From 75481e352b2854378f33dff4ae2199fab6fe4840 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 13 Jul 2006 20:03:46 +0000 Subject: [PATCH 01/20] initial import of compiler test --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%402 --- compiler-init/amx.h | 462 +++ compiler-init/amxdbg.h | 172 ++ compiler-init/libpawnc.c | 257 ++ compiler-init/libpawnc.rc | 55 + compiler-init/lstring.c | 124 + compiler-init/lstring.h | 18 + compiler-init/memfile.c | 105 + compiler-init/memfile.h | 23 + compiler-init/osdefs.h | 108 + compiler-init/pawn.ico | Bin 0 -> 8478 bytes compiler-init/pawncc.c | 30 + compiler-init/sc.h | 812 +++++ compiler-init/sc1.c | 5592 +++++++++++++++++++++++++++++++++++ compiler-init/sc2.c | 2801 ++++++++++++++++++ compiler-init/sc3.c | 2453 +++++++++++++++ compiler-init/sc4.c | 1331 +++++++++ compiler-init/sc5.c | 228 ++ compiler-init/sc5.scp | 342 +++ compiler-init/sc6.c | 1298 ++++++++ compiler-init/sc7.c | 703 +++++ compiler-init/sc7.scp | 2022 +++++++++++++ compiler-init/scexpand.c | 68 + compiler-init/sci18n.c | 428 +++ compiler-init/sclist.c | 486 +++ compiler-init/scmemfil.c | 179 ++ compiler-init/scstate.c | 375 +++ compiler-init/scvars.c | 113 + compiler-init/spcomp.sln | 20 + compiler-init/spcomp.vcproj | 297 ++ compiler-init/svnrev.h | 9 + 30 files changed, 20911 insertions(+) create mode 100644 compiler-init/amx.h create mode 100644 compiler-init/amxdbg.h create mode 100644 compiler-init/libpawnc.c create mode 100644 compiler-init/libpawnc.rc create mode 100644 compiler-init/lstring.c create mode 100644 compiler-init/lstring.h create mode 100644 compiler-init/memfile.c create mode 100644 compiler-init/memfile.h create mode 100644 compiler-init/osdefs.h create mode 100644 compiler-init/pawn.ico create mode 100644 compiler-init/pawncc.c create mode 100644 compiler-init/sc.h create mode 100644 compiler-init/sc1.c create mode 100644 compiler-init/sc2.c create mode 100644 compiler-init/sc3.c create mode 100644 compiler-init/sc4.c create mode 100644 compiler-init/sc5.c create mode 100644 compiler-init/sc5.scp create mode 100644 compiler-init/sc6.c create mode 100644 compiler-init/sc7.c create mode 100644 compiler-init/sc7.scp create mode 100644 compiler-init/scexpand.c create mode 100644 compiler-init/sci18n.c create mode 100644 compiler-init/sclist.c create mode 100644 compiler-init/scmemfil.c create mode 100644 compiler-init/scstate.c create mode 100644 compiler-init/scvars.c create mode 100644 compiler-init/spcomp.sln create mode 100644 compiler-init/spcomp.vcproj create mode 100644 compiler-init/svnrev.h diff --git a/compiler-init/amx.h b/compiler-init/amx.h new file mode 100644 index 00000000..6efdd35e --- /dev/null +++ b/compiler-init/amx.h @@ -0,0 +1,462 @@ +/* Pawn Abstract Machine (for the Pawn language) + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amx.h 3579 2006-06-06 13:35:29Z thiadmer $ + */ + +#ifndef AMX_H_INCLUDED +#define AMX_H_INCLUDED + +#include /* for size_t */ +#include + +#if defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#endif +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + +#if defined HAVE_STDINT_H + #include +#else + #if defined __LCC__ || defined __DMC__ || defined LINUX || (defined __WATCOMC__ && __WATCOMC__ >= 1200) + #if defined HAVE_INTTYPES_H + #include + #else + #include + #endif + #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L + /* The ISO C99 defines the int16_t and int_32t types. If the compiler got + * here, these types are probably undefined. + */ + #if defined __MACH__ + #include + typedef unsigned short int uint16_t; + typedef unsigned long int uint32_t; + #elif defined __FreeBSD__ + #include + #else + typedef short int int16_t; + typedef unsigned short int uint16_t; + #if defined SN_TARGET_PS2 + typedef int int32_t; + typedef unsigned int uint32_t; + #else + typedef long int int32_t; + typedef unsigned long int uint32_t; + #endif + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #define HAVE_I64 + #elif defined __GNUC__ + typedef long long int64_t; + typedef unsigned long long uint64_t; + #define HAVE_I64 + #endif + #endif + #endif + #define HAVE_STDINT_H +#endif +#if defined _LP64 || defined WIN64 || defined _WIN64 + #if !defined __64BIT__ + #define __64BIT__ + #endif +#endif + +#if HAVE_ALLOCA_H + #include +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ + #if !defined alloca + #define alloca(n) _alloca(n) + #endif +#endif + +#if !defined arraysize + #define arraysize(array) (sizeof(array) / sizeof((array)[0])) +#endif +#if !defined assert_static + /* see "Compile-Time Assertions" by Ralf Holly, + * C/C++ Users Journal, November 2004 + */ + #define assert_static(e) \ + do { \ + enum { assert_static__ = 1/(e) }; \ + } while (0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined PAWN_DLL + #if !defined AMX_NATIVE_CALL + #define AMX_NATIVE_CALL __stdcall + #endif + #if !defined AMXAPI + #define AMXAPI __stdcall + #endif +#endif + +/* calling convention for native functions */ +#if !defined AMX_NATIVE_CALL + #define AMX_NATIVE_CALL +#endif +/* calling convention for all interface functions and callback functions */ +#if !defined AMXAPI + #if defined STDECL + #define AMXAPI __stdcall + #elif defined CDECL + #define AMXAPI __cdecl + #elif defined GCC_HASCLASSVISIBILITY + #define AMXAPI __attribute__ ((visibility("default"))) + #else + #define AMXAPI + #endif +#endif +#if !defined AMXEXPORT + #define AMXEXPORT +#endif + +/* File format version (in CUR_FILE_VERSION) + * 0 (original version) + * 1 (opcodes JUMP.pri, SWITCH and CASETBL) + * 2 (compressed files) + * 3 (public variables) + * 4 (opcodes SWAP.pri/alt and PUSHADDR) + * 5 (tagnames table) + * 6 (reformatted header) + * 7 (name table, opcodes SYMTAG & SYSREQ.D) + * 8 (opcode STMT, renewed debug interface) + * 9 (macro opcodes) + * MIN_FILE_VERSION is the lowest file version number that the current AMX + * implementation supports. If the AMX file header gets new fields, this number + * often needs to be incremented. MAX_AMX_VERSION is the lowest AMX version that + * is needed to support the current file version. When there are new opcodes, + * this number needs to be incremented. + * The file version supported by the JIT may run behind MIN_AMX_VERSION. So + * there is an extra constant for it: MAX_FILE_VER_JIT. + */ +#define CUR_FILE_VERSION 9 /* current file version; also the current AMX version */ +#define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */ +#define MIN_AMX_VERSION 9 /* minimum AMX version needed to support the current file format */ +#define MAX_FILE_VER_JIT 8 /* file version supported by the JIT */ +#define MIN_AMX_VER_JIT 8 /* AMX version supported by the JIT */ + +#if !defined PAWN_CELL_SIZE + #define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */ +#endif +#if PAWN_CELL_SIZE==16 + typedef uint16_t ucell; + typedef int16_t cell; +#elif PAWN_CELL_SIZE==32 + typedef uint32_t ucell; + typedef int32_t cell; +#elif PAWN_CELL_SIZE==64 + typedef uint64_t ucell; + typedef int64_t cell; +#else + #error Unsupported cell size (PAWN_CELL_SIZE) +#endif + +#define UNPACKEDMAX (((cell)1 << (sizeof(cell)-1)*8) - 1) +#define UNLIMITED (~1u >> 1) + +struct tagAMX; +typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, cell *params); +typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index, + cell *result, cell *params); +typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx); +typedef int (AMXAPI *AMX_IDLE)(struct tagAMX *amx, int AMXAPI Exec(struct tagAMX *, cell *, int)); +#if !defined _FAR + #define _FAR +#endif + +#if defined _MSC_VER + #pragma warning(disable:4103) /* disable warning message 4103 that complains + * about pragma pack in a header file */ + #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ +#endif + +/* Some compilers do not support the #pragma align, which should be fine. Some + * compilers give a warning on unknown #pragmas, which is not so fine... + */ +#if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN + #define AMX_NO_ALIGN +#endif + +#if defined __GNUC__ + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=mac68k + #else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #if defined __TURBOC__ + #pragma option -a- /* "pack" pragma for older Borland compilers */ + #endif + #endif +#endif + +typedef struct tagAMX_NATIVE_INFO { + const char _FAR *name PACKED; + AMX_NATIVE func PACKED; +} AMX_NATIVE_INFO; + +#define AMX_USERNUM 4 +#define sEXPMAX 19 /* maximum name length for file version <= 6 */ +#define sNAMEMAX 31 /* maximum name length of symbol name */ + +typedef struct tagAMX_FUNCSTUB { + ucell address PACKED; + char name[sEXPMAX+1] PACKED; +} AMX_FUNCSTUB; + +typedef struct tagFUNCSTUBNT { + ucell address PACKED; + uint32_t nameofs PACKED; +} AMX_FUNCSTUBNT; + +/* The AMX structure is the internal structure for many functions. Not all + * fields are valid at all times; many fields are cached in local variables. + */ +typedef struct tagAMX { + unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */ + unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */ + AMX_CALLBACK callback PACKED; + AMX_DEBUG debug PACKED; /* debug callback */ + /* for external functions a few registers must be accessible from the outside */ + cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */ + cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */ + cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */ + cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */ + cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */ + cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */ + int flags PACKED; /* current status, see amx_Flags() */ + /* user data */ + long usertags[AMX_USERNUM] PACKED; + void _FAR *userdata[AMX_USERNUM] PACKED; + /* native functions can raise an error */ + int error PACKED; + /* passing parameters requires a "count" field */ + int paramcount; + /* the sleep opcode needs to store the full AMX status */ + cell pri PACKED; + cell alt PACKED; + cell reset_stk PACKED; + cell reset_hea PACKED; + cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */ + #if defined JIT + /* support variables for the JIT */ + int reloc_size PACKED; /* required temporary buffer for relocations */ + long code_size PACKED; /* estimated memory footprint of the native code */ + #endif +} AMX; + +/* The AMX_HEADER structure is both the memory format as the file format. The + * structure is used internaly. + */ +typedef struct tagAMX_HEADER { + int32_t size PACKED; /* size of the "file" */ + uint16_t magic PACKED; /* signature */ + char file_version PACKED; /* file format version */ + char amx_version PACKED; /* required version of the AMX */ + int16_t flags PACKED; + int16_t defsize PACKED; /* size of a definition record */ + int32_t cod PACKED; /* initial value of COD - code block */ + int32_t dat PACKED; /* initial value of DAT - data block */ + int32_t hea PACKED; /* initial value of HEA - start of the heap */ + int32_t stp PACKED; /* initial value of STP - stack top */ + int32_t cip PACKED; /* initial value of CIP - the instruction pointer */ + int32_t publics PACKED; /* offset to the "public functions" table */ + int32_t natives PACKED; /* offset to the "native functions" table */ + int32_t libraries PACKED; /* offset to the table of libraries */ + int32_t pubvars PACKED; /* the "public variables" table */ + int32_t tags PACKED; /* the "public tagnames" table */ + int32_t nametable PACKED; /* name table */ +} AMX_HEADER; + +#if PAWN_CELL_SIZE==16 + #define AMX_MAGIC 0xf1e2 +#elif PAWN_CELL_SIZE==32 + #define AMX_MAGIC 0xf1e0 +#elif PAWN_CELL_SIZE==64 + #define AMX_MAGIC 0xf1e1 +#endif + +enum { + AMX_ERR_NONE, + /* reserve the first 15 error codes for exit codes of the abstract machine */ + AMX_ERR_EXIT, /* forced exit */ + AMX_ERR_ASSERT, /* assertion failed */ + AMX_ERR_STACKERR, /* stack/heap collision */ + AMX_ERR_BOUNDS, /* index out of bounds */ + AMX_ERR_MEMACCESS, /* invalid memory access */ + AMX_ERR_INVINSTR, /* invalid instruction */ + AMX_ERR_STACKLOW, /* stack underflow */ + AMX_ERR_HEAPLOW, /* heap underflow */ + AMX_ERR_CALLBACK, /* no callback, or invalid callback */ + AMX_ERR_NATIVE, /* native function failed */ + AMX_ERR_DIVIDE, /* divide by zero */ + AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */ + AMX_ERR_INVSTATE, /* invalid state for this access */ + + AMX_ERR_MEMORY = 16, /* out of memory */ + AMX_ERR_FORMAT, /* invalid file format */ + AMX_ERR_VERSION, /* file is for a newer version of the AMX */ + AMX_ERR_NOTFOUND, /* function not found */ + AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */ + AMX_ERR_DEBUG, /* debugger cannot run */ + AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */ + AMX_ERR_USERDATA, /* unable to set user data field (table full) */ + AMX_ERR_INIT_JIT, /* cannot initialize the JIT */ + AMX_ERR_PARAMS, /* parameter error */ + AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */ + AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */ +}; + +/* AMX_FLAG_CHAR16 0x01 no longer used */ +#define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */ +#define AMX_FLAG_COMPACT 0x04 /* compact encoding */ +#define AMX_FLAG_SLEEP 0x08 /* script uses the sleep instruction (possible re-entry or power-down mode) */ +#define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no BREAK opcodes */ +#define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */ +#define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */ +#define AMX_FLAG_BROWSE 0x4000 /* busy browsing */ +#define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ + +#define AMX_EXEC_MAIN (-1) /* start at program entry point */ +#define AMX_EXEC_CONT (-2) /* continue from last address */ + +#define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24)) + +#if !defined AMX_COMPACTMARGIN + #define AMX_COMPACTMARGIN 64 +#endif + +/* for native functions that use floating point parameters, the following + * two macros are convenient for casting a "cell" into a "float" type _without_ + * changing the bit pattern + */ +#if PAWN_CELL_SIZE==32 + #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ + #define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */ +#elif PAWN_CELL_SIZE==64 + #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ + #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ +#else + #error Unsupported cell size +#endif + +#define amx_StrParam(amx,param,result) \ + do { \ + cell *amx_cstr_; int amx_length_; \ + amx_GetAddr((amx), (param), &amx_cstr_); \ + amx_StrLen(amx_cstr_, &amx_length_); \ + if (amx_length_ > 0 && \ + ((result) = (void*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \ + amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \ + else (result) = NULL; \ + } while (0) + +uint16_t * AMXAPI amx_Align16(uint16_t *v); +uint32_t * AMXAPI amx_Align32(uint32_t *v); +#if defined _I64_MAX || defined HAVE_I64 + uint64_t * AMXAPI amx_Align64(uint64_t *v); +#endif +int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); +int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params); +int AMXAPI amx_Cleanup(AMX *amx); +int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); +int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); +int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); +int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr); +int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname); +int AMXAPI amx_Flags(AMX *amx,uint16_t *flags); +int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr); +int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname); +int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname); +int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr); +int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size); +int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id); +int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr); +int AMXAPI amx_Init(AMX *amx, void *program); +int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code); +int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap); +int AMXAPI amx_NameLength(AMX *amx, int *length); +AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func); +int AMXAPI amx_NumNatives(AMX *amx, int *number); +int AMXAPI amx_NumPublics(AMX *amx, int *number); +int AMXAPI amx_NumPubVars(AMX *amx, int *number); +int AMXAPI amx_NumTags(AMX *amx, int *number); +int AMXAPI amx_Push(AMX *amx, cell value); +int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); +int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); +int AMXAPI amx_RaiseError(AMX *amx, int error); +int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); +int AMXAPI amx_Release(AMX *amx, cell amx_addr); +int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback); +int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug); +int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size); +int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr); +int AMXAPI amx_StrLen(const cell *cstring, int *length); +int AMXAPI amx_UTF8Check(const char *string, int *length); +int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value); +int AMXAPI amx_UTF8Len(const cell *cstr, int *length); +int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); + +#if PAWN_CELL_SIZE==16 + #define amx_AlignCell(v) amx_Align16(v) +#elif PAWN_CELL_SIZE==32 + #define amx_AlignCell(v) amx_Align32(v) +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) + #define amx_AlignCell(v) amx_Align64(v) +#else + #error Unsupported cell size +#endif + +#define amx_RegisterFunc(amx, name, func) \ + amx_Register((amx), amx_NativeInfo((name),(func)), 1); + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack() /* reset default packing */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=reset + #else + #pragma pack(pop) /* reset previous packing */ + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMX_H_INCLUDED */ diff --git a/compiler-init/amxdbg.h b/compiler-init/amxdbg.h new file mode 100644 index 00000000..acc4d1b3 --- /dev/null +++ b/compiler-init/amxdbg.h @@ -0,0 +1,172 @@ +/* Abstract Machine for the Pawn compiler, debugger support + * + * This file contains extra definitions that are convenient for debugger + * support. + * + * Copyright (c) ITB CompuPhase, 2005-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxdbg.h 3519 2006-02-17 17:57:04Z thiadmer $ + */ + +#ifndef AMXDBG_H_INCLUDED +#define AMXDBG_H_INCLUDED + +#ifndef AMX_H_INCLUDED + #include "amx.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Some compilers do not support the #pragma align, which should be fine. Some + * compilers give a warning on unknown #pragmas, which is not so fine... + */ +#if defined SN_TARGET_PS2 || defined __GNUC__ + #define AMX_NO_ALIGN +#endif + +#if defined __GNUC__ + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=mac68k + #else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #if defined __TURBOC__ + #pragma option -a- /* "pack" pragma for older Borland compilers */ + #endif + #endif +#endif + +typedef struct tagAMX_DBG_HDR { + int32_t size PACKED; /* size of the debug information chunk */ + uint16_t magic PACKED; /* signature, must be 0xf1ef */ + char file_version PACKED; /* file format version */ + char amx_version PACKED; /* required version of the AMX */ + int16_t flags PACKED; /* currently unused */ + int16_t files PACKED; /* number of entries in the "file" table */ + int16_t lines PACKED; /* number of entries in the "line" table */ + int16_t symbols PACKED; /* number of entries in the "symbol" table */ + int16_t tags PACKED; /* number of entries in the "tag" table */ + int16_t automatons PACKED; /* number of entries in the "automaton" table */ + int16_t states PACKED; /* number of entries in the "state" table */ +} PACKED AMX_DBG_HDR; +#define AMX_DBG_MAGIC 0xf1ef + +typedef struct tagAMX_DBG_FILE { + ucell address PACKED; /* address in the code segment where generated code (for this file) starts */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_FILE; + +typedef struct tagAMX_DBG_LINE { + ucell address PACKED; /* address in the code segment where generated code (for this line) starts */ + int32_t line PACKED; /* line number */ +} PACKED AMX_DBG_LINE; + +typedef struct tagAMX_DBG_SYMBOL { + ucell address PACKED; /* address in the data segment or relative to the frame */ + int16_t tag PACKED; /* tag for the symbol */ + ucell codestart PACKED; /* address in the code segment from which this symbol is valid (in scope) */ + ucell codeend PACKED; /* address in the code segment until which this symbol is valid (in scope) */ + char ident PACKED; /* kind of symbol (function/variable) */ + char vclass PACKED; /* class of symbol (global/local) */ + int16_t dim PACKED; /* number of dimensions */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_SYMBOL; + +typedef struct tagAMX_DBG_SYMDIM { + int16_t tag PACKED; /* tag for the array dimension */ + ucell size PACKED; /* size of the array dimension */ +} PACKED AMX_DBG_SYMDIM; + +typedef struct tagAMX_DBG_TAG { + int16_t tag PACKED; /* tag id */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_TAG; + +typedef struct tagAMX_DBG_MACHINE { + int16_t automaton PACKED; /* automaton id */ + ucell address PACKED; /* address of state variable */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_MACHINE; + +typedef struct tagAMX_DBG_STATE { + int16_t state PACKED; /* state id */ + int16_t automaton PACKED; /* automaton id */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_STATE; + +typedef struct tagAMX_DBG { + AMX_DBG_HDR *hdr PACKED; /* points to the AMX_DBG header */ + AMX_DBG_FILE **filetbl PACKED; + AMX_DBG_LINE *linetbl PACKED; + AMX_DBG_SYMBOL **symboltbl PACKED; + AMX_DBG_TAG **tagtbl PACKED; + AMX_DBG_MACHINE **automatontbl PACKED; + AMX_DBG_STATE **statetbl PACKED; +} PACKED AMX_DBG; + +#if !defined iVARIABLE + #define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ + #define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ + #define iARRAY 3 + #define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ + #define iFUNCTN 9 +#endif + + +int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg); +int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp); + +int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename); +int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname); +int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line); + +int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address); +int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address); +int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name); +int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name); +int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name); +int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym); +int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim); + + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack() /* reset default packing */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=reset + #else + #pragma pack(pop) /* reset previous packing */ + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMXDBG_H_INCLUDED */ diff --git a/compiler-init/libpawnc.c b/compiler-init/libpawnc.c new file mode 100644 index 00000000..ca42d1a9 --- /dev/null +++ b/compiler-init/libpawnc.c @@ -0,0 +1,257 @@ +/* LIBPAWNC.C + * + * A "glue file" for building the Pawn compiler as a DLL or shared library. + * + * Copyright (c) ITB CompuPhase, 2000-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: libsc.c 3114 2005-03-17 14:48:29Z thiadmer $ + */ +#include +#include +#include +#include +#include +#include "sc.h" +#include "memfile.h" + +/* pc_printf() + * Called for general purpose "console" output. This function prints general + * purpose messages; errors go through pc_error(). The function is modelled + * after printf(). + */ +int pc_printf(const char *message,...) +{ + int ret; + va_list argptr; + + va_start(argptr,message); + ret=vprintf(message,argptr); + va_end(argptr); + + return ret; +} + +/* pc_error() + * Called for producing error output. + * number the error number (as documented in the manual) + * message a string describing the error with embedded %d and %s tokens + * filename the name of the file currently being parsed + * firstline the line number at which the expression started on which + * the error was found, or -1 if there is no "starting line" + * lastline the line number at which the error was detected + * argptr a pointer to the first of a series of arguments (for macro + * "va_arg") + * Return: + * If the function returns 0, the parser attempts to continue compilation. + * On a non-zero return value, the parser aborts. + */ +int pc_error(int number,char *message,char *filename,int firstline,int lastline,va_list argptr) +{ +static char *prefix[3]={ "error", "fatal error", "warning" }; + + if (number!=0) { + char *pre; + + pre=prefix[number/100]; + if (firstline>=0) + fprintf(stderr,"%s(%d -- %d) : %s %03d: ",filename,firstline,lastline,pre,number); + else + fprintf(stderr,"%s(%d) : %s %03d: ",filename,lastline,pre,number); + } /* if */ + vfprintf(stderr,message,argptr); + fflush(stderr); + return 0; +} + +/* pc_opensrc() + * Opens a source file (or include file) for reading. The "file" does not have + * to be a physical file, one might compile from memory. + * filename the name of the "file" to read from + * Return: + * The function must return a pointer, which is used as a "magic cookie" to + * all I/O functions. When failing to open the file for reading, the + * function must return NULL. + * Note: + * Several "source files" may be open at the same time. Specifically, one + * file can be open for reading and another for writing. + */ +void *pc_opensrc(char *filename) +{ + return fopen(filename,"rt"); +} + +/* pc_createsrc() + * Creates/overwrites a source file for writing. The "file" does not have + * to be a physical file, one might compile from memory. + * filename the name of the "file" to create + * Return: + * The function must return a pointer which is used as a "magic cookie" to + * all I/O functions. When failing to open the file for reading, the + * function must return NULL. + * Note: + * Several "source files" may be open at the same time. Specifically, one + * file can be open for reading and another for writing. + */ +void *pc_createsrc(char *filename) +{ + return fopen(filename,"wt"); +} + +/* pc_closesrc() + * Closes a source file (or include file). The "handle" parameter has the + * value that pc_opensrc() returned in an earlier call. + */ +void pc_closesrc(void *handle) +{ + assert(handle!=NULL); + fclose((FILE*)handle); +} + +/* pc_resetsrc() + * "position" may only hold a pointer that was previously obtained from + * pc_getpossrc() + */ +void pc_resetsrc(void *handle,void *position) +{ + assert(handle!=NULL); + fsetpos((FILE*)handle,(fpos_t *)position); +} + +/* pc_readsrc() + * Reads a single line from the source file (or up to a maximum number of + * characters if the line in the input file is too long). + */ +char *pc_readsrc(void *handle,unsigned char *target,int maxchars) +{ + return fgets((char*)target,maxchars,(FILE*)handle); +} + +/* pc_writesrc() + * Writes to to the source file. There is no automatic line ending; to end a + * line, write a "\n". + */ +int pc_writesrc(void *handle,unsigned char *source) +{ + return fputs((char*)source,(FILE*)handle) >= 0; +} + +void *pc_getpossrc(void *handle) +{ + static fpos_t lastpos; /* may need to have a LIFO stack of such positions */ + + fgetpos((FILE*)handle,&lastpos); + return &lastpos; +} + +int pc_eofsrc(void *handle) +{ + return feof((FILE*)handle); +} + +/* should return a pointer, which is used as a "magic cookie" to all I/O + * functions; return NULL for failure + */ +void *pc_openasm(char *filename) +{ + #if defined __MSDOS__ || defined SC_LIGHT + return fopen(filename,"w+t"); + #else + return mfcreate(filename); + #endif +} + +void pc_closeasm(void *handle, int deletefile) +{ + #if defined __MSDOS__ || defined SC_LIGHT + if (handle!=NULL) + fclose((FILE*)handle); + if (deletefile) + remove(outfname); + #else + if (handle!=NULL) { + if (!deletefile) + mfdump((MEMFILE*)handle); + mfclose((MEMFILE*)handle); + } /* if */ + #endif +} + +void pc_resetasm(void *handle) +{ + assert(handle!=NULL); + #if defined __MSDOS__ || defined SC_LIGHT + fflush((FILE*)handle); + fseek((FILE*)handle,0,SEEK_SET); + #else + mfseek((MEMFILE*)handle,0,SEEK_SET); + #endif +} + +int pc_writeasm(void *handle,char *string) +{ + #if defined __MSDOS__ || defined SC_LIGHT + return fputs(string,(FILE*)handle) >= 0; + #else + return mfputs((MEMFILE*)handle,string); + #endif +} + +char *pc_readasm(void *handle, char *string, int maxchars) +{ + #if defined __MSDOS__ || defined SC_LIGHT + return fgets(string,maxchars,(FILE*)handle); + #else + return mfgets((MEMFILE*)handle,string,maxchars); + #endif +} + +/* Should return a pointer, which is used as a "magic cookie" to all I/O + * functions; return NULL for failure. + */ +void *pc_openbin(char *filename) +{ + return memfile_creat(filename, 1); +} + +void pc_closebin(void *handle,int deletefile) +{ + if (deletefile) + { + memfile_destroy((memfile_t *)handle); + } +} + +/* pc_resetbin() + * Can seek to any location in the file. + * The offset is always from the start of the file. + */ +void pc_resetbin(void *handle,long offset) +{ + memfile_seek((memfile_t *)handle, offset); +} + +int pc_writebin(void *handle,void *buffer,int size) +{ + return memfile_write((memfile_t *)handle, buffer, size); +} + +long pc_lengthbin(void *handle) +{ + return memfile_tell((memfile_t *)handle); +} diff --git a/compiler-init/libpawnc.rc b/compiler-init/libpawnc.rc new file mode 100644 index 00000000..df89015a --- /dev/null +++ b/compiler-init/libpawnc.rc @@ -0,0 +1,55 @@ +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif +#include "svnrev.h" + +AppIcon ICON "pawn.ico" + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION SMC_VERSION +#define REVISION SMC_REVISION +#define BUILD SMC_BUILD +#define VERSIONSTR SMC_VERSTRING +#define VERSIONNAME "smcomp.exe\0" +#define VERSIONDESCRIPTION "SourcePawn Compiler\0" +#define VERSIONPRODUCTNAME "smcomp\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "(C)1998-2006 ITB CompuPhase, AlliedModders LLC\0" + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", "(C)1998-2006 ITB CompuPhase, AlliedModders LLC\0" + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/compiler-init/lstring.c b/compiler-init/lstring.c new file mode 100644 index 00000000..f5156b32 --- /dev/null +++ b/compiler-init/lstring.c @@ -0,0 +1,124 @@ +/* Safe string copying and concatenation + * These routines are originally distributed in two separate files. I have + * copied the files verbatim in this single source file, including all comments. + * The only change is that the second set of include files is commented out + * (there is no need to include the same files twice). + */ + +#include "lstring.h" + +#if !defined HAVE_SAFESTR + +/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + #include already included through lstring.h + */ +#include /* for strlen() */ + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + #include already included + #include already included +*/ + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +#endif /* #if !defined HAVE_SAFESTR */ diff --git a/compiler-init/lstring.h b/compiler-init/lstring.h new file mode 100644 index 00000000..ca62cb9a --- /dev/null +++ b/compiler-init/lstring.h @@ -0,0 +1,18 @@ +/* prototypes for strlcpy() and strlcat() */ + +#include + +#if defined __WATCOMC__ && __WATCOMC__ >= 1240 + /* OpenWatcom introduced BSD "safe string functions" with version 1.4 */ + #define HAVE_SAFESTR +#endif + +#if !defined HAVE_SAFESTR + +size_t +strlcpy(char *dst, const char *src, size_t siz); + +size_t +strlcat(char *dst, const char *src, size_t siz); + +#endif diff --git a/compiler-init/memfile.c b/compiler-init/memfile.c new file mode 100644 index 00000000..2047f710 --- /dev/null +++ b/compiler-init/memfile.c @@ -0,0 +1,105 @@ +#include "memfile.h" +#include +#include "osdefs.h" + +memfile_t *memfile_creat(const char *name, size_t init) +{ + memfile_t mf; + memfile_t *pmf; + + mf.size = init; + mf.base = (char *)malloc(init); + mf.usedoffs = 0; + if (!mf.base) + { + return NULL; + } + + mf.offs = 0; + mf._static = 0; + + pmf = (memfile_t *)malloc(sizeof(memfile_t)); + memcpy(pmf, &mf, sizeof(memfile_t)); + + pmf->name = strdup(name); + + return pmf; +} + +void memfile_destroy(memfile_t *mf) +{ + if (!mf->_static) + { + free(mf->name); + free(mf->base); + free(mf); + } +} + +void memfile_seek(memfile_t *mf, long seek) +{ + mf->offs = seek; +} + +long memfile_tell(memfile_t *mf) +{ + return mf->offs; +} + +size_t memfile_read(memfile_t *mf, void *buffer, size_t maxsize) +{ + if (!maxsize || mf->offs >= mf->usedoffs) + { + return 0; + } + + if (mf->usedoffs - mf->offs < (long)maxsize) + { + maxsize = mf->usedoffs - mf->offs; + if (!maxsize) + { + return 0; + } + } + + memcpy(buffer, mf->base + mf->offs, maxsize); + + mf->offs += maxsize; + + return maxsize; +} + +int memfile_write(memfile_t *mf, void *buffer, size_t size) +{ + if (mf->offs + size > mf->size) + { + size_t newsize = (mf->size + size) * 2; + if (mf->_static) + { + char *oldbase = mf->base; + mf->base = (char *)malloc(newsize); + if (!mf->base) + { + return 0; + } + memcpy(mf->base, oldbase, mf->size); + } else { + mf->base = (char *)realloc(mf->base, newsize); + if (!mf->base) + { + return 0; + } + } + mf->_static = 0; + mf->size = newsize; + } + memcpy(mf->base + mf->offs, buffer, size); + mf->offs += size; + + if (mf->offs > mf->usedoffs) + { + mf->usedoffs = mf->offs; + } + + return 1; +} diff --git a/compiler-init/memfile.h b/compiler-init/memfile.h new file mode 100644 index 00000000..f5222a9e --- /dev/null +++ b/compiler-init/memfile.h @@ -0,0 +1,23 @@ +#ifndef _INCLUDE_MEMFILE_H +#define _INCLUDE_MEMFILE_H + +#include + +typedef struct memfile_s +{ + char *name; + char *base; + long offs; + long usedoffs; + size_t size; + int _static; +} memfile_t; + +memfile_t *memfile_creat(const char *name, size_t init); +void memfile_destroy(memfile_t *mf); +void memfile_seek(memfile_t *mf, long seek); +int memfile_write(memfile_t *mf, void *buffer, size_t size); +size_t memfile_read(memfile_t *mf, void *buffer, size_t maxsize); +long memfile_tell(memfile_t *mf); + +#endif //_INCLUDE_MEMFILE_H diff --git a/compiler-init/osdefs.h b/compiler-init/osdefs.h new file mode 100644 index 00000000..299af953 --- /dev/null +++ b/compiler-init/osdefs.h @@ -0,0 +1,108 @@ +/* __MSDOS__ set when compiling for DOS (not Windows) + * _Windows set when compiling for any version of Microsoft Windows + * __WIN32__ set when compiling for Windows95 or WindowsNT (32 bit mode) + * __32BIT__ set when compiling in 32-bit "flat" mode (DOS or Windows) + * + * Copyright 1998-2005, ITB CompuPhase, The Netherlands. + * info@compuphase.com. + */ + +#ifndef _OSDEFS_H +#define _OSDEFS_H + +/* Every compiler uses different "default" macros to indicate the mode + * it is in. Throughout the source, we use the Borland C++ macros, so + * the macros of Watcom C/C++ and Microsoft Visual C/C++ are mapped to + * those of Borland C++. + */ +#if defined(__WATCOMC__) +# if defined(__WINDOWS__) || defined(__NT__) +# define _Windows 1 +# endif +# ifdef __386__ +# define __32BIT__ 1 +# endif +# if defined(_Windows) && defined(__32BIT__) +# define __WIN32__ 1 +# endif +#elif defined(_MSC_VER) +# if defined(_WINDOWS) || defined(_WIN32) +# define _Windows 1 +# endif +# ifdef _WIN32 +# define __WIN32__ 1 +# define __32BIT__ 1 +# endif +# if _MSC_VER >= 1400 +# if !defined _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# define strdup _strdup +# define stricmp _stricmp +# define access _access +# define chdir _chdir +# define strdup _strdup +# endif +#endif + +#if defined __FreeBSD__ + #include +#elif defined LINUX + #include +#endif + +/* Linux NOW has these */ +#if !defined BIG_ENDIAN + #define BIG_ENDIAN 4321 +#endif +#if !defined LITTLE_ENDIAN + #define LITTLE_ENDIAN 1234 +#endif + +/* educated guess, BYTE_ORDER is undefined, i386 is common => little endian */ +#if !defined BYTE_ORDER + #if defined UCLINUX + #define BYTE_ORDER BIG_ENDIAN + #else + #define BYTE_ORDER LITTLE_ENDIAN + #endif +#endif + +#if defined __MSDOS__ || defined __WIN32__ || defined _Windows + #define DIRSEP_CHAR '\\' +#elif defined macintosh + #define DIRSEP_CHAR ':' +#else + #define DIRSEP_CHAR '/' /* directory separator character */ +#endif + +/* _MAX_PATH is sometimes called differently and it may be in limits.h or + * stdlib.h instead of stdio.h. + */ +#if !defined _MAX_PATH + /* not defined, perhaps stdio.h was not included */ + #if !defined PATH_MAX + #include + #endif + #if !defined _MAX_PATH && !defined PATH_MAX + /* no _MAX_PATH and no MAX_PATH, perhaps it is in limits.h */ + #include + #endif + #if !defined _MAX_PATH && !defined PATH_MAX + /* no _MAX_PATH and no MAX_PATH, perhaps it is in stdlib.h */ + #include + #endif + /* if _MAX_PATH is undefined, try common alternative names */ + #if !defined _MAX_PATH + #if defined MAX_PATH + #define _MAX_PATH MAX_PATH + #elif defined _POSIX_PATH_MAX + #define _MAX_PATH _POSIX_PATH_MAX + #else + /* everything failed, actually we have a problem here... */ + #define _MAX_PATH 1024 + #endif + #endif +#endif + +#endif /* _OSDEFS_H */ diff --git a/compiler-init/pawn.ico b/compiler-init/pawn.ico new file mode 100644 index 0000000000000000000000000000000000000000..7a6ab60bc5be72862ee6fee5e3bd4585a6644f36 GIT binary patch literal 8478 zcmeI1J!~6C7RN`X0-1jOasId_?ko&&fT6+xS*394Ykk$=3c7X~x^Y9`0H!do zU5Z1HN;p`{=BPq$&)d5&1?24JqK^}mD=Z*^=YId$U22!&QoJ_M$p4+M_ujmDGn|?C zY|N7R#N4}QLi%CNm_Jc!G}8KCJ~8GaZH=tq%Ozvn7slMWmG=K|)tJAotDZ9a`-(At z{Jk+>(k+pH21^DarFxo8f4gen)6ELqtg^eiYj#Jw=KFt+&GPcHxq9`gSzTQ<&1TbF zzkc1^xN*bWxpT+dy?fU@di2OVeE86`S}k+`{(bZ8*)#J@a`{jUh0sF9Qga{z2~dLt z4n!aU9*D>S2O^LF55#DJ0})7o2NGt10})7oSKU(w34kK}Ild7{fCo~*0tX_H01u>; z1r9_Y0Uk(E3mk|*0z8oN7B~=r1bASXkOL7&fCmU?WEcJ-z6tO^leWNt2qeG*8Nvbw zB9H(X#sUW-kN^*4EDIu#01sq53nGvJ&xi?M88O13`b%V7S%x~4j2%Hi8^*T4iHDslm->rW7>Dp%>D(KSYTU|S<<~Y9H>4gMKU}!g6jn!&~>nXLqDM!1_ar9Z`03QPV7AbH@CFu7n=~qPG zkK?%Cha>0&{b?orIHiUm4h%tjYmSLh`eA(tLnozfwJXIN6HA%VXaI4mi7HCm4b@4i z^jsj#=-buiL`>XIiCay`1IIjdj5&e4t)(JiOE~7tR%&*7@vu%BjpBH#=i2kdn?AEm z(_2lY-|KLy%}McFC6Tgbe0rVM9BSs9r#|b;9GJ{pdQ*?Xh6tKy#!u#x(>n-5Nb~jr z)G$czAfKCcku(T9wC5I4Cdlg{f2+gRSwuQC+I1av#093ANZaZ4CB60Sq~(HwbZ^bw z_+$}bZ{9lzudfb*Gj@RaD}) z=)>_3eFeo-bDdM2Y4qZ$_UdYVI2cs;KHmR$jdzguAN3?6u|f}&kBzyxNS@Q@`L7v2 z`8VRv^IU#jpU(4s->r$w``wjtp6QwEJITj4Q@Ta-TyE7kdAnIk*F{Bwv22Jl9)b;-@ZAnUT6`<8$e!;x%6SscZ)Mi1Hip8p-9gUHYl`lD||+PI-Gz2fnEdQ#u9TLbBOHPM`jHeB)h0XL0$+j2eY8wvM(V1Kk8GoY{#3b)o3awbNH622m-o?cKW=)ma&+)y zfB)d{c&uY!Ivsw!zV6(5yVYp44}PBRHK%EaQ^7ZE`8` zmYZptyuXj9Ubx2d=1Kkm>&-BIslQTtsQQE&=E(1FDIRowZ{+gB(pSgR#@ZPy{;buL zh$o@Da)!qWp9GTGQ#cB-cu~xbSFyvjzmYL}iUaN75HFq0KaPnX5N;?x#qzn64@dtZ ze!(b(wo;Mk^C;fxm)c5|z52QML2%EzxX5yMi;(*~4Y}NMd*z}Fa($;eUDWkZ0-2AM8fMoHg+Orq*V!GLeM( zMk9Og&8iQ#Z8LqniN3aJH}u+?B&!EvZBng$jzmVxQ*CoN+}_q}FHNdLkyjaT6Zw_BT&6^Jd!x`T4bQX{MQx*?jp~gvZ9@Owr|Jc5XZ16`(s)ewY&tN|S*%NVaRAGI zxSyE|b+ +#include +#include +#if defined __BORLANDC__ && defined _Windows && !(defined __32BIT__ || defined __WIN32__) + /* setjmp() and longjmp() not well supported in 16-bit windows */ + #include + typedef int jmp_buf[9]; + #define setjmp(b) Catch(b) + #define longjmp(b,e) Throw(b,e) +#else + #include +#endif +#include "osdefs.h" +#include "amx.h" + +/* Note: the "cell" and "ucell" types are defined in AMX.H */ + +#define PUBLIC_CHAR '@' /* character that defines a function "public" */ +#define CTRL_CHAR '\\' /* default control character */ +#define sCHARBITS 8 /* size of a packed character */ + +#define sDIMEN_MAX 3 /* maximum number of array dimensions */ +#define sLINEMAX 511 /* input line length (in characters) */ +#define sCOMP_STACK 32 /* maximum nesting of #if .. #endif sections */ +#define sDEF_LITMAX 500 /* initial size of the literal pool, in "cells" */ +#define sDEF_AMXSTACK 4096 /* default stack size for AMX files */ +#define PREPROC_TERM '\x7f'/* termination character for preprocessor expressions (the "DEL" code) */ +#define sDEF_PREFIX "default.inc" /* default prefix filename */ + +typedef union { + void *pv; /* e.g. a name */ + int i; +} stkitem; /* type of items stored on the compiler stack */ + +typedef struct s_arginfo { /* function argument info */ + char name[sNAMEMAX+1]; + char ident; /* iVARIABLE, iREFERENCE, iREFARRAY or iVARARGS */ + char usage; /* uCONST */ + int *tags; /* argument tag id. list */ + int numtags; /* number of tags in the tag list */ + int dim[sDIMEN_MAX]; + int idxtag[sDIMEN_MAX]; + int numdim; /* number of dimensions */ + unsigned char hasdefault; /* bit0: is there a default value? bit6: "tagof"; bit7: "sizeof" */ + union { + cell val; /* default value */ + struct { + char *symname; /* name of another symbol */ + short level; /* indirection level for that symbol */ + } size; /* used for "sizeof" default value */ + struct { + cell *data; /* values of default array */ + int size; /* complete length of default array */ + int arraysize; /* size to reserve on the heap */ + cell addr; /* address of the default array in the data segment */ + } array; + } defvalue; /* default value, or pointer to default array */ + int defvalue_tag; /* tag of the default value */ +} arginfo; + +/* Equate table, tagname table, library table */ +typedef struct s_constvalue { + struct s_constvalue *next; + char name[sNAMEMAX+1]; + cell value; + int index; /* index level, for constants referring to array sizes/tags + * automaton id. for states and automatons + * tag for enumeration lists */ +} constvalue; + +/* Symbol table format + * + * The symbol name read from the input file is stored in "name", the + * value of "addr" is written to the output file. The address in "addr" + * depends on the class of the symbol: + * global offset into the data segment + * local offset relative to the stack frame + * label generated hexadecimal number + * function offset into code segment + */ +typedef struct s_symbol { + struct s_symbol *next; + struct s_symbol *parent; /* hierarchical types (multi-dimensional arrays) */ + char name[sNAMEMAX+1]; + uint32_t hash; /* value derived from name, for quicker searching */ + cell addr; /* address or offset (or value for constant, index for native function) */ + cell codeaddr; /* address (in the code segment) where the symbol declaration starts */ + char vclass; /* sLOCAL if "addr" refers to a local symbol */ + char ident; /* see below for possible values */ + short usage; /* see below for possible values */ + char flags; /* see below for possible values */ + int compound; /* compound level (braces nesting level) */ + int tag; /* tagname id */ + union { + int declared; /* label: how many local variables are declared */ + struct { + int index; /* array & enum: tag of array indices or the enum item */ + int field; /* enumeration fields, where a size is attached to the field */ + } tags; /* extra tags */ + constvalue *lib; /* native function: library it is part of */ + long stacksize; /* normal/public function: stack requirements */ + } x; /* 'x' for 'extra' */ + union { + arginfo *arglist; /* types of all parameters for functions */ + constvalue *enumlist;/* list of names for the "root" of an enumeration */ + struct { + cell length; /* arrays: length (size) */ + short level; /* number of dimensions below this level */ + } array; + } dim; /* for 'dimension', both functions and arrays */ + constvalue *states; /* list of state function/state variable ids + addresses */ + int fnumber; /* static global variables: file number in which the declaration is visible */ + int lnumber; /* line number (in the current source file) for the declaration */ + struct s_symbol **refer; /* referrer list, functions that "use" this symbol */ + int numrefers; /* number of entries in the referrer list */ + char *documentation; /* optional documentation string */ +} symbol; + + +/* Possible entries for "ident". These are used in the "symbol", "value" + * and arginfo structures. Not every constant is valid for every use. + * In an argument list, the list is terminated with a "zero" ident; labels + * cannot be passed as function arguments, so the value 0 is overloaded. + */ +#define iLABEL 0 +#define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ +#define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ +#define iARRAY 3 +#define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ +#define iARRAYCELL 5 /* array element, cell that must be fetched indirectly */ +#define iARRAYCHAR 6 /* array element, character from cell from array */ +#define iEXPRESSION 7 /* expression result, has no address (rvalue) */ +#define iCONSTEXPR 8 /* constant expression (or constant symbol) */ +#define iFUNCTN 9 +#define iREFFUNC 10 +#define iVARARGS 11 /* function specified ... as argument(s) */ + +/* Possible entries for "usage" + * + * This byte is used as a serie of bits, the syntax is different for + * functions and other symbols: + * + * VARIABLE + * bits: 0 (uDEFINE) the variable is defined in the source file + * 1 (uREAD) the variable is "read" (accessed) in the source file + * 2 (uWRITTEN) the variable is altered (assigned a value) + * 3 (uCONST) the variable is constant (may not be assigned to) + * 4 (uPUBLIC) the variable is public + * 6 (uSTOCK) the variable is discardable (without warning) + * + * FUNCTION + * bits: 0 (uDEFINE) the function is defined ("implemented") in the source file + * 1 (uREAD) the function is invoked in the source file + * 2 (uRETVALUE) the function returns a value (or should return a value) + * 3 (uPROTOTYPED) the function was prototyped (implicitly via a definition or explicitly) + * 4 (uPUBLIC) the function is public + * 5 (uNATIVE) the function is native + * 6 (uSTOCK) the function is discardable (without warning) + * 7 (uMISSING) the function is not implemented in this source file + * 8 (uFORWARD) the function is explicitly forwardly declared + * + * CONSTANT + * bits: 0 (uDEFINE) the symbol is defined in the source file + * 1 (uREAD) the constant is "read" (accessed) in the source file + * 2 (uWRITTEN) redundant, but may be set for constants passed by reference + * 3 (uPREDEF) the constant is pre-defined and should be kept between passes + * 5 (uENUMROOT) the constant is the "root" of an enumeration + * 6 (uENUMFIELD) the constant is a field in a named enumeration + */ +#define uDEFINE 0x001 +#define uREAD 0x002 +#define uWRITTEN 0x004 +#define uRETVALUE 0x004 /* function returns (or should return) a value */ +#define uCONST 0x008 +#define uPROTOTYPED 0x008 +#define uPREDEF 0x008 /* constant is pre-defined */ +#define uPUBLIC 0x010 +#define uNATIVE 0x020 +#define uENUMROOT 0x020 +#define uSTOCK 0x040 +#define uENUMFIELD 0x040 +#define uMISSING 0x080 +#define uFORWARD 0x100 +/* uRETNONE is not stored in the "usage" field of a symbol. It is + * used during parsing a function, to detect a mix of "return;" and + * "return value;" in a few special cases. + */ +#define uRETNONE 0x10 + +#define flgDEPRICATED 0x01 /* symbol is depricated (avoid use) */ + +#define uTAGOF 0x40 /* set in the "hasdefault" field of the arginfo struct */ +#define uSIZEOF 0x80 /* set in the "hasdefault" field of the arginfo struct */ + +#define uMAINFUNC "main" +#define uENTRYFUNC "entry" + +#define sGLOBAL 0 /* global variable/constant class (no states) */ +#define sLOCAL 1 /* local variable/constant */ +#define sSTATIC 2 /* global life, local scope */ + +#define sSTATEVAR 3 /* criterion to find variables (sSTATEVAR implies a global variable) */ + +typedef struct s_value { + symbol *sym; /* symbol in symbol table, NULL for (constant) expression */ + cell constval; /* value of the constant expression (if ident==iCONSTEXPR) + * also used for the size of a literal array */ + int tag; /* tag (of the expression) */ + int cmptag; /* for searching symbols: choose the one with the matching tag */ + char ident; /* iCONSTEXPR, iVARIABLE, iARRAY, iARRAYCELL, + * iEXPRESSION or iREFERENCE */ + char boolresult; /* boolean result for relational operators */ + cell *arrayidx; /* last used array indices, for checking self assignment */ +} value; + +/* "while" statement queue (also used for "for" and "do - while" loops) */ +enum { + wqBRK, /* used to restore stack for "break" */ + wqCONT, /* used to restore stack for "continue" */ + wqLOOP, /* loop start label number */ + wqEXIT, /* loop exit label number (jump if false) */ + /* --- */ + wqSIZE /* "while queue" size */ +}; +#define wqTABSZ (24*wqSIZE) /* 24 nested loop statements */ + +enum { + statIDLE, /* not compiling yet */ + statFIRST, /* first pass */ + statWRITE, /* writing output */ + statSKIP, /* skipping output */ +}; + +typedef struct s_stringlist { + struct s_stringlist *next; + char *line; +} stringlist; + +typedef struct s_stringpair { + struct s_stringpair *next; + char *first; + char *second; + int matchlength; +} stringpair; + +/* macros for code generation */ +#define opcodes(n) ((n)*sizeof(cell)) /* opcode size */ +#define opargs(n) ((n)*sizeof(cell)) /* size of typical argument */ + +/* Tokens recognized by lex() + * Some of these constants are assigned as well to the variable "lastst" + */ +#define tFIRST 256 /* value of first multi-character operator */ +#define tMIDDLE 280 /* value of last multi-character operator */ +#define tLAST 325 /* value of last multi-character match-able token */ +/* multi-character operators */ +#define taMULT 256 /* *= */ +#define taDIV 257 /* /= */ +#define taMOD 258 /* %= */ +#define taADD 259 /* += */ +#define taSUB 260 /* -= */ +#define taSHL 261 /* <<= */ +#define taSHRU 262 /* >>>= */ +#define taSHR 263 /* >>= */ +#define taAND 264 /* &= */ +#define taXOR 265 /* ^= */ +#define taOR 266 /* |= */ +#define tlOR 267 /* || */ +#define tlAND 268 /* && */ +#define tlEQ 269 /* == */ +#define tlNE 270 /* != */ +#define tlLE 271 /* <= */ +#define tlGE 272 /* >= */ +#define tSHL 273 /* << */ +#define tSHRU 274 /* >>> */ +#define tSHR 275 /* >> */ +#define tINC 276 /* ++ */ +#define tDEC 277 /* -- */ +#define tELLIPS 278 /* ... */ +#define tDBLDOT 279 /* .. */ +#define tDBLCOLON 280 /* :: */ +/* reserved words (statements) */ +#define tASSERT 281 +#define tBREAK 282 +#define tCASE 283 +#define tCHAR 284 +#define tCONST 285 +#define tCONTINUE 286 +#define tDEFAULT 287 +#define tDEFINED 288 +#define tDO 289 +#define tELSE 290 +#define tENUM 291 +#define tEXIT 292 +#define tFOR 293 +#define tFORWARD 294 +#define tGOTO 295 +#define tIF 296 +#define tNATIVE 297 +#define tNEW 298 +#define tOPERATOR 299 +#define tPUBLIC 300 +#define tRETURN 301 +#define tSIZEOF 302 +#define tSLEEP 303 +#define tSTATE 304 +#define tSTATIC 305 +#define tSTOCK 306 +#define tSWITCH 307 +#define tTAGOF 308 +#define tWHILE 309 +/* compiler directives */ +#define tpASSERT 310 /* #assert */ +#define tpDEFINE 311 +#define tpELSE 312 /* #else */ +#define tpELSEIF 313 /* #elseif */ +#define tpEMIT 314 +#define tpENDIF 315 +#define tpENDINPUT 316 +#define tpENDSCRPT 317 +#define tpERROR 318 +#define tpFILE 319 +#define tpIF 320 /* #if */ +#define tINCLUDE 321 +#define tpLINE 322 +#define tpPRAGMA 323 +#define tpTRYINCLUDE 324 +#define tpUNDEF 325 +/* semicolon is a special case, because it can be optional */ +#define tTERM 326 /* semicolon or newline */ +#define tENDEXPR 327 /* forced end of expression */ +/* other recognized tokens */ +#define tNUMBER 328 /* integer number */ +#define tRATIONAL 329 /* rational number */ +#define tSYMBOL 330 +#define tLABEL 331 +#define tSTRING 332 +#define tEXPR 333 /* for assigment to "lastst" only */ + +/* (reversed) evaluation of staging buffer */ +#define sSTARTREORDER 0x01 +#define sENDREORDER 0x02 +#define sEXPRSTART 0x80 /* top bit set, rest is free */ +#define sMAXARGS 127 /* relates to the bit pattern of sEXPRSTART */ + +#define sDOCSEP 0x01 /* to separate documentation comments between functions */ + +/* codes for ffabort() */ +#define xEXIT 1 /* exit code in PRI */ +#define xASSERTION 2 /* abort caused by failing assertion */ +#define xSTACKERROR 3 /* stack/heap overflow */ +#define xBOUNDSERROR 4 /* array index out of bounds */ +#define xMEMACCESS 5 /* data access error */ +#define xINVINSTR 6 /* invalid instruction */ +#define xSTACKUNDERFLOW 7 /* stack underflow */ +#define xHEAPUNDERFLOW 8 /* heap underflow */ +#define xCALLBACKERR 9 /* no, or invalid, callback */ +#define xSLEEP 12 /* sleep, exit code in PRI, tag in ALT */ + +/* Miscellaneous */ +#if !defined TRUE + #define FALSE 0 + #define TRUE 1 +#endif +#define sIN_CSEG 1 /* if parsing CODE */ +#define sIN_DSEG 2 /* if parsing DATA */ +#define sCHKBOUNDS 1 /* bit position in "debug" variable: check bounds */ +#define sSYMBOLIC 2 /* bit position in "debug" variable: symbolic info */ +#define sRESET 0 /* reset error flag */ +#define sFORCESET 1 /* force error flag on */ +#define sEXPRMARK 2 /* mark start of expression */ +#define sEXPRRELEASE 3 /* mark end of expression */ +#define sSETPOS 4 /* set line number for the error */ + +enum { + sOPTIMIZE_NONE, /* no optimization */ + sOPTIMIZE_NOMACRO, /* no macro instructions */ + sOPTIMIZE_DEFAULT, /* full optimization */ + /* ----- */ + sOPTIMIZE_NUMBER +}; + +typedef enum s_regid { + sPRI, /* indicates the primary register */ + sALT, /* indicates the secundary register */ +} regid; + +typedef enum s_optmark { + sEXPR, /* end of expression (for expressions that form a statement) */ + sPARM, /* end of parameter (in a function parameter list) */ + sLDECL, /* start of local declaration (variable) */ +} optmark; + +#define suSLEEP_INSTR 0x01 /* the "sleep" instruction was used */ + +#if INT_MAX<0x8000u + #define PUBLICTAG 0x8000u + #define FIXEDTAG 0x4000u +#else + #define PUBLICTAG 0x80000000Lu + #define FIXEDTAG 0x40000000Lu +#endif +#define TAGMASK (~PUBLICTAG) +#define CELL_MAX (((ucell)1 << (sizeof(cell)*8-1)) - 1) + + +/* interface functions */ +#if defined __cplusplus + extern "C" { +#endif + +/* + * Functions you call from the "driver" program + */ +int pc_compile(int argc, char **argv); +int pc_addconstant(char *name,cell value,int tag); +int pc_addtag(char *name); +int pc_enablewarning(int number,int enable); + +/* + * Functions called from the compiler (to be implemented by you) + */ + +/* general console output */ +int pc_printf(const char *message,...); + +/* error report function */ +int pc_error(int number,char *message,char *filename,int firstline,int lastline,va_list argptr); + +/* input from source file */ +void *pc_opensrc(char *filename); /* reading only */ +void *pc_createsrc(char *filename); +void pc_closesrc(void *handle); /* never delete */ +void pc_resetsrc(void *handle,void *position); /* reset to a position marked earlier */ +char *pc_readsrc(void *handle,unsigned char *target,int maxchars); +int pc_writesrc(void *handle,unsigned char *source); +void *pc_getpossrc(void *handle); /* mark the current position */ +int pc_eofsrc(void *handle); + +/* output to intermediate (.ASM) file */ +void *pc_openasm(char *filename); /* read/write */ +void pc_closeasm(void *handle,int deletefile); +void pc_resetasm(void *handle); +int pc_writeasm(void *handle,char *str); +char *pc_readasm(void *handle,char *target,int maxchars); + +/* output to binary (.AMX) file */ +void *pc_openbin(char *filename); +void pc_closebin(void *handle,int deletefile); +void pc_resetbin(void *handle,long offset); +int pc_writebin(void *handle,void *buffer,int size); +long pc_lengthbin(void *handle); /* return the length of the file */ + +#if defined __cplusplus + } +#endif + + +/* by default, functions and variables used in throughout the compiler + * files are "external" + */ +#if !defined SC_FUNC + #define SC_FUNC +#endif +#if !defined SC_VDECL + #define SC_VDECL extern +#endif +#if !defined SC_VDEFINE + #define SC_VDEFINE +#endif + +/* function prototypes in SC1.C */ +SC_FUNC void set_extension(char *filename,char *extension,int force); +SC_FUNC symbol *fetchfunc(char *name,int tag); +SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag); +SC_FUNC char *funcdisplayname(char *dest,char *funcname); +SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr); +SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index); +SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index); +SC_FUNC void delete_consttable(constvalue *table); +SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag); +SC_FUNC void exporttag(int tag); +SC_FUNC void sc_attachdocumentation(symbol *sym); + +/* function prototypes in SC2.C */ +#define PUSHSTK_P(v) { stkitem s_; s_.pv=(v); pushstk(s_); } +#define PUSHSTK_I(v) { stkitem s_; s_.i=(v); pushstk(s_); } +#define POPSTK_P() (popstk().pv) +#define POPSTK_I() (popstk().i) +SC_FUNC void pushstk(stkitem val); +SC_FUNC stkitem popstk(void); +SC_FUNC void clearstk(void); +SC_FUNC int plungequalifiedfile(char *name); /* explicit path included */ +SC_FUNC int plungefile(char *name,int try_currentpath,int try_includepaths); /* search through "include" paths */ +SC_FUNC void preprocess(void); +SC_FUNC void lexinit(void); +SC_FUNC int lex(cell *lexvalue,char **lexsym); +SC_FUNC void lexpush(void); +SC_FUNC void lexclr(int clreol); +SC_FUNC int matchtoken(int token); +SC_FUNC int tokeninfo(cell *val,char **str); +SC_FUNC int needtoken(int token); +SC_FUNC void litadd(cell value); +SC_FUNC void litinsert(cell value,int pos); +SC_FUNC int alphanum(char c); +SC_FUNC int ishex(char c); +SC_FUNC void delete_symbol(symbol *root,symbol *sym); +SC_FUNC void delete_symbols(symbol *root,int level,int del_labels,int delete_functions); +SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom); +SC_FUNC void markusage(symbol *sym,int usage); +SC_FUNC uint32_t namehash(const char *name); +SC_FUNC symbol *findglb(const char *name,int filter); +SC_FUNC symbol *findloc(const char *name); +SC_FUNC symbol *findconst(const char *name,int *matchtag); +SC_FUNC symbol *finddepend(const symbol *parent); +SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag, + int usage); +SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag, + int dim[],int numdim,int idxtag[]); +SC_FUNC int getlabel(void); +SC_FUNC char *itoh(ucell val); + +/* function prototypes in SC3.C */ +SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, + value *lval,int *resulttag); +SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce); +SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult); +SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state); +SC_FUNC cell array_totalsize(symbol *sym); + +/* function prototypes in SC4.C */ +SC_FUNC void writeleader(symbol *root); +SC_FUNC void writetrailer(void); +SC_FUNC void begcseg(void); +SC_FUNC void begdseg(void); +SC_FUNC void setline(int chkbounds); +SC_FUNC void setfiledirect(char *name); +SC_FUNC void setlinedirect(int line); +SC_FUNC void setlabel(int index); +SC_FUNC void markexpr(optmark type,const char *name,cell offset); +SC_FUNC void startfunc(char *fname); +SC_FUNC void endfunc(void); +SC_FUNC void alignframe(int numbytes); +SC_FUNC void rvalue(value *lval); +SC_FUNC void address(symbol *ptr,regid reg); +SC_FUNC void store(value *lval); +SC_FUNC void loadreg(cell address,regid reg); +SC_FUNC void storereg(cell address,regid reg); +SC_FUNC void memcopy(cell size); +SC_FUNC void copyarray(symbol *sym,cell size); +SC_FUNC void fillarray(symbol *sym,cell size,cell value); +SC_FUNC void ldconst(cell val,regid reg); +SC_FUNC void moveto1(void); +SC_FUNC void pushreg(regid reg); +SC_FUNC void pushval(cell val); +SC_FUNC void popreg(regid reg); +SC_FUNC void swap1(void); +SC_FUNC void ffswitch(int label); +SC_FUNC void ffcase(cell value,char *labelname,int newtable); +SC_FUNC void ffcall(symbol *sym,const char *label,int numargs); +SC_FUNC void ffret(int remparams); +SC_FUNC void ffabort(int reason); +SC_FUNC void ffbounds(cell size); +SC_FUNC void jumplabel(int number); +SC_FUNC void defstorage(void); +SC_FUNC void modstk(int delta); +SC_FUNC void setstk(cell value); +SC_FUNC void modheap(int delta); +SC_FUNC void setheap_pri(void); +SC_FUNC void setheap(cell value); +SC_FUNC void cell2addr(void); +SC_FUNC void cell2addr_alt(void); +SC_FUNC void addr2cell(void); +SC_FUNC void char2addr(void); +SC_FUNC void charalign(void); +SC_FUNC void addconst(cell value); + +/* Code generation functions for arithmetic operators. + * + * Syntax: o[u|s|b]_name + * | | | +--- name of operator + * | | +----- underscore + * | +--------- "u"nsigned operator, "s"igned operator or "b"oth + * +------------- "o"perator + */ +SC_FUNC void os_mult(void); /* multiplication (signed) */ +SC_FUNC void os_div(void); /* division (signed) */ +SC_FUNC void os_mod(void); /* modulus (signed) */ +SC_FUNC void ob_add(void); /* addition */ +SC_FUNC void ob_sub(void); /* subtraction */ +SC_FUNC void ob_sal(void); /* shift left (arithmetic) */ +SC_FUNC void os_sar(void); /* shift right (arithmetic, signed) */ +SC_FUNC void ou_sar(void); /* shift right (logical, unsigned) */ +SC_FUNC void ob_or(void); /* bitwise or */ +SC_FUNC void ob_xor(void); /* bitwise xor */ +SC_FUNC void ob_and(void); /* bitwise and */ +SC_FUNC void ob_eq(void); /* equality */ +SC_FUNC void ob_ne(void); /* inequality */ +SC_FUNC void relop_prefix(void); +SC_FUNC void relop_suffix(void); +SC_FUNC void os_le(void); /* less or equal (signed) */ +SC_FUNC void os_ge(void); /* greater or equal (signed) */ +SC_FUNC void os_lt(void); /* less (signed) */ +SC_FUNC void os_gt(void); /* greater (signed) */ + +SC_FUNC void lneg(void); +SC_FUNC void neg(void); +SC_FUNC void invert(void); +SC_FUNC void nooperation(void); +SC_FUNC void inc(value *lval); +SC_FUNC void dec(value *lval); +SC_FUNC void jmp_ne0(int number); +SC_FUNC void jmp_eq0(int number); +SC_FUNC void outval(cell val,int newline); + +/* function prototypes in SC5.C */ +SC_FUNC int error(int number,...); +SC_FUNC void errorset(int code,int line); + +/* function prototypes in SC6.C */ +SC_FUNC int assemble(FILE *fout,FILE *fin); + +/* function prototypes in SC7.C */ +SC_FUNC void stgbuffer_cleanup(void); +SC_FUNC void stgmark(char mark); +SC_FUNC void stgwrite(const char *st); +SC_FUNC void stgout(int index); +SC_FUNC void stgdel(int index,cell code_index); +SC_FUNC int stgget(int *index,cell *code_index); +SC_FUNC void stgset(int onoff); +SC_FUNC int phopt_init(void); +SC_FUNC int phopt_cleanup(void); + +/* function prototypes in SCLIST.C */ +SC_FUNC char* duplicatestring(const char* sourcestring); +SC_FUNC stringpair *insert_alias(char *name,char *alias); +SC_FUNC stringpair *find_alias(char *name); +SC_FUNC int lookup_alias(char *target,char *name); +SC_FUNC void delete_aliastable(void); +SC_FUNC stringlist *insert_path(char *path); +SC_FUNC char *get_path(int index); +SC_FUNC void delete_pathtable(void); +SC_FUNC stringpair *insert_subst(char *pattern,char *substitution,int prefixlen); +SC_FUNC int get_subst(int index,char **pattern,char **substitution); +SC_FUNC stringpair *find_subst(char *name,int length); +SC_FUNC int delete_subst(char *name,int length); +SC_FUNC void delete_substtable(void); +SC_FUNC stringlist *insert_sourcefile(char *string); +SC_FUNC char *get_sourcefile(int index); +SC_FUNC void delete_sourcefiletable(void); +SC_FUNC stringlist *insert_docstring(char *string); +SC_FUNC char *get_docstring(int index); +SC_FUNC void delete_docstring(int index); +SC_FUNC void delete_docstringtable(void); +SC_FUNC stringlist *insert_autolist(char *string); +SC_FUNC char *get_autolist(int index); +SC_FUNC void delete_autolisttable(void); +SC_FUNC stringlist *insert_dbgfile(const char *filename); +SC_FUNC stringlist *insert_dbgline(int linenr); +SC_FUNC stringlist *insert_dbgsymbol(symbol *sym); +SC_FUNC char *get_dbgstring(int index); +SC_FUNC void delete_dbgstringtable(void); + +/* function prototypes in SCMEMFILE.C */ +#if !defined tMEMFILE + typedef unsigned char MEMFILE; + #define tMEMFILE 1 +#endif +MEMFILE *mfcreate(char *filename); +void mfclose(MEMFILE *mf); +int mfdump(MEMFILE *mf); +long mflength(MEMFILE *mf); +long mfseek(MEMFILE *mf,long offset,int whence); +unsigned int mfwrite(MEMFILE *mf,unsigned char *buffer,unsigned int size); +unsigned int mfread(MEMFILE *mf,unsigned char *buffer,unsigned int size); +char *mfgets(MEMFILE *mf,char *string,unsigned int size); +int mfputs(MEMFILE *mf,char *string); + +/* function prototypes in SCI18N.C */ +#define MAXCODEPAGE 12 +SC_FUNC int cp_path(const char *root,const char *directory); +SC_FUNC int cp_set(const char *name); +SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr); +SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr); +SC_FUNC int scan_utf8(FILE *fp,const char *filename); + +/* function prototypes in SCSTATE.C */ +SC_FUNC constvalue *automaton_add(const char *name); +SC_FUNC constvalue *automaton_find(const char *name); +SC_FUNC constvalue *automaton_findid(int id); +SC_FUNC constvalue *state_add(const char *name,int fsa_id); +SC_FUNC constvalue *state_find(const char *name,int fsa_id); +SC_FUNC constvalue *state_findid(int id); +SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid); +SC_FUNC int state_addlist(int *list,int count,int fsa_id); +SC_FUNC void state_deletetable(void); +SC_FUNC int state_getfsa(int listid); +SC_FUNC int state_count(int listid); +SC_FUNC int state_inlist(int listid,int state); +SC_FUNC int state_listitem(int listid,int index); +SC_FUNC void state_conflict(symbol *root); +SC_FUNC int state_conflict_id(int listid1,int listid2); + +/* external variables (defined in scvars.c) */ +#if !defined SC_SKIP_VDECL +SC_VDECL symbol loctab; /* local symbol table */ +SC_VDECL symbol glbtab; /* global symbol table */ +SC_VDECL cell *litq; /* the literal queue */ +SC_VDECL unsigned char pline[]; /* the line read from the input file */ +SC_VDECL const unsigned char *lptr;/* points to the current position in "pline" */ +SC_VDECL constvalue tagname_tab;/* tagname table */ +SC_VDECL constvalue libname_tab;/* library table (#pragma library "..." syntax) */ +SC_VDECL constvalue *curlibrary;/* current library */ +SC_VDECL int pc_addlibtable; /* is the library table added to the AMX file? */ +SC_VDECL symbol *curfunc; /* pointer to current function */ +SC_VDECL char *inpfname; /* name of the file currently read from */ +SC_VDECL char outfname[]; /* intermediate (assembler) file name */ +SC_VDECL char binfname[]; /* binary file name */ +SC_VDECL char errfname[]; /* error file name */ +SC_VDECL char sc_ctrlchar; /* the control character (or escape character) */ +SC_VDECL char sc_ctrlchar_org;/* the default control character */ +SC_VDECL int litidx; /* index to literal table */ +SC_VDECL int litmax; /* current size of the literal table */ +SC_VDECL int stgidx; /* index to the staging buffer */ +SC_VDECL int sc_labnum; /* number of (internal) labels */ +SC_VDECL int staging; /* true if staging output */ +SC_VDECL cell declared; /* number of local cells declared */ +SC_VDECL cell glb_declared; /* number of global cells declared */ +SC_VDECL cell code_idx; /* number of bytes with generated code */ +SC_VDECL int ntv_funcid; /* incremental number of native function */ +SC_VDECL int errnum; /* number of errors */ +SC_VDECL int warnnum; /* number of warnings */ +SC_VDECL int sc_debug; /* debug/optimization options (bit field) */ +SC_VDECL int sc_packstr; /* strings are packed by default? */ +SC_VDECL int sc_asmfile; /* create .ASM file? */ +SC_VDECL int sc_listing; /* create .LST file? */ +SC_VDECL int sc_compress; /* compress bytecode? */ +SC_VDECL int sc_needsemicolon;/* semicolon required to terminate expressions? */ +SC_VDECL int sc_dataalign; /* data alignment value */ +SC_VDECL int sc_alignnext; /* must frame of the next function be aligned? */ +SC_VDECL int pc_docexpr; /* must expression be attached to documentation comment? */ +SC_VDECL int curseg; /* 1 if currently parsing CODE, 2 if parsing DATA */ +SC_VDECL cell pc_stksize; /* stack size */ +SC_VDECL cell pc_amxlimit; /* abstract machine size limit (code + data, or only code) */ +SC_VDECL cell pc_amxram; /* abstract machine data size limit */ +SC_VDECL int freading; /* is there an input file ready for reading? */ +SC_VDECL int fline; /* the line number in the current file */ +SC_VDECL short fnumber; /* number of files in the file table (debugging) */ +SC_VDECL short fcurrent; /* current file being processed (debugging) */ +SC_VDECL short sc_intest; /* true if inside a test */ +SC_VDECL int sideeffect; /* true if an expression causes a side-effect */ +SC_VDECL int stmtindent; /* current indent of the statement */ +SC_VDECL int indent_nowarn; /* skip warning "217 loose indentation" */ +SC_VDECL int sc_tabsize; /* number of spaces that a TAB represents */ +SC_VDECL short sc_allowtags; /* allow/detect tagnames in lex() */ +SC_VDECL int sc_status; /* read/write status */ +SC_VDECL int sc_rationaltag; /* tag for rational numbers */ +SC_VDECL int rational_digits; /* number of fractional digits */ +SC_VDECL int sc_allowproccall;/* allow/detect tagnames in lex() */ +SC_VDECL short sc_is_utf8; /* is this source file in UTF-8 encoding */ +SC_VDECL char *pc_depricate; /* if non-NULL, mark next declaration as depricated */ +SC_VDECL int sc_curstates; /* ID of the current state list */ +SC_VDECL int pc_optimize; /* (peephole) optimization level */ +SC_VDECL int pc_memflags; /* special flags for the stack/heap usage */ + +SC_VDECL constvalue sc_automaton_tab; /* automaton table */ +SC_VDECL constvalue sc_state_tab; /* state table */ + +SC_VDECL FILE *inpf; /* file read from (source or include) */ +SC_VDECL FILE *inpf_org; /* main source file */ +SC_VDECL FILE *outf; /* file written to */ + +SC_VDECL jmp_buf errbuf; /* target of longjmp() on a fatal error */ + +#if !defined SC_LIGHT + SC_VDECL int sc_makereport; /* generate a cross-reference report */ +#endif + +#endif /* SC_SKIP_VDECL */ + +#endif /* SC_H_INCLUDED */ diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c new file mode 100644 index 00000000..03c7afba --- /dev/null +++ b/compiler-init/sc1.c @@ -0,0 +1,5592 @@ +/* Pawn compiler + * + * Function and variable definition and declaration, statement parser. + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc1.c 3591 2006-06-25 14:30:07Z thiadmer $ + */ +#include +#include +#include +#include +#include +#include +#include + +#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ + #include + #include +#endif + +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include + #include /* from BinReloc, see www.autopackage.org */ +#endif + +#if defined FORTIFY + #include +#endif + +#if defined __BORLANDC__ || defined __WATCOMC__ + #include + static unsigned total_drives; /* dummy variable */ + #define dos_setdrive(i) _dos_setdrive(i,&total_drives) +#elif defined _MSC_VER && defined _WIN32 + #include /* for _chdrive() */ + #define dos_setdrive(i) _chdrive(i) +#endif +#if defined __BORLANDC__ + #include /* for chdir() */ +#elif defined __WATCOMC__ + #include /* for chdir() */ +#endif +#if defined __WIN32__ || defined _WIN32 || defined _Windows + #include +#endif + +#include "lstring.h" +#include "sc.h" +#include "svnrev.h" +#define VERSION_STR "3.2." SVN_REVSTR +#define VERSION_INT 0x0302 + +static void resetglobals(void); +static void initglobals(void); +static char *get_extension(char *filename); +static void setopt(int argc,char **argv,char *oname,char *ename,char *pname, + char *rname,char *codepage); +static void setconfig(char *root); +static void setcaption(void); +static void about(void); +static void setconstants(void); +static void parse(void); +static void dumplits(void); +static void dumpzero(int count); +static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst); +static void declglb(char *firstname,int firsttag,int fpublic,int fstatic, + int stock,int fconst); +static int declloc(int fstatic); +static void decl_const(int table); +static void decl_enum(int table); +static cell needsub(int *tag,constvalue **enumroot); +static void initials(int ident,int tag,cell *size,int dim[],int numdim, + constvalue *enumroot); +static cell initarray(int ident,int tag,int dim[],int numdim,int cur, + int startlit,int counteddim[],constvalue *lastdim, + constvalue *enumroot,int *errorfound); +static cell initvector(int ident,int tag,cell size,int fillzero, + constvalue *enumroot,int *errorfound); +static cell init(int ident,int *tag,int *errorfound); +static int getstates(const char *funcname); +static void attachstatelist(symbol *sym, int state_id); +static void funcstub(int fnative); +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock); +static int declargs(symbol *sym,int chkshadow); +static void doarg(char *name,int ident,int offset,int tags[],int numtags, + int fpublic,int fconst,int chkshadow,arginfo *arg); +static void make_report(symbol *root,FILE *log,char *sourcefile); +static void reduce_referrers(symbol *root); +static long max_stacksize(symbol *root,int *recursion); +static int testsymbols(symbol *root,int level,int testlabs,int testconst); +static void destructsymbols(symbol *root,int level); +static constvalue *find_constval_byval(constvalue *table,cell val); +static void statement(int *lastindent,int allow_decl); +static void compound(int stmt_sameline); +static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr, + int *tag,symbol **symptr,int chkfuncresult); +static void doassert(void); +static void doexit(void); +static void test(int label,int parens,int invert); +static void doif(void); +static void dowhile(void); +static void dodo(void); +static void dofor(void); +static void doswitch(void); +static void dogoto(void); +static void dolabel(void); +static symbol *fetchlab(char *name); +static void doreturn(void); +static void dobreak(void); +static void docont(void); +static void dosleep(void); +static void dostate(void); +static void addwhile(int *ptr); +static void delwhile(void); +static int *readwhile(void); + +static int lastst = 0; /* last executed statement type */ +static int nestlevel = 0; /* number of active (open) compound statements */ +static int rettype = 0; /* the type that a "return" expression should have */ +static int skipinput = 0; /* number of lines to skip from the first input file */ +static int optproccall = TRUE; /* support "procedure call" */ +static int verbosity = 1; /* verbosity level, 0=quiet, 1=normal, 2=verbose */ +static int sc_reparse = 0; /* needs 3th parse because of changed prototypes? */ +static int sc_parsenum = 0; /* number of the extra parses */ +static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */ +static int *wqptr; /* pointer to next entry */ +#if !defined SC_LIGHT + static char sc_rootpath[_MAX_PATH]; + static char *sc_documentation=NULL;/* main documentation */ +#endif +#if defined __WIN32__ || defined _WIN32 || defined _Windows + static HWND hwndFinish = 0; +#endif + +/* "main" of the compiler + */ +#if defined __cplusplus + extern "C" +#endif +int pc_compile(int argc, char *argv[]) +{ + int entry,i,jmpcode; + int retcode; + char incfname[_MAX_PATH]; + char reportname[_MAX_PATH]; + char codepage[MAXCODEPAGE+1]; + FILE *binf; + void *inpfmark; + int lcl_packstr,lcl_needsemicolon,lcl_tabsize; + #if !defined SC_LIGHT + int hdrsize=0; + #endif + char *ptr; + + /* set global variables to their initial value */ + binf=NULL; + initglobals(); + errorset(sRESET,0); + errorset(sEXPRRELEASE,0); + lexinit(); + + /* make sure that we clean up on a fatal error; do this before the first + * call to error(). */ + if ((jmpcode=setjmp(errbuf))!=0) + goto cleanup; + + /* allocate memory for fixed tables */ + inpfname=(char*)malloc(_MAX_PATH); + if (inpfname==NULL) + error(103); /* insufficient memory */ + litq=(cell*)malloc(litmax*sizeof(cell)); + if (litq==NULL) + error(103); /* insufficient memory */ + if (!phopt_init()) + error(103); /* insufficient memory */ + + setopt(argc,argv,outfname,errfname,incfname,reportname,codepage); + strcpy(binfname,outfname); + ptr=get_extension(binfname); + if (ptr!=NULL && stricmp(ptr,".asm")==0) + set_extension(binfname,".amx",TRUE); + else + set_extension(binfname,".amx",FALSE); + /* set output names that depend on the input name */ + if (sc_listing) + set_extension(outfname,".lst",TRUE); + else + set_extension(outfname,".asm",TRUE); + if (strlen(errfname)!=0) + remove(errfname); /* delete file on startup */ + else if (verbosity>0) + setcaption(); + setconfig(argv[0]); /* the path to the include and codepage files */ + sc_ctrlchar_org=sc_ctrlchar; + lcl_packstr=sc_packstr; + lcl_needsemicolon=sc_needsemicolon; + lcl_tabsize=sc_tabsize; + #if !defined NO_CODEPAGE + if (!cp_set(codepage)) /* set codepage */ + error(108); /* codepage mapping file not found */ + #endif + /* optionally create a temporary input file that is a collection of all + * input files + */ + assert(get_sourcefile(0)!=NULL); /* there must be at least one source file */ + if (get_sourcefile(1)!=NULL) { + /* there are at least two or more source files */ + char *tname,*sname; + FILE *ftmp,*fsrc; + int fidx; + #if defined __WIN32__ || defined _WIN32 + tname=_tempnam(NULL,"pawn"); + #elif defined __MSDOS__ || defined _Windows + tname=tempnam(NULL,"pawn"); + #elif defined(MACOS) && !defined(__MACH__) + /* tempnam is not supported for the Macintosh CFM build. */ + error(104,get_sourcefile(1)); + tname=NULL; + sname=NULL; + #else + tname=tempnam(NULL,"pawn"); + #endif + ftmp=(FILE*)pc_createsrc(tname); + for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) { + unsigned char tstring[128]; + fsrc=(FILE*)pc_opensrc(sname); + if (fsrc==NULL) { + strcpy(inpfname,sname); /* avoid invalid filename */ + error(100,sname); + } /* if */ + pc_writesrc(ftmp,(unsigned char*)"#file "); + pc_writesrc(ftmp,(unsigned char*)sname); + pc_writesrc(ftmp,(unsigned char*)"\n"); + while (!pc_eofsrc(fsrc)) { + pc_readsrc(fsrc,tstring,sizeof tstring); + pc_writesrc(ftmp,tstring); + } /* while */ + pc_closesrc(fsrc); + } /* for */ + pc_closesrc(ftmp); + strcpy(inpfname,tname); + free(tname); + } else { + strcpy(inpfname,get_sourcefile(0)); + } /* if */ + inpf_org=(FILE*)pc_opensrc(inpfname); + if (inpf_org==NULL) + error(100,inpfname); + freading=TRUE; + outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */ + if (outf==NULL) + error(101,outfname); + /* immediately open the binary file, for other programs to check */ + if (sc_asmfile || sc_listing) { + binf=NULL; + } else { + binf=(FILE*)pc_openbin(binfname); + if (binf==NULL) + error(101,binfname); + } /* if */ + setconstants(); /* set predefined constants and tagnames */ + for (i=0; i0) { + if (strcmp(incfname,sDEF_PREFIX)==0) { + plungefile(incfname,FALSE,TRUE); /* parse "default.inc" */ + } else { + if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */ + error(100,incfname); /* cannot read from ... (fatal error) */ + } /* if */ + } /* if */ + preprocess(); /* fetch first line */ + parse(); /* process all input */ + sc_parsenum++; + } while (sc_reparse); + + /* second (or third) pass */ + sc_status=statWRITE; /* set, to enable warnings */ + state_conflict(&glbtab); + + /* write a report, if requested */ + #if !defined SC_LIGHT + if (sc_makereport) { + FILE *frep=stdout; + if (strlen(reportname)>0) + frep=fopen(reportname,"wb"); /* avoid translation of \n to \r\n in DOS/Windows */ + if (frep!=NULL) { + make_report(&glbtab,frep,get_sourcefile(0)); + if (strlen(reportname)>0) + fclose(frep); + } /* if */ + if (sc_documentation!=NULL) { + free(sc_documentation); + sc_documentation=NULL; + } /* if */ + } /* if */ + #endif + if (sc_listing) + goto cleanup; + + /* ??? for re-parsing the listing file instead of the original source + * file (and doing preprocessing twice): + * - close input file, close listing file + * - re-open listing file for reading (inpf) + * - open assembler file (outf) + */ + + /* reset "defined" flag of all functions and global variables */ + reduce_referrers(&glbtab); + delete_symbols(&glbtab,0,TRUE,FALSE); + #if !defined NO_DEFINE + delete_substtable(); + #endif + resetglobals(); + sc_ctrlchar=sc_ctrlchar_org; + sc_packstr=lcl_packstr; + sc_needsemicolon=lcl_needsemicolon; + sc_tabsize=lcl_tabsize; + errorset(sRESET,0); + /* reset the source file */ + inpf=inpf_org; + freading=TRUE; + pc_resetsrc(inpf,inpfmark); /* reset file position */ + fline=skipinput; /* reset line number */ + lexinit(); /* clear internal flags of lex() */ + sc_status=statWRITE; /* allow to write --this variable was reset by resetglobals() */ + writeleader(&glbtab); + insert_dbgfile(inpfname); + if (strlen(incfname)>0) { + if (strcmp(incfname,sDEF_PREFIX)==0) + plungefile(incfname,FALSE,TRUE); /* parse "default.inc" (again) */ + else + plungequalifiedfile(incfname); /* parse implicit include file (again) */ + } /* if */ + preprocess(); /* fetch first line */ + parse(); /* process all input */ + /* inpf is already closed when readline() attempts to pop of a file */ + writetrailer(); /* write remaining stuff */ + + entry=testsymbols(&glbtab,0,TRUE,FALSE); /* test for unused or undefined + * functions and variables */ + if (!entry) + error(13); /* no entry point (no public functions) */ + +cleanup: + if (inpf!=NULL) /* main source file is not closed, do it now */ + pc_closesrc(inpf); + /* write the binary file (the file is already open) */ + if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) { + assert(binf!=NULL); + pc_resetasm(outf); /* flush and loop back, for reading */ + #if !defined SC_LIGHT + hdrsize= + #endif + assemble(binf,outf); /* assembler file is now input */ + } /* if */ + if (outf!=NULL) { + pc_closeasm(outf,!(sc_asmfile || sc_listing)); + outf=NULL; + } /* if */ + if (binf!=NULL) { + pc_closebin(binf,errnum!=0); + binf=NULL; + } /* if */ + + #if !defined SC_LIGHT + if (errnum==0 && strlen(errfname)==0) { + int recursion; + long stacksize=max_stacksize(&glbtab,&recursion); + int flag_exceed=0; + if (pc_amxlimit>0) { + long totalsize=hdrsize+code_idx; + if (pc_amxram==0) + totalsize+=(glb_declared+pc_stksize)*sizeof(cell); + if (totalsize>=pc_amxlimit) + flag_exceed=1; + } /* if */ + if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram) + flag_exceed=1; + if ((sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) { + pc_printf("Header size: %8ld bytes\n", (long)hdrsize); + pc_printf("Code size: %8ld bytes\n", (long)code_idx); + pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell)); + pc_printf("Stack/heap size: %8ld bytes; ", (long)pc_stksize*sizeof(cell)); + pc_printf("estimated max. usage"); + if (recursion) + pc_printf(" is unknown, due to recursion\n"); + else if ((pc_memflags & suSLEEP_INSTR)!=0) + pc_printf(" is unknown, due to the \"sleep\" instruction\n"); + else + pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell)); + pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); + } /* if */ + if (flag_exceed) + error(106,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */ + } /* if */ + #endif + + if (inpfname!=NULL) { + if (get_sourcefile(1)!=NULL) + remove(inpfname); /* the "input file" was in fact a temporary file */ + free(inpfname); + } /* if */ + if (litq!=NULL) + free(litq); + phopt_cleanup(); + stgbuffer_cleanup(); + clearstk(); + assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols + * should already have been deleted */ + delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet + * done (i.e. on a fatal error) */ + delete_symbols(&glbtab,0,TRUE,TRUE); + delete_consttable(&tagname_tab); + delete_consttable(&libname_tab); + delete_consttable(&sc_automaton_tab); + delete_consttable(&sc_state_tab); + state_deletetable(); + delete_aliastable(); + delete_pathtable(); + delete_sourcefiletable(); + delete_dbgstringtable(); + #if !defined NO_DEFINE + delete_substtable(); + #endif + #if !defined SC_LIGHT + delete_docstringtable(); + if (sc_documentation!=NULL) + free(sc_documentation); + #endif + delete_autolisttable(); + if (errnum!=0) { + if (strlen(errfname)==0) + pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : ""); + retcode=1; + } else if (warnnum!=0){ + if (strlen(errfname)==0) + pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : ""); + retcode=0; /* use "0", so that MAKE and similar tools continue */ + } else { + retcode=jmpcode; + if (retcode==0 && verbosity>=2) + pc_printf("\nDone.\n"); + } /* if */ + #if defined __WIN32__ || defined _WIN32 || defined _Windows + if (IsWindow(hwndFinish)) + PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L); + #endif + #if defined FORTIFY + Fortify_ListAllMemory(); + #endif + return retcode; +} + +#if defined __cplusplus + extern "C" +#endif +int pc_addconstant(char *name,cell value,int tag) +{ + errorset(sFORCESET,0); /* make sure error engine is silenced */ + sc_status=statIDLE; + add_constant(name,value,sGLOBAL,tag); + return 1; +} + +#if defined __cplusplus + extern "C" +#endif +int pc_addtag(char *name) +{ + cell val; + constvalue *ptr; + int last,tag; + + if (name==NULL) { + /* no tagname was given, check for one */ + if (lex(&val,&name)!=tLABEL) { + lexpush(); + return 0; /* untagged */ + } /* if */ + } /* if */ + + assert(strchr(name,':')==NULL); /* colon should already have been stripped */ + last=0; + ptr=tagname_tab.next; + while (ptr!=NULL) { + tag=(int)(ptr->value & TAGMASK); + if (strcmp(name,ptr->name)==0) + return tag; /* tagname is known, return its sequence number */ + tag &= (int)~FIXEDTAG; + if (tag>last) + last=tag; + ptr=ptr->next; + } /* while */ + + /* tagname currently unknown, add it */ + tag=last+1; /* guaranteed not to exist already */ + if (isupper(*name)) + tag |= (int)FIXEDTAG; + append_constval(&tagname_tab,name,(cell)tag,0); + return tag; +} + +static void resetglobals(void) +{ + /* reset the subset of global variables that is modified by the first pass */ + curfunc=NULL; /* pointer to current function */ + lastst=0; /* last executed statement type */ + nestlevel=0; /* number of active (open) compound statements */ + rettype=0; /* the type that a "return" expression should have */ + litidx=0; /* index to literal table */ + stgidx=0; /* index to the staging buffer */ + sc_labnum=0; /* top value of (internal) labels */ + staging=0; /* true if staging output */ + declared=0; /* number of local cells declared */ + glb_declared=0; /* number of global cells declared */ + code_idx=0; /* number of bytes with generated code */ + ntv_funcid=0; /* incremental number of native function */ + curseg=0; /* 1 if currently parsing CODE, 2 if parsing DATA */ + freading=FALSE; /* no input file ready yet */ + fline=0; /* the line number in the current file */ + fnumber=0; /* the file number in the file table (debugging) */ + fcurrent=0; /* current file being processed (debugging) */ + sc_intest=FALSE; /* true if inside a test */ + sideeffect=0; /* true if an expression causes a side-effect */ + stmtindent=0; /* current indent of the statement */ + indent_nowarn=FALSE; /* do not skip warning "217 loose indentation" */ + sc_allowtags=TRUE; /* allow/detect tagnames */ + sc_status=statIDLE; + sc_allowproccall=FALSE; + pc_addlibtable=TRUE; /* by default, add a "library table" to the output file */ + sc_alignnext=FALSE; + pc_docexpr=FALSE; + pc_depricate=NULL; + sc_curstates=0; + pc_memflags=0; +} + +static void initglobals(void) +{ + resetglobals(); + + sc_asmfile=FALSE; /* do not create .ASM file */ + sc_listing=FALSE; /* do not create .LST file */ + skipinput=0; /* number of lines to skip from the first input file */ + sc_ctrlchar=CTRL_CHAR;/* the escape character */ + litmax=sDEF_LITMAX; /* current size of the literal table */ + errnum=0; /* number of errors */ + warnnum=0; /* number of warnings */ + optproccall=TRUE; /* support "procedure call" */ + verbosity=1; /* verbosity level, no copyright banner */ + sc_debug=sCHKBOUNDS; /* by default: bounds checking+assertions */ + pc_optimize=sOPTIMIZE_NOMACRO; + sc_packstr=FALSE; /* strings are unpacked by default */ + #if AMX_COMPACTMARGIN > 2 + sc_compress=TRUE; /* compress output bytecodes */ + #else + sc_compress=FALSE; + #endif + sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */ + sc_dataalign=sizeof(cell); + pc_stksize=sDEF_AMXSTACK;/* default stack size */ + pc_amxlimit=0; /* no limit on size of the abstract machine */ + pc_amxram=0; /* no limit on data size of the abstract machine */ + sc_tabsize=8; /* assume a TAB is 8 spaces */ + sc_rationaltag=0; /* assume no support for rational numbers */ + rational_digits=0; /* number of fractional digits */ + + outfname[0]='\0'; /* output file name */ + errfname[0]='\0'; /* error file name */ + inpf=NULL; /* file read from */ + inpfname=NULL; /* pointer to name of the file currently read from */ + outf=NULL; /* file written to */ + litq=NULL; /* the literal queue */ + glbtab.next=NULL; /* clear global variables/constants table */ + loctab.next=NULL; /* " local " / " " */ + tagname_tab.next=NULL;/* tagname table */ + libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ + + pline[0]='\0'; /* the line read from the input file */ + lptr=NULL; /* points to the current position in "pline" */ + curlibrary=NULL; /* current library */ + inpf_org=NULL; /* main source file */ + + wqptr=wq; /* initialize while queue pointer */ + +#if !defined SC_LIGHT + sc_documentation=NULL; + sc_makereport=FALSE; /* do not generate a cross-reference report */ +#endif +} + +static char *get_extension(char *filename) +{ + char *ptr; + + assert(filename!=NULL); + ptr=strrchr(filename,'.'); + if (ptr!=NULL) { + /* ignore extension on a directory or at the start of the filename */ + if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR) + ptr=NULL; + } /* if */ + return ptr; +} + +/* set_extension + * Set the default extension, or force an extension. To erase the + * extension of a filename, set "extension" to an empty string. + */ +SC_FUNC void set_extension(char *filename,char *extension,int force) +{ + char *ptr; + + assert(extension!=NULL && (*extension=='\0' || *extension=='.')); + assert(filename!=NULL); + ptr=get_extension(filename); + if (force && ptr!=NULL) + *ptr='\0'; /* set zero terminator at the position of the period */ + if (force || ptr==NULL) + strcat(filename,extension); +} + +static const char *option_value(const char *optptr) +{ + return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1; +} + +static int toggle_option(const char *optptr, int option) +{ + switch (*option_value(optptr)) { + case '\0': + option=!option; + break; + case '-': + option=FALSE; + break; + case '+': + option=TRUE; + break; + default: + about(); + } /* switch */ + return option; +} + +/* Parsing command line options is indirectly recursive: parseoptions() + * calls parserespf() to handle options in a a response file and + * parserespf() calls parseoptions() at its turn after having created + * an "option list" from the contents of the file. + */ +static void parserespf(char *filename,char *oname,char *ename,char *pname, + char *rname, char *codepage); + +static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname, + char *rname, char *codepage) +{ + char str[_MAX_PATH],*name; + const char *ptr; + int arg,i,isoption; + + for (arg=1; arg1) + verbosity=1; + break; + case 'C': + #if AMX_COMPACTMARGIN > 2 + sc_compress=toggle_option(ptr,sc_compress); + #else + about(); + #endif + break; + case 'c': + strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */ + break; +#if defined dos_setdrive + case 'D': /* set active directory */ + ptr=option_value(ptr); + if (ptr[1]==':') + dos_setdrive(toupper(*ptr)-'A'+1); /* set active drive */ + chdir(ptr); + break; +#endif + case 'd': + switch (*option_value(ptr)) { + case '0': + sc_debug=0; + break; + case '1': + sc_debug=sCHKBOUNDS; /* assertions and bounds checking */ + break; + case '2': + sc_debug=sCHKBOUNDS | sSYMBOLIC; /* also symbolic info */ + break; + case '3': + sc_debug=sCHKBOUNDS | sSYMBOLIC; + pc_optimize=sOPTIMIZE_NONE; + /* also avoid peephole optimization */ + break; + default: + about(); + } /* switch */ + break; + case 'e': + strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */ + break; +#if defined __WIN32__ || defined _WIN32 || defined _Windows + case 'H': + hwndFinish=(HWND)atoi(option_value(ptr)); + if (!IsWindow(hwndFinish)) + hwndFinish=(HWND)0; + break; +#endif + case 'i': + strlcpy(str,option_value(ptr),sizeof str); /* set name of include directory */ + i=strlen(str); + if (i>0) { + if (str[i-1]!=DIRSEP_CHAR) { + str[i]=DIRSEP_CHAR; + str[i+1]='\0'; + } /* if */ + insert_path(str); + } /* if */ + break; + case 'l': + if (*(ptr+1)!='\0') + about(); + sc_listing=TRUE; /* skip second pass & code generation */ + break; + case 'o': + strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */ + break; + case 'O': + pc_optimize=*option_value(ptr) - '0'; + if (pc_optimize=sOPTIMIZE_NUMBER) + about(); + break; + case 'p': + strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */ + break; +#if !defined SC_LIGHT + case 'r': + strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */ + sc_makereport=TRUE; + if (strlen(rname)>0) { + set_extension(rname,".xml",FALSE); + } else if ((name=get_sourcefile(0))!=NULL) { + assert(strlen(rname)==0); + assert(strlen(name)<_MAX_PATH); + if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL) + ptr++; /* strip path */ + else + ptr=name; + assert(strlen(ptr)<_MAX_PATH); + strcpy(rname,ptr); + set_extension(rname,".xml",TRUE); + } /* if */ + break; +#endif + case 'S': + i=atoi(option_value(ptr)); + if (i>32) + pc_stksize=(cell)i; /* stack size has minimum size */ + else + about(); + break; + case 's': + skipinput=atoi(option_value(ptr)); + break; + case 't': + sc_tabsize=atoi(option_value(ptr)); + break; + case 'v': + verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2; + if (sc_asmfile && verbosity>1) + verbosity=1; + break; + case 'w': + i=(int)strtol(option_value(ptr),(char **)&ptr,10); + if (*ptr=='-') + pc_enablewarning(i,0); + else if (*ptr=='+') + pc_enablewarning(i,1); + else if (*ptr=='\0') + pc_enablewarning(i,2); + break; + case 'X': + if (*(ptr+1)=='D') { + i=atoi(option_value(ptr+1)); + if (i>64) + pc_amxram=(cell)i; /* abstract machine data/stack has minimum size */ + else + about(); + } else { + i=atoi(option_value(ptr)); + if (i>64) + pc_amxlimit=(cell)i;/* abstract machine has minimum size */ + else + about(); + } /* if */ + break; + case '\\': /* use \ instead for escape characters */ + sc_ctrlchar='\\'; + break; + case '^': /* use ^ instead for escape characters */ + sc_ctrlchar='^'; + break; + case ';': + sc_needsemicolon=toggle_option(ptr,sc_needsemicolon); + break; + case '(': + optproccall=!toggle_option(ptr,!optproccall); + break; + default: /* wrong option */ + about(); + } /* switch */ + } else if (argv[arg][0]=='@') { + #if !defined SC_LIGHT + parserespf(&argv[arg][1],oname,ename,pname,rname,codepage); + #endif + } else if ((ptr=strchr(argv[arg],'='))!=NULL) { + i=(int)(ptr-argv[arg]); + if (i>sNAMEMAX) { + i=sNAMEMAX; + error(200,argv[arg],sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */ + } /* if */ + strlcpy(str,argv[arg],i+1); /* str holds symbol name */ + i=atoi(ptr+1); + add_constant(str,i,sGLOBAL,0); + } else { + strlcpy(str,argv[arg],sizeof(str)-2); /* -2 because default extension is ".p" */ + set_extension(str,".p",FALSE); + insert_sourcefile(str); + /* The output name is the first input name with a different extension, + * but it is stored in a different directory + */ + if (strlen(oname)==0) { + if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL) + ptr++; /* strip path */ + else + ptr=str; + assert(strlen(ptr)<_MAX_PATH); + strcpy(oname,ptr); + } /* if */ + set_extension(oname,".asm",TRUE); +#if !defined SC_LIGHT + if (sc_makereport && strlen(rname)==0) { + if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL) + ptr++; /* strip path */ + else + ptr=str; + assert(strlen(ptr)<_MAX_PATH); + strcpy(rname,ptr); + set_extension(rname,".xml",TRUE); + } /* if */ +#endif + } /* if */ + } /* for */ +} + +#if !defined SC_LIGHT +static void parserespf(char *filename,char *oname,char *ename,char *pname, + char *rname,char *codepage) +{ +#define MAX_OPTIONS 100 + FILE *fp; + char *string, *ptr, **argv; + int argc; + long size; + + if ((fp=fopen(filename,"r"))==NULL) + error(100,filename); /* error reading input file */ + /* load the complete file into memory */ + fseek(fp,0L,SEEK_END); + size=ftell(fp); + fseek(fp,0L,SEEK_SET); + assert(size [filename...] [options]\n\n"); + pc_printf("Options:\n"); + pc_printf(" -A alignment in bytes of the data segment and the stack\n"); + pc_printf(" -a output assembler code\n"); +#if AMX_COMPACTMARGIN > 2 + pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-'); +#endif + pc_printf(" -c codepage name or number; e.g. 1252 for Windows Latin-1\n"); +#if defined dos_setdrive + pc_printf(" -Dpath active directory path\n"); +#endif + pc_printf(" -d debugging level (default=-d%d)\n",sc_debug); + pc_printf(" 0 no symbolic information, no run-time checks\n"); + pc_printf(" 1 run-time checks, no symbolic information\n"); + pc_printf(" 2 full debug information and dynamic checking\n"); + pc_printf(" 3 same as -d2, but implies -O0\n"); + pc_printf(" -e set name of error file (quiet compile)\n"); +#if defined __WIN32__ || defined _WIN32 || defined _Windows + pc_printf(" -H window handle to send a notification message on finish\n"); +#endif + pc_printf(" -i path for include files\n"); + pc_printf(" -l create list file (preprocess only)\n"); + pc_printf(" -o set base name of (P-code) output file\n"); + pc_printf(" -O optimization level (default=-O%d)\n",pc_optimize); + pc_printf(" 0 no optimization\n"); + pc_printf(" 1 JIT-compatible optimizations only\n"); + pc_printf(" 2 full optimizations\n"); + pc_printf(" -p set name of \"prefix\" file\n"); +#if !defined SC_LIGHT + pc_printf(" -r[name] write cross reference report to console or to specified file\n"); +#endif + pc_printf(" -S stack/heap size in cells (default=%d)\n",(int)pc_stksize); + pc_printf(" -s skip lines from the input file\n"); + pc_printf(" -t TAB indent size (in character positions, default=%d)\n",sc_tabsize); + pc_printf(" -v verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity); + pc_printf(" -w disable a specific warning by its number\n"); + pc_printf(" -X abstract machine size limit in bytes\n"); + pc_printf(" -XD abstract machine data/stack size limit in bytes\n"); + pc_printf(" -\\ use '\\' for escape characters\n"); + pc_printf(" -^ use '^' for escape characters\n"); + pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-'); + pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+'); + pc_printf(" sym=val define constant \"sym\" with value \"val\"\n"); + pc_printf(" sym= define constant \"sym\" with value 0\n"); +#if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__ + pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n"); + pc_printf("equivalent.\n"); +#endif + pc_printf("\nOptions with a value may optionally separate the value from the option letter\n"); + pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n"); + pc_printf("and \"-d:0\" are all equivalent.\n"); + } /* if */ + longjmp(errbuf,3); /* user abort */ +} + +static void setconstants(void) +{ + int debug; + + assert(sc_status==statIDLE); + append_constval(&tagname_tab,"_",0,0);/* "untagged" */ + append_constval(&tagname_tab,"bool",1,0); + + add_constant("true",1,sGLOBAL,1); /* boolean flags */ + add_constant("false",0,sGLOBAL,1); + add_constant("EOS",0,sGLOBAL,0); /* End Of String, or '\0' */ + #if PAWN_CELL_SIZE==16 + add_constant("cellbits",16,sGLOBAL,0); + #if defined _I16_MAX + add_constant("cellmax",_I16_MAX,sGLOBAL,0); + add_constant("cellmin",_I16_MIN,sGLOBAL,0); + #else + add_constant("cellmax",SHRT_MAX,sGLOBAL,0); + add_constant("cellmin",SHRT_MIN,sGLOBAL,0); + #endif + #elif PAWN_CELL_SIZE==32 + add_constant("cellbits",32,sGLOBAL,0); + #if defined _I32_MAX + add_constant("cellmax",_I32_MAX,sGLOBAL,0); + add_constant("cellmin",_I32_MIN,sGLOBAL,0); + #else + add_constant("cellmax",LONG_MAX,sGLOBAL,0); + add_constant("cellmin",LONG_MIN,sGLOBAL,0); + #endif + #elif PAWN_CELL_SIZE==64 + #if !defined _I64_MIN + #define _I64_MIN (-9223372036854775807ULL - 1) + #define _I64_MAX 9223372036854775807ULL + #endif + add_constant("cellbits",64,sGLOBAL,0); + add_constant("cellmax",_I64_MAX,sGLOBAL,0); + add_constant("cellmin",_I64_MIN,sGLOBAL,0); + #else + #error Unsupported cell size + #endif + add_constant("charbits",sCHARBITS,sGLOBAL,0); + add_constant("charmin",0,sGLOBAL,0); + add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0); + add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0); + + add_constant("__Pawn",VERSION_INT,sGLOBAL,0); + + debug=0; + if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC)) + debug=2; + else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS) + debug=1; + add_constant("debug",debug,sGLOBAL,0); + + append_constval(&sc_automaton_tab,"",0,0); /* anonymous automaton */ +} + +static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst) +{ + int tok,err; + cell val; + char *str; + + assert(fconst!=NULL); + assert(fstock!=NULL); + assert(fstatic!=NULL); + assert(fpublic!=NULL); + *fconst=FALSE; + *fstock=FALSE; + *fstatic=FALSE; + *fpublic=FALSE; + switch (initialtok) { + case tCONST: + *fconst=TRUE; + break; + case tSTOCK: + *fstock=TRUE; + break; + case tSTATIC: + *fstatic=TRUE; + break; + case tPUBLIC: + *fpublic=TRUE; + break; + } /* switch */ + + err=0; + do { + tok=lex(&val,&str); /* read in (new) token */ + switch (tok) { + case tCONST: + if (*fconst) + err=42; /* invalid combination of class specifiers */ + *fconst=TRUE; + break; + case tSTOCK: + if (*fstock) + err=42; /* invalid combination of class specifiers */ + *fstock=TRUE; + break; + case tSTATIC: + if (*fstatic) + err=42; /* invalid combination of class specifiers */ + *fstatic=TRUE; + break; + case tPUBLIC: + if (*fpublic) + err=42; /* invalid combination of class specifiers */ + *fpublic=TRUE; + break; + default: + lexpush(); + tok=0; /* force break out of loop */ + } /* switch */ + } while (tok && err==0); + + /* extra checks */ + if (*fstatic && *fpublic) { + err=42; /* invalid combination of class specifiers */ + *fstatic=*fpublic=FALSE; + } /* if */ + + if (err) + error(err); + return err==0; +} + +/* parse - process all input text + * + * At this level, only static declarations and function definitions are legal. + */ +static void parse(void) +{ + int tok,fconst,fstock,fstatic,fpublic; + cell val; + char *str; + + while (freading){ + /* first try whether a declaration possibly is native or public */ + tok=lex(&val,&str); /* read in (new) token */ + switch (tok) { + case 0: + /* ignore zero's */ + break; + case tNEW: + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) + declglb(NULL,0,fpublic,fstatic,fstock,fconst); + break; + case tSTATIC: + /* This can be a static function or a static global variable; we know + * which of the two as soon as we have parsed up to the point where an + * opening paranthesis of a function would be expected. To back out after + * deciding it was a declaration of a static variable after all, we have + * to store the symbol name and tag. + */ + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + assert(!fpublic); + declfuncvar(fpublic,fstatic,fstock,fconst); + } /* if */ + break; + case tCONST: + decl_const(sGLOBAL); + break; + case tENUM: + decl_enum(sGLOBAL); + break; + case tPUBLIC: + /* This can be a public function or a public variable; see the comment + * above (for static functions/variables) for details. + */ + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + assert(!fstatic); + declfuncvar(fpublic,fstatic,fstock,fconst); + } /* if */ + break; + case tSTOCK: + /* This can be a stock function or a stock *global*) variable; see the + * comment above (for static functions/variables) for details. + */ + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + assert(fstock); + declfuncvar(fpublic,fstatic,fstock,fconst); + } /* if */ + break; + case tLABEL: + case tSYMBOL: + case tOPERATOR: + lexpush(); + if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) { + error(10); /* illegal function or declaration */ + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop the literal queue too */ + } /* if */ + break; + case tNATIVE: + funcstub(TRUE); /* create a dummy function */ + break; + case tFORWARD: + funcstub(FALSE); + break; + case '}': + error(54); /* unmatched closing brace */ + break; + case '{': + error(55); /* start of function body without function header */ + break; + default: + if (freading) { + error(10); /* illegal function or declaration */ + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop any literal arrays (strings) */ + } /* if */ + } /* switch */ + } /* while */ +} + +/* dumplits + * + * Dump the literal pool (strings etc.) + * + * Global references: litidx (referred to only) + */ +static void dumplits(void) +{ + int j,k; + + k=0; + while (k=litidx) + stgwrite("\n"); /* force a newline after 10 dumps */ + /* Note: stgwrite() buffers a line until it is complete. It recognizes + * the end of line as a sequence of "\n\0", so something like "\n\t" + * so should not be passed to stgwrite(). + */ + } /* while */ + } /* while */ +} + +/* dumpzero + * + * Dump zero's for default initial values + */ +static void dumpzero(int count) +{ + int i; + + if (count<=0) + return; + assert(curseg==2); + defstorage(); + i=0; + while (count-- > 0) { + outval(0, FALSE); + i=(i+1) % 16; + stgwrite((i==0 || count==0) ? "\n" : " "); + if (i==0 && count>0) + defstorage(); + } /* while */ +} + +static void aligndata(int numbytes) +{ + assert(numbytes % sizeof(cell) == 0); /* alignment must be a multiple of + * the cell size */ + assert(numbytes!=0); + + if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) { + while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) + litadd(0); + } /* if */ + +} + +#if !defined SC_LIGHT +/* sc_attachdocumentation() + * appends documentation comments to the passed-in symbol, or to a global + * string if "sym" is NULL. + */ +void sc_attachdocumentation(symbol *sym) +{ + int line; + size_t length; + char *str,*doc; + + if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) { + /* just clear the entire table */ + delete_docstringtable(); + return; + } /* if */ + /* in the case of state functions, multiple documentation sections may + * appear; we should concatenate these + * (with forward declarations, this is also already the case, so the assertion + * below is invalid) + */ + // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL); + + /* first check the size */ + length=0; + for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) { + if (length>0) + length++; /* count 1 extra for a separating space */ + length+=strlen(str); + } /* for */ + if (sym==NULL && sc_documentation!=NULL) { + length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "

" */ + assert(length>strlen(sc_documentation)); + } /* if */ + + if (length>0) { + /* allocate memory for the documentation */ + if (sym!=NULL && sym->documentation!=NULL) + length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "

" */ + doc=(char*)malloc((length+1)*sizeof(char)); + if (doc!=NULL) { + /* initialize string or concatenate */ + if (sym==NULL && sc_documentation!=NULL) { + strcpy(doc,sc_documentation); + strcat(doc,"

"); + } else if (sym!=NULL && sym->documentation!=NULL) { + strcpy(doc,sym->documentation); + strcat(doc,"

"); + free(sym->documentation); + sym->documentation=NULL; + } else { + doc[0]='\0'; + } /* if */ + /* collect all documentation */ + while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) { + if (doc[0]!='\0') + strcat(doc," "); + strcat(doc,str); + delete_docstring(0); + } /* while */ + if (str!=NULL) { + /* also delete the separator */ + assert(*str==sDOCSEP); + delete_docstring(0); + } /* if */ + if (sym!=NULL) { + assert(sym->documentation==NULL); + sym->documentation=doc; + } else { + if (sc_documentation!=NULL) + free(sc_documentation); + sc_documentation=doc; + } /* if */ + } /* if */ + } else { + /* delete an empty separator, if present */ + if ((str=get_docstring(0))!=NULL && *str==sDOCSEP) + delete_docstring(0); + } /* if */ +} + +static void insert_docstring_separator(void) +{ + char sep[2]={sDOCSEP,'\0'}; + insert_docstring(sep); +} +#else + #define sc_attachdocumentation(s) (void)(s) + #define insert_docstring_separator() +#endif + +static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst) +{ + char name[sNAMEMAX+11]; + int tok,tag; + char *str; + cell val; + int invalidfunc; + + tag=pc_addtag(NULL); + tok=lex(&val,&str); + /* if we arrived here, this may not be a declaration of a native function + * or variable + */ + if (tok==tNATIVE) { + error(42); /* invalid combination of class specifiers */ + return; + } /* if */ + + if (tok!=tSYMBOL && tok!=tOPERATOR) { + lexpush(); + needtoken(tSYMBOL); + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop the literal queue too */ + return; + } /* if */ + if (tok==tOPERATOR) { + lexpush(); /* push "operator" keyword back (for later analysis) */ + if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) { + error(10); /* illegal function or declaration */ + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop the literal queue too */ + } /* if */ + } else { + /* so tok is tSYMBOL */ + assert(strlen(str)<=sNAMEMAX); + strcpy(name,str); + /* only variables can be "const" or both "public" and "stock" */ + invalidfunc= fconst || (fpublic && fstock); + if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) { + /* if not a function, try a global variable */ + declglb(name,tag,fpublic,fstatic,fstock,fconst); + } /* if */ + } /* if */ +} + +/* declglb - declare global symbols + * + * Declare a static (global) variable. Global variables are stored in + * the DATA segment. + * + * global references: glb_declared (altered) + */ +static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst) +{ + int ident,tag,ispublic; + int idxtag[sDIMEN_MAX]; + char name[sNAMEMAX+1]; + cell val,size,cidx; + ucell address; + int glb_incr; + char *str; + int dim[sDIMEN_MAX]; + int numdim; + short filenum; + symbol *sym; + constvalue *enumroot; + #if !defined NDEBUG + cell glbdecl=0; + #endif + + assert(!fpublic || !fstatic); /* may not both be set */ + insert_docstring_separator(); /* see comment in newfunc() */ + filenum=fcurrent; /* save file number at the start of the declaration */ + do { + size=1; /* single size (no array) */ + numdim=0; /* no dimensions */ + ident=iVARIABLE; + if (firstname!=NULL) { + assert(strlen(firstname)<=sNAMEMAX); + strcpy(name,firstname); /* save symbol name */ + tag=firsttag; + firstname=NULL; + } else { + tag=pc_addtag(NULL); + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ + error(20,str); /* invalid symbol name */ + assert(strlen(str)<=sNAMEMAX); + strcpy(name,str); /* save symbol name */ + } /* if */ + ispublic=fpublic; + if (name[0]==PUBLIC_CHAR) { + ispublic=TRUE; /* implicitly public variable */ + assert(!fstatic); + } /* if */ + while (matchtoken('[')) { + ident=iARRAY; + if (numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return; + } /* if */ + size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + if (ispublic) + error(56,name); /* arrays cannot be public */ + dim[numdim++]=(int)size; + } /* while */ + assert(sc_curstates==0); + sc_curstates=getstates(name); + if (sc_curstates<0) { + error(85,name); /* empty state list on declaration */ + sc_curstates=0; + } else if (sc_curstates>0 && ispublic) { + error(88,name); /* public variables may not have states */ + sc_curstates=0; + } /* if */ + sym=findconst(name,NULL); + if (sym==NULL) { + sym=findglb(name,sSTATEVAR); + /* if a global variable without states is found and this declaration has + * states, the declaration is okay + */ + if (sym!=NULL && sym->states==NULL && sc_curstates>0) + sym=NULL; /* set to NULL, we found the global variable */ + if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL) + error(233,name); /* state variable shadows a global variable */ + } /* if */ + /* we have either: + * a) not found a matching variable (or rejected it, because it was a shadow) + * b) found a global variable and we were looking for that global variable + * c) found a state variable in the automaton that we were looking for + */ + assert(sym==NULL + || sym->states==NULL && sc_curstates==0 + || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates); + /* a state variable may only have a single id in its list (so either this + * variable has no states, or it has a single list) + */ + assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL); + /* it is okay for the (global) variable to exist, as long as it belongs to + * a different automaton + */ + if (sym!=NULL && (sym->usage & uDEFINE)!=0) + error(21,name); /* symbol already defined */ + /* if this variable is never used (which can be detected only in the + * second stage), shut off code generation + */ + cidx=0; /* only to avoid a compiler warning */ + if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) { + sc_status=statSKIP; + cidx=code_idx; + #if !defined NDEBUG + glbdecl=glb_declared; + #endif + } /* if */ + begdseg(); /* real (initialized) data in data segment */ + assert(litidx==0); /* literal queue should be empty */ + if (sc_alignnext) { + litidx=0; + aligndata(sc_dataalign); + dumplits(); /* dump the literal queue */ + sc_alignnext=FALSE; + litidx=0; /* global initial data is dumped, so restart at zero */ + } /* if */ + assert(litidx==0); /* literal queue should be empty (again) */ + initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */ + assert(size>=litidx); + if (numdim==1) + dim[0]=(int)size; + /* before dumping the initial values (or zeros) check whether this variable + * overlaps another + */ + if (sc_curstates>0) { + unsigned char *map; + + if (litidx!=0) + error(89,name); /* state variables may not be initialized */ + /* find an appropriate address for the state variable */ + /* assume that it cannot be found */ + address=sizeof(cell)*glb_declared; + glb_incr=(int)size; + /* use a memory map in which every cell occupies one bit */ + if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) { + int fsa=state_getfsa(sc_curstates); + symbol *sweep; + cell sweepsize,addr; + memset(map,0,(glb_declared+7)/8); + assert(fsa>=0); + /* fill in all variables belonging to this automaton */ + for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) { + if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym) + continue; /* hierarchical type, or no states, or same as this variable */ + if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY) + continue; /* a function or a constant */ + if ((sweep->usage & uDEFINE)==0) + continue; /* undefined variable, ignore */ + if (fsa!=state_getfsa(sweep->states->next->index)) + continue; /* wrong automaton */ + /* when arrived here, this is a global variable, with states and + * belonging to the same automaton as the variable we are declaring + */ + sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); + assert(sweep->addr % sizeof(cell) == 0); + addr=sweep->addr/sizeof(cell); + /* mark this address range */ + while (sweepsize-->0) { + map[addr/8] |= (unsigned char)(1 << (addr % 8)); + addr++; + } /* while */ + } /* for */ + /* go over it again, clearing any ranges that have conflicts */ + for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) { + if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym) + continue; /* hierarchical type, or no states, or same as this variable */ + if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY) + continue; /* a function or a constant */ + if ((sweep->usage & uDEFINE)==0) + continue; /* undefined variable, ignore */ + if (fsa!=state_getfsa(sweep->states->next->index)) + continue; /* wrong automaton */ + /* when arrived here, this is a global variable, with states and + * belonging to the same automaton as the variable we are declaring + */ + /* if the lists of states of the existing variable and the new + * variable have a non-empty intersection, this is not a suitable + * overlap point -> wipe the address range + */ + if (state_conflict_id(sc_curstates,sweep->states->next->index)) { + sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); + assert(sweep->addr % sizeof(cell) == 0); + addr=sweep->addr/sizeof(cell); + /* mark this address range */ + while (sweepsize-->0) { + map[addr/8] &= (unsigned char)(~(1 << (addr % 8))); + addr++; + } /* while */ + } /* if */ + } /* for */ + /* now walk through the map and find a starting point that is big enough */ + sweepsize=0; + for (addr=0; addr=size) + break; /* fitting range found, no need to search further */ + } /* for */ + if (sweepsize-addr>=size) + break; /* fitting range found, no need to search further */ + addr=sweepsize; + } /* for */ + free(map); + if (sweepsize-addr>=size) { + address=sizeof(cell)*addr; /* fitting range found, set it */ + glb_incr=0; + } /* if */ + } /* if */ + } else { + address=sizeof(cell)*glb_declared; + glb_incr=(int)size; + } /* if */ + if (address==sizeof(cell)*glb_declared) { + dumplits(); /* dump the literal queue */ + dumpzero((int)size-litidx); + } /* if */ + litidx=0; + if (sym==NULL) { /* define only if not yet defined */ + sym=addvariable(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag); + if (sc_curstates>0) + attachstatelist(sym,sc_curstates); + } else { /* if declared but not yet defined, adjust the variable's address */ + assert(sym->states==NULL && sc_curstates==0 + || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL); + sym->addr=address; + sym->codeaddr=code_idx; + sym->usage|=uDEFINE; + } /* if */ + assert(sym!=NULL); + sc_curstates=0; + if (ispublic) + sym->usage|=uPUBLIC; + if (fconst) + sym->usage|=uCONST; + if (fstock) + sym->usage|=uSTOCK; + if (fstatic) + sym->fnumber=filenum; + sc_attachdocumentation(sym);/* attach any documenation to the variable */ + if (sc_status==statSKIP) { + sc_status=statWRITE; + code_idx=cidx; + assert(glb_declared==glbdecl); + } else { + glb_declared+=glb_incr; /* add total number of cells (if added to the end) */ + } /* if */ + } while (matchtoken(',')); /* enddo */ /* more? */ + needtoken(tTERM); /* if not comma, must be semicolumn */ +} + +/* declloc - declare local symbols + * + * Declare local (automatic) variables. Since these variables are relative + * to the STACK, there is no switch to the DATA segment. These variables + * cannot be initialized either. + * + * global references: declared (altered) + * funcstatus (referred to only) + */ +static int declloc(int fstatic) +{ + int ident,tag; + int idxtag[sDIMEN_MAX]; + char name[sNAMEMAX+1]; + symbol *sym; + constvalue *enumroot; + cell val,size; + char *str; + value lval = {0}; + int cur_lit=0; + int dim[sDIMEN_MAX]; + int numdim; + int fconst; + int staging_start; + + fconst=matchtoken(tCONST); + do { + ident=iVARIABLE; + size=1; + numdim=0; /* no dimensions */ + tag=pc_addtag(NULL); + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ + error(20,str); /* invalid symbol name */ + assert(strlen(str)<=sNAMEMAX); + strcpy(name,str); /* save symbol name */ + if (name[0]==PUBLIC_CHAR) + error(56,name); /* local variables cannot be public */ + /* Note: block locals may be named identical to locals at higher + * compound blocks (as with standard C); so we must check (and add) + * the "nesting level" of local variables to verify the + * multi-definition of symbols. + */ + if ((sym=findloc(name))!=NULL && sym->compound==nestlevel) + error(21,name); /* symbol already defined */ + /* Although valid, a local variable whose name is equal to that + * of a global variable or to that of a local variable at a lower + * level might indicate a bug. + */ + if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL) + error(219,name); /* variable shadows another symbol */ + while (matchtoken('[')){ + ident=iARRAY; + if (numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return ident; + } /* if */ + size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + dim[numdim++]=(int)size; + } /* while */ + if (getstates(name)) + error(88,name); /* local variables may not have states */ + if (ident==iARRAY || fstatic) { + if (sc_alignnext) { + aligndata(sc_dataalign); + sc_alignnext=FALSE; + } /* if */ + cur_lit=litidx; /* save current index in the literal table */ + initials(ident,tag,&size,dim,numdim,enumroot); + if (size==0) + return ident; /* error message already given */ + if (numdim==1) + dim[0]=(int)size; + } /* if */ + /* reserve memory (on the stack) for the variable */ + if (fstatic) { + /* write zeros for uninitialized fields */ + while (litidxusage & uNATIVE)==0); + if (curfunc->x.stacksizex.stacksize=declared+1; /* +1 for PROC opcode */ + } /* if */ + /* now that we have reserved memory for the variable, we can proceed + * to initialize it */ + assert(sym!=NULL); /* we declared it, it must be there */ + sym->compound=nestlevel; /* for multiple declaration/shadowing check */ + if (fconst) + sym->usage|=uCONST; + if (!fstatic) { /* static variables already initialized */ + if (ident==iVARIABLE) { + /* simple variable, also supports initialization */ + int ctag = tag; /* set to "tag" by default */ + int explicit_init=FALSE;/* is the variable explicitly initialized? */ + if (matchtoken('=')) { + doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE); + explicit_init=TRUE; + } else { + ldconst(0,sPRI); /* uninitialized variable, set to zero */ + } /* if */ + /* now try to save the value (still in PRI) in the variable */ + lval.sym=sym; + lval.ident=iVARIABLE; + lval.constval=0; + lval.tag=tag; + check_userop(NULL,ctag,lval.tag,2,NULL,&ctag); + store(&lval); + markexpr(sEXPR,NULL,0); /* full expression ends after the store */ + assert(staging); /* end staging phase (optimize expression) */ + stgout(staging_start); + stgset(FALSE); + if (!matchtag(tag,ctag,TRUE)) + error(213); /* tag mismatch */ + /* if the variable was not explicitly initialized, reset the + * "uWRITTEN" flag that store() set */ + if (!explicit_init) + sym->usage &= ~uWRITTEN; + } else { + /* an array */ + assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax); + assert(size>0 && size>=sym->dim.array.length); + assert(numdim>1 || size==sym->dim.array.length); + /* final literal values that are zero make no sense to put in the literal + * pool, because values get zero-initialized anyway; we check for this, + * because users often explicitly initialize strings to "" + */ + while (litidx>cur_lit && litq[litidx-1]==0) + litidx--; + /* if the array is not completely filled, set all values to zero first */ + if (litidx-cur_lit=0 && cur<=numdim); + if (cur==numdim) + return 0; + subsize=calc_arraysize(dim,numdim,cur+1); + newsize=dim[cur]+dim[cur]*subsize; + if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize<(ucell)subsize + || newsize*sizeof(cell)>=CELL_MAX) + return CELL_MAX; + return newsize; +} + +static cell adjust_indirectiontables(int dim[],int numdim,int cur,cell increment, + int startlit,constvalue *lastdim,int *skipdim) +{ +static int base; + int d; + cell accum; + + assert(cur>=0 && cur=0); + assert(cur>0 && startlit==-1 || startlit>=0 && startlit<=litidx); + if (cur==0) + base=startlit; + if (cur==numdim-1) + return 0; + /* 2 or more dimensions left, fill in an indirection vector */ + assert(dim[cur]>0); + if (dim[cur+1]>0) { + for (d=0; dnext; d<*skipdim; d++,ld=ld->next) { + assert(ld!=NULL); + } /* for */ + for (d=0; dname,NULL,16)==d); + litq[base++]=(dim[cur]+accum+increment) * sizeof(cell); + accum+=ld->value-1; + *skipdim+=1; + ld=ld->next; + } /* for */ + } /* if */ + /* create the indirection tables for the lower level */ + if (cur+2=dim[cur]) { + error(18); /* initialization data exceeds array size */ + break; + } /* if */ + if (cur+20) { + if (idxcounteddim[cur]) + error(18); /* initialization data exceeds declared size */ + } /* if */ + counteddim[cur]=idx; + + return totalsize+dim[cur]; /* size of sub-arrays + indirection vector */ +} + +/* initvector + * Initialize a single dimensional array + */ +static cell initvector(int ident,int tag,cell size,int fillzero, + constvalue *enumroot,int *errorfound) +{ + cell prev1=0,prev2=0; + int ellips=FALSE; + int curlit=litidx; + int rtag,ctag; + + assert(ident==iARRAY || ident==iREFARRAY); + if (matchtoken('{')) { + constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL; + do { + int fieldlit=litidx; + int matchbrace,i; + if (matchtoken('}')) { /* to allow for trailing ',' after the initialization */ + lexpush(); + break; + } /* if */ + if ((ellips=matchtoken(tELLIPS))!=0) + break; + /* for enumeration fields, allow another level of braces ("{...}") */ + matchbrace=0; /* preset */ + ellips=0; + if (enumfield!=NULL) + matchbrace=matchtoken('{'); + for ( ;; ) { + prev2=prev1; + prev1=init(ident,&ctag,errorfound); + if (!matchbrace) + break; + if ((ellips=matchtoken(tELLIPS))!=0) + break; + if (!matchtoken(',')) { + needtoken('}'); + break; + } /* for */ + } /* for */ + /* if this array is based on an enumeration, fill the "field" up with + * zeros, and toggle the tag + */ + if (enumroot!=NULL && enumfield==NULL) + error(227); /* more initiallers than enum fields */ + rtag=tag; /* preset, may be overridden by enum field tag */ + if (enumfield!=NULL) { + cell step; + int cmptag=enumfield->index; + symbol *symfield=findconst(enumfield->name,&cmptag); + if (cmptag>1) + error(91,enumfield->name); /* ambiguous constant, needs tag override */ + assert(symfield!=NULL); + assert(fieldlitsymfield->dim.array.length) + error(228); /* length of initialler exceeds size of the enum field */ + if (ellips) { + step=prev1-prev2; + } else { + step=0; + prev1=0; + } /* if */ + for (i=litidx-fieldlit; idim.array.length; i++) { + prev1+=step; + litadd(prev1); + } /* for */ + rtag=symfield->x.tags.index; /* set the expected tag to the index tag */ + enumfield=enumfield->next; + } /* if */ + if (!matchtag(rtag,ctag,TRUE)) + error(213); /* tag mismatch */ + } while (matchtoken(',')); /* do */ + needtoken('}'); + } else { + init(ident,&ctag,errorfound); + if (!matchtag(tag,ctag,TRUE)) + error(213); /* tagname mismatch */ + } /* if */ + /* fill up the literal queue with a series */ + if (ellips) { + cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2; + if (size==0 || (litidx-curlit)==0) + error(41); /* invalid ellipsis, array size unknown */ + else if ((litidx-curlit)==(int)size) + error(18); /* initialisation data exceeds declared size */ + while ((litidx-curlit)<(int)size) { + prev1+=step; + litadd(prev1); + } /* while */ + } /* if */ + if (fillzero && size>0) { + while ((litidx-curlit)<(int)size) + litadd(0); + } /* if */ + if (size==0) { + size=litidx-curlit; /* number of elements defined */ + } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */ + error(18); /* initialisation data exceeds declared size */ + litidx=(int)size+curlit; /* avoid overflow in memory moves */ + } /* if */ + return size; +} + +/* init + * + * Evaluate one initializer. + */ +static cell init(int ident,int *tag,int *errorfound) +{ + cell i = 0; + + if (matchtoken(tSTRING)){ + /* lex() automatically stores strings in the literal table (and + * increases "litidx") */ + if (ident==iVARIABLE) { + error(6); /* must be assigned to an array */ + litidx=1; /* reset literal queue */ + } /* if */ + *tag=0; + } else if (constexpr(&i,tag,NULL)){ + litadd(i); /* store expression result in literal table */ + } else { + if (errorfound!=NULL) + *errorfound=TRUE; + } /* if */ + return i; +} + +/* needsub + * + * Get required array size + */ +static cell needsub(int *tag,constvalue **enumroot) +{ + cell val; + symbol *sym; + + assert(tag!=NULL); + *tag=0; + if (enumroot!=NULL) + *enumroot=NULL; /* preset */ + if (matchtoken(']')) /* we have already seen "[" */ + return 0; /* zero size (like "char msg[]") */ + + constexpr(&val,tag,&sym); /* get value (must be constant expression) */ + if (val<0) { + error(9); /* negative array size is invalid; assumed zero */ + val=0; + } /* if */ + needtoken(']'); + + if (enumroot!=NULL) { + /* get the field list for an enumeration */ + assert(*enumroot==NULL);/* should have been preset */ + assert(sym==NULL || sym->ident==iCONSTEXPR); + if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) { + assert(sym->dim.enumlist!=NULL); + *enumroot=sym->dim.enumlist; + } /* if */ + } /* if */ + + return val; /* return array size */ +} + +/* decl_const - declare a single constant + * + */ +static void decl_const(int vclass) +{ + char constname[sNAMEMAX+1]; + cell val; + char *str; + int tag,exprtag; + int symbolline; + symbol *sym; + + insert_docstring_separator(); /* see comment in newfunc() */ + tag=pc_addtag(NULL); + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ + error(20,str); /* invalid symbol name */ + symbolline=fline; /* save line where symbol was found */ + strcpy(constname,str); /* save symbol name */ + needtoken('='); + constexpr(&val,&exprtag,NULL);/* get value */ + needtoken(tTERM); + /* add_constant() checks for duplicate definitions */ + if (!matchtag(tag,exprtag,FALSE)) { + /* temporarily reset the line number to where the symbol was defined */ + int orgfline=fline; + fline=symbolline; + error(213); /* tagname mismatch */ + fline=orgfline; + } /* if */ + sym=add_constant(constname,val,vclass,tag); + if (sym!=NULL) + sc_attachdocumentation(sym);/* attach any documenation to the function */ +} + +/* decl_enum - declare enumerated constants + * + */ +static void decl_enum(int vclass) +{ + char enumname[sNAMEMAX+1],constname[sNAMEMAX+1]; + cell val,value,size; + char *str; + int tag,explicittag; + cell increment,multiplier; + constvalue *enumroot; + symbol *enumsym; + + /* get an explicit tag, if any (we need to remember whether an explicit + * tag was passed, even if that explicit tag was "_:", so we cannot call + * pc_addtag() here + */ + if (lex(&val,&str)==tLABEL) { + tag=pc_addtag(str); + explicittag=TRUE; + } else { + lexpush(); + tag=0; + explicittag=FALSE; + } /* if */ + + /* get optional enum name (also serves as a tag if no explicit tag was set) */ + if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */ + strcpy(enumname,str); /* save enum name (last constant) */ + if (!explicittag) + tag=pc_addtag(enumname); + } else { + lexpush(); /* analyze again */ + enumname[0]='\0'; + } /* if */ + + /* get increment and multiplier */ + increment=1; + multiplier=1; + if (matchtoken('(')) { + if (matchtoken(taADD)) { + constexpr(&increment,NULL,NULL); + } else if (matchtoken(taMULT)) { + constexpr(&multiplier,NULL,NULL); + } else if (matchtoken(taSHL)) { + constexpr(&val,NULL,NULL); + while (val-->0) + multiplier*=2; + } /* if */ + needtoken(')'); + } /* if */ + + if (strlen(enumname)>0) { + /* already create the root symbol, so the fields can have it as their "parent" */ + enumsym=add_constant(enumname,0,vclass,tag); + if (enumsym!=NULL) + enumsym->usage |= uENUMROOT; + /* start a new list for the element names */ + if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(103); /* insufficient memory (fatal error) */ + memset(enumroot,0,sizeof(constvalue)); + } else { + enumsym=NULL; + enumroot=NULL; + } /* if */ + + needtoken('{'); + /* go through all constants */ + value=0; /* default starting value */ + do { + int idxtag,fieldtag; + symbol *sym; + if (matchtoken('}')) { /* quick exit if '}' follows ',' */ + lexpush(); + break; + } /* if */ + idxtag=pc_addtag(NULL); /* optional explicit item tag */ + if (needtoken(tSYMBOL)) { /* read in (new) token */ + tokeninfo(&val,&str); /* get the information */ + strcpy(constname,str); /* save symbol name */ + } else { + constname[0]='\0'; + } /* if */ + size=increment; /* default increment of 'val' */ + fieldtag=0; /* default field tag */ + if (matchtoken('[')) { + constexpr(&size,&fieldtag,NULL); /* get size */ + needtoken(']'); + } /* if */ + if (matchtoken('=')) + constexpr(&value,NULL,NULL); /* get value */ + /* add_constant() checks whether a variable (global or local) or + * a constant with the same name already exists + */ + sym=add_constant(constname,value,vclass,tag); + if (sym==NULL) + continue; /* error message already given */ + /* set the item tag and the item size, for use in indexing arrays */ + sym->x.tags.index=idxtag; + sym->x.tags.field=fieldtag; + sym->dim.array.length=size; + sym->dim.array.level=0; + sym->parent=enumsym; + /* add the constant to a separate list as well */ + if (enumroot!=NULL) { + sym->usage |= uENUMFIELD; + append_constval(enumroot,constname,value,tag); + } /* if */ + if (multiplier==1) + value+=size; + else + value*=size*multiplier; + } while (matchtoken(',')); + needtoken('}'); /* terminates the constant list */ + matchtoken(';'); /* eat an optional ; */ + + /* set the enum name to the "next" value (typically the last value plus one) */ + if (enumsym!=NULL) { + assert((enumsym->usage & uENUMROOT)!=0); + enumsym->addr=value; + /* assign the constant list */ + assert(enumroot!=NULL); + enumsym->dim.enumlist=enumroot; + sc_attachdocumentation(enumsym); /* attach any documenation to the enumeration */ + } /* if */ +} + +static int getstates(const char *funcname) +{ + char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1]; + cell val; + char *str; + constvalue *automaton; + constvalue *state; + int fsa,islabel; + int *list; + int count,listsize,state_id; + + if (!matchtoken('<')) + return 0; + if (matchtoken('>')) + return -1; /* special construct: all other states (fall-back) */ + + count=0; + listsize=0; + list=NULL; + fsa=-1; + + do { + if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL)) + break; + tokeninfo(&val,&str); + assert(strlen(str)=0 && automaton->index!=fsa) + error(83,funcname); /* multiple automatons for a single function/variable */ + fsa=automaton->index; + } /* if */ + state=state_add(statename,fsa); + /* add this state to the state combination list (it will be attached to the + * automaton later) */ + state_buildlist(&list,&listsize,&count,(int)state->value); + } while (matchtoken(',')); + needtoken('>'); + + if (count>0) { + assert(automaton!=NULL); + assert(fsa>=0); + state_id=state_addlist(list,count,fsa); + assert(state_id>0); + } else { + /* error is already given */ + state_id=0; + } /* if */ + if (list!=NULL) + free(list); + + return state_id; +} + +static void attachstatelist(symbol *sym, int state_id) +{ + assert(sym!=NULL); + if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0)) + error(21,sym->name); /* function already defined, either without states or the current definition has no states */ + + if (state_id!=0) { + /* add the state list id */ + constvalue *stateptr; + if (sym->states==NULL) { + if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(103); /* insufficient memory (fatal error) */ + memset(sym->states,0,sizeof(constvalue)); + } /* if */ + /* see whether the id already exists (add new state only if it does not + * yet exist + */ + assert(sym->states!=NULL); + for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next) + /* nothing */; + assert(state_id<=SHRT_MAX); + if (stateptr==NULL) + append_constval(sym->states,"",code_idx,(short)state_id); + else if (stateptr->value==0) + stateptr->value=code_idx; + else + error(84,sym->name); + /* also check for another conflicting situation: a fallback function + * without any states + */ + if (state_id==-1 && sc_status!=statFIRST) { + /* in the second round, all states should have been accumulated */ + assert(sym->states!=NULL); + for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next) + /* nothing */; + if (stateptr==NULL) + error(85,sym->name); /* no states are defined for this function */ + } /* if */ + } /* if */ +} + +/* + * Finds a function in the global symbol table or creates a new entry. + * It does some basic processing and error checking. + */ +SC_FUNC symbol *fetchfunc(char *name,int tag) +{ + symbol *sym; + + if ((sym=findglb(name,sGLOBAL))!=0) { /* already in symbol table? */ + if (sym->ident!=iFUNCTN) { + error(21,name); /* yes, but not as a function */ + return NULL; /* make sure the old symbol is not damaged */ + } else if ((sym->usage & uNATIVE)!=0) { + error(21,name); /* yes, and it is a native */ + } /* if */ + assert(sym->vclass==sGLOBAL); + if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag) + error(25); /* mismatch from earlier prototype */ + if ((sym->usage & uDEFINE)==0) { + /* as long as the function stays undefined, update the address and the tag */ + if (sym->states==NULL) + sym->addr=code_idx; + sym->tag=tag; + } /* if */ + } else { + /* don't set the "uDEFINE" flag; it may be a prototype */ + sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0); + assert(sym!=NULL); /* fatal error 103 must be given on error */ + /* assume no arguments */ + sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo)); + sym->dim.arglist[0].ident=0; + /* set library ID to NULL (only for native functions) */ + sym->x.lib=NULL; + /* set the required stack size to zero (only for non-native functions) */ + sym->x.stacksize=1; /* 1 for PROC opcode */ + } /* if */ + if (pc_depricate!=NULL) { + assert(sym!=NULL); + sym->flags|=flgDEPRICATED; + if (sc_status==statWRITE) { + if (sym->documentation!=NULL) { + free(sym->documentation); + sym->documentation=NULL; + } /* if */ + sym->documentation=pc_depricate; + } else { + free(pc_depricate); + } /* if */ + pc_depricate=NULL; + } /* if */ + + return sym; +} + +/* This routine adds symbolic information for each argument. + */ +static void define_args(void) +{ + symbol *sym; + + /* At this point, no local variables have been declared. All + * local symbols are function arguments. + */ + sym=loctab.next; + while (sym!=NULL) { + assert(sym->ident!=iLABEL); + assert(sym->vclass==sLOCAL); + markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */ + sym=sym->next; + } /* while */ +} + +static int operatorname(char *name) +{ + int opertok; + char *str; + cell val; + + assert(name!=NULL); + + /* check the operator */ + opertok=lex(&val,&str); + switch (opertok) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '>': + case '<': + case '!': + case '~': + case '=': + name[0]=(char)opertok; + name[1]='\0'; + break; + case tINC: + strcpy(name,"++"); + break; + case tDEC: + strcpy(name,"--"); + break; + case tlEQ: + strcpy(name,"=="); + break; + case tlNE: + strcpy(name,"!="); + break; + case tlLE: + strcpy(name,"<="); + break; + case tlGE: + strcpy(name,">="); + break; + default: + name[0]='\0'; + error(7); /* operator cannot be redefined (or bad operator name) */ + return 0; + } /* switch */ + + return opertok; +} + +static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag) +{ + int tags[2]={0,0}; + int count=0; + arginfo *arg; + char tmpname[sNAMEMAX+1]; + symbol *oldsym; + + if (opertok==0) + return TRUE; + + assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL); + /* count arguments and save (first two) tags */ + while (arg=&sym->dim.arglist[count], arg->ident!=0) { + if (count<2) { + if (arg->numtags>1) + error(65,count+1); /* function argument may only have a single tag */ + else if (arg->numtags==1) + tags[count]=arg->tags[0]; + } /* if */ + if (opertok=='~' && count==0) { + if (arg->ident!=iREFARRAY) + error(73,arg->name);/* must be an array argument */ + } else { + if (arg->ident!=iVARIABLE) + error(66,arg->name);/* must be non-reference argument */ + } /* if */ + if (arg->hasdefault) + error(59,arg->name); /* arguments of an operator may not have a default value */ + count++; + } /* while */ + + /* for '!', '++' and '--', count must be 1 + * for '-', count may be 1 or 2 + * for '=', count must be 1, and the resulttag is also important + * for all other (binary) operators and the special '~' operator, count must be 2 + */ + switch (opertok) { + case '!': + case '=': + case tINC: + case tDEC: + if (count!=1) + error(62); /* number or placement of the operands does not fit the operator */ + break; + case '-': + if (count!=1 && count!=2) + error(62); /* number or placement of the operands does not fit the operator */ + break; + default: + if (count!=2) + error(62); /* number or placement of the operands does not fit the operator */ + } /* switch */ + + if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0)) + error(64); /* cannot change predefined operators */ + + /* change the operator name */ + assert(strlen(opername)>0); + operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag); + if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) { + int i; + if ((oldsym->usage & uDEFINE)!=0) { + char errname[2*sNAMEMAX+16]; + funcdisplayname(errname,tmpname); + error(21,errname); /* symbol already defined */ + } /* if */ + sym->usage|=oldsym->usage; /* copy flags from the previous definition */ + for (i=0; inumrefers; i++) + if (oldsym->refer[i]!=NULL) + refer_symbol(sym,oldsym->refer[i]); + delete_symbol(&glbtab,oldsym); + } /* if */ + strcpy(sym->name,tmpname); + sym->hash=namehash(sym->name);/* calculate new hash */ + + /* operators should return a value, except the '~' operator */ + if (opertok!='~') + sym->usage |= uRETVALUE; + + return TRUE; +} + +static int check_operatortag(int opertok,int resulttag,char *opername) +{ + assert(opername!=NULL && strlen(opername)>0); + switch (opertok) { + case '!': + case '<': + case '>': + case tlEQ: + case tlNE: + case tlLE: + case tlGE: + if (resulttag!=pc_addtag("bool")) { + error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */ + return FALSE; + } /* if */ + break; + case '~': + if (resulttag!=0) { + error(63,opername,"_:"); /* operator "~" requires a "_:" result tag */ + return FALSE; + } /* if */ + break; + } /* switch */ + return TRUE; +} + +static char *tag2str(char *dest,int tag) +{ + tag &= TAGMASK; + assert(tag>=0); + sprintf(dest,"0%x",tag); + return isdigit(dest[1]) ? &dest[1] : dest; +} + +SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag) +{ + char tagstr1[10], tagstr2[10]; + int opertok; + + assert(numtags>=1 && numtags<=2); + opertok= (opername[1]=='\0') ? opername[0] : 0; + if (opertok=='=') + sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1)); + else if (numtags==1 || opertok=='~') + sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1)); + else + sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2)); + return symname; +} + +static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname) +{ + char *ptr,*name; + int unary; + + /* tags are only positive, so if the function name starts with a '-', + * the operator is an unary '-' or '--' operator. + */ + if (*fname=='-') { + *tag1=0; + unary=TRUE; + ptr=fname; + } else { + *tag1=(int)strtol(fname,&ptr,16); + unary= ptr==fname; /* unary operator if it doesn't start with a tag name */ + } /* if */ + assert(!unary || *tag1==0); + assert(*ptr!='\0'); + for (name=opname; !isdigit(*ptr); ) + *name++ = *ptr++; + *name='\0'; + *tag2=(int)strtol(ptr,NULL,16); + return unary; +} + +static constvalue *find_tag_byval(int tag) +{ + constvalue *tagsym; + tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG); + if (tagsym==NULL) + tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG); + return tagsym; +} + +SC_FUNC char *funcdisplayname(char *dest,char *funcname) +{ + int tags[2]; + char opname[10]; + constvalue *tagsym[2]; + int unary; + + if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') { + if (dest!=funcname) + strcpy(dest,funcname); + return dest; + } /* if */ + + unary=parse_funcname(funcname,&tags[0],&tags[1],opname); + tagsym[1]=find_tag_byval(tags[1]); + assert(tagsym[1]!=NULL); + if (unary) { + sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name); + } else { + tagsym[0]=find_tag_byval(tags[0]); + assert(tagsym[0]!=NULL); + /* special case: the assignment operator has the return value as the 2nd tag */ + if (opname[0]=='=' && opname[1]=='\0') + sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name); + else + sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name); + } /* if */ + return dest; +} + +static void funcstub(int fnative) +{ + int tok,tag,fpublic; + char *str; + cell val,size; + char symbolname[sNAMEMAX+1]; + int idxtag[sDIMEN_MAX]; + int dim[sDIMEN_MAX]; + int numdim; + symbol *sym,*sub; + int opertok; + + opertok=0; + lastst=0; + litidx=0; /* clear the literal pool */ + assert(loctab.next==NULL); /* local symbol table should be empty */ + + tag=pc_addtag(NULL); /* get the tag of the return value */ + numdim=0; + while (matchtoken('[')) { + /* the function returns an array, get this tag for the index and the array + * dimensions + */ + if (numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return; + } /* if */ + size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */ + if (size==0) + error(9); /* invalid array size */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + dim[numdim++]=(int)size; + } /* while */ + + tok=lex(&val,&str); + fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR); + if (fnative) { + if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR) + error(42); /* invalid combination of class specifiers */ + } else { + if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC) + tok=lex(&val,&str); + } /* if */ + + if (tok==tOPERATOR) { + opertok=operatorname(symbolname); + if (opertok==0) + return; /* error message already given */ + check_operatortag(opertok,tag,symbolname); + } else { + if (tok!=tSYMBOL && freading) { + error(10); /* illegal function or declaration */ + return; + } /* if */ + strcpy(symbolname,str); + } /* if */ + needtoken('('); /* only functions may be native/forward */ + + sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ + if (sym==NULL) + return; + if (fnative) { + sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); + sym->x.lib=curlibrary; + } else if (fpublic) { + sym->usage|=uPUBLIC; + } /* if */ + sym->usage|=uFORWARD; + + declargs(sym,FALSE); + /* "declargs()" found the ")" */ + sc_attachdocumentation(sym); /* attach any documenation to the function */ + if (!operatoradjust(opertok,sym,symbolname,tag)) + sym->usage &= ~uDEFINE; + + if (getstates(symbolname)!=0) { + if (fnative || opertok!=0) + error(82); /* native functions and operators may not have states */ + else + error(231); /* ignoring state specifications on forward declarations */ + } /* if */ + + /* for a native operator, also need to specify an "exported" function name; + * for a native function, this is optional + */ + if (fnative) { + if (opertok!=0) { + needtoken('='); + lexpush(); /* push back, for matchtoken() to retrieve again */ + } /* if */ + if (matchtoken('=')) { + /* allow number or symbol */ + if (matchtoken(tSYMBOL)) { + tokeninfo(&val,&str); + insert_alias(sym->name,str); + } else { + constexpr(&val,NULL,NULL); + sym->addr=val; + /* At the moment, I have assumed that this syntax is only valid if + * val < 0. To properly mix "normal" native functions and indexed + * native functions, one should use negative indices anyway. + * Special code for a negative index in sym->addr exists in SC4.C + * (ffcall()) and in SC6.C (the loops for counting the number of native + * variables and for writing them). + */ + } /* if */ + } /* if */ + } /* if */ + needtoken(tTERM); + + /* attach the array to the function symbol */ + if (numdim>0) { + assert(sym!=NULL); + sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag); + sub->parent=sym; + } /* if */ + + litidx=0; /* clear the literal pool */ + delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */ +} + +/* newfunc - begin a function + * + * This routine is called from "parse" and tries to make a function + * out of the following text + * + * Global references: funcstatus,lastst,litidx + * rettype (altered) + * curfunc (altered) + * declared (altered) + * glb_declared (altered) + * sc_alignnext (altered) + */ +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock) +{ + symbol *sym; + int argcnt,tok,tag,funcline; + int opertok,opererror; + char symbolname[sNAMEMAX+1]; + char *str; + cell val,cidx,glbdecl; + short filenum; + int state_id; + + assert(litidx==0); /* literal queue should be empty */ + litidx=0; /* clear the literal pool (should already be empty) */ + opertok=0; + lastst=0; /* no statement yet */ + cidx=0; /* just to avoid compiler warnings */ + glbdecl=0; + assert(loctab.next==NULL); /* local symbol table should be empty */ + filenum=fcurrent; /* save file number at the start of the declaration */ + + if (firstname!=NULL) { + assert(strlen(firstname)<=sNAMEMAX); + strcpy(symbolname,firstname); /* save symbol name */ + tag=firsttag; + } else { + tag= (firsttag>=0) ? firsttag : pc_addtag(NULL); + tok=lex(&val,&str); + assert(!fpublic); + if (tok==tNATIVE || tok==tPUBLIC && stock) + error(42); /* invalid combination of class specifiers */ + if (tok==tOPERATOR) { + opertok=operatorname(symbolname); + if (opertok==0) + return TRUE; /* error message already given */ + check_operatortag(opertok,tag,symbolname); + } else { + if (tok!=tSYMBOL && freading) { + error(20,str); /* invalid symbol name */ + return FALSE; + } /* if */ + assert(strlen(str)<=sNAMEMAX); + strcpy(symbolname,str); + } /* if */ + } /* if */ + /* check whether this is a function or a variable declaration */ + if (!matchtoken('(')) + return FALSE; + /* so it is a function, proceed */ + funcline=fline; /* save line at which the function is defined */ + if (symbolname[0]==PUBLIC_CHAR) { + fpublic=TRUE; /* implicitly public function */ + if (stock) + error(42); /* invalid combination of class specifiers */ + } /* if */ + sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ + if (sym==NULL || (sym->usage & uNATIVE)!=0) + return TRUE; /* it was recognized as a function declaration, but not as a valid one */ + if (fpublic) + sym->usage|=uPUBLIC; + if (fstatic) + sym->fnumber=filenum; + /* if the function was used before being declared, and it has a tag for the + * result, add a third pass (as second "skimming" parse) because the function + * result may have been used with user-defined operators, which have now + * been incorrectly flagged (as the return tag was unknown at the time of + * the call) + */ + if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) { + int curstatus=sc_status; + sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */ + error(208); + sc_status=curstatus; + sc_reparse=TRUE; /* must add another pass to "initial scan" phase */ + } /* if */ + /* we want public functions to be explicitly prototyped, as they are called + * from the outside + */ + if (fpublic && (sym->usage & uFORWARD)==0) + error(235,symbolname); + /* declare all arguments */ + argcnt=declargs(sym,TRUE); + opererror=!operatoradjust(opertok,sym,symbolname,tag); + if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) { + if (argcnt>0) + error(5); /* "main()" and "entry()" functions may not have any arguments */ + sym->usage|=uREAD; /* "main()" is the program's entry point: always used */ + } /* if */ + state_id=getstates(symbolname); + if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0)) + error(82); /* operators may not have states, main() may neither */ + attachstatelist(sym,state_id); + /* "declargs()" found the ")"; if a ";" appears after this, it was a + * prototype */ + if (matchtoken(';')) { + sym->usage|=uFORWARD; + if (!sc_needsemicolon) + error(218); /* old style prototypes used with optional semicolumns */ + delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */ + return TRUE; + } /* if */ + /* so it is not a prototype, proceed */ + /* if this is a function that is not referred to (this can only be detected + * in the second stage), shut code generation off */ + if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) { + sc_status=statSKIP; + cidx=code_idx; + glbdecl=glb_declared; + } /* if */ + if ((sym->flags & flgDEPRICATED)!=0) { + char *ptr= (sym->documentation!=NULL) ? sym->documentation : ""; + error(234,symbolname,ptr); /* depricated (probably a public function) */ + } /* if */ + begcseg(); + sym->usage|=uDEFINE; /* set the definition flag */ + if (stock) + sym->usage|=uSTOCK; + if (opertok!=0 && opererror) + sym->usage &= ~uDEFINE; + /* if the function has states, dump the label to the start of the function */ + if (state_id!=0) { + constvalue *ptr=sym->states->next; + while (ptr!=NULL) { + assert(sc_status!=statWRITE || strlen(ptr->name)>0); + if (ptr->index==state_id) { + setlabel((int)strtol(ptr->name,NULL,16)); + break; + } /* if */ + ptr=ptr->next; + } /* while */ + } /* if */ + startfunc(sym->name); /* creates stack frame */ + insert_dbgline(funcline); + setline(FALSE); + if (sc_alignnext) { + alignframe(sc_dataalign); + sc_alignnext=FALSE; + } /* if */ + declared=0; /* number of local cells */ + rettype=(sym->usage & uRETVALUE); /* set "return type" variable */ + curfunc=sym; + define_args(); /* add the symbolic info for the function arguments */ + #if !defined SC_LIGHT + if (matchtoken('{')) { + lexpush(); + } else { + /* Insert a separator so that comments following the statement will not + * be attached to this function; they should be attached to the next + * function. This is not a problem for functions having a compound block, + * because the closing brace is an explicit "end token" for the function. + * With single statement functions, the preprocessor may overread the + * source code before the parser determines an "end of statement". + */ + insert_docstring_separator(); + } /* if */ + #endif + sc_curstates=state_id;/* set state id, for accessing global state variables */ + statement(NULL,FALSE); + sc_curstates=0; + if ((rettype & uRETVALUE)!=0) + sym->usage|=uRETVALUE; + if (declared!=0) { + /* This happens only in a very special (and useless) case, where a function + * has only a single statement in its body (no compound block) and that + * statement declares a new variable + */ + modstk((int)declared*sizeof(cell)); /* remove all local variables */ + declared=0; + } /* if */ + if ((lastst!=tRETURN) && (lastst!=tGOTO)){ + ldconst(0,sPRI); + ffret(strcmp(sym->name,uENTRYFUNC)!=0); + if ((sym->usage & uRETVALUE)!=0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + error(209,symname); /* function should return a value */ + } /* if */ + } /* if */ + endfunc(); + sym->codeaddr=code_idx; + sc_attachdocumentation(sym); /* attach collected documenation to the function */ + if (litidx) { /* if there are literals defined */ + glb_declared+=litidx; + begdseg(); /* flip to DATA segment */ + dumplits(); /* dump literal strings */ + litidx=0; + } /* if */ + testsymbols(&loctab,0,TRUE,TRUE); /* test for unused arguments and labels */ + delete_symbols(&loctab,0,TRUE,TRUE); /* clear local variables queue */ + assert(loctab.next==NULL); + curfunc=NULL; + if (sc_status==statSKIP) { + sc_status=statWRITE; + code_idx=cidx; + glb_declared=glbdecl; + } /* if */ + return TRUE; +} + +static int argcompare(arginfo *a1,arginfo *a2) +{ + int result,level,i; + + result= strcmp(a1->name,a2->name)==0; /* name */ + if (result) + result= a1->ident==a2->ident; /* type/class */ + if (result) + result= a1->usage==a2->usage; /* "const" flag */ + if (result) + result= a1->numtags==a2->numtags; /* tags (number and names) */ + for (i=0; result && inumtags; i++) + result= a1->tags[i]==a2->tags[i]; + if (result) + result= a1->numdim==a2->numdim; /* array dimensions & index tags */ + for (level=0; result && levelnumdim; level++) + result= a1->dim[level]==a2->dim[level]; + for (level=0; result && levelnumdim; level++) + result= a1->idxtag[level]==a2->idxtag[level]; + if (result) + result= a1->hasdefault==a2->hasdefault; /* availability of default value */ + if (a1->hasdefault) { + if (a1->ident==iREFARRAY) { + if (result) + result= a1->defvalue.array.size==a2->defvalue.array.size; + if (result) + result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize; + /* ??? should also check contents of the default array (these troubles + * go away in a 2-pass compiler that forbids double declarations, but + * Pawn currently does not forbid them) */ + } else { + if (result) { + if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0) + result= a1->hasdefault==a2->hasdefault + && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0 + && a1->defvalue.size.level==a2->defvalue.size.level; + else + result= a1->defvalue.val==a2->defvalue.val; + } /* if */ + } /* if */ + if (result) + result= a1->defvalue_tag==a2->defvalue_tag; + } /* if */ + return result; +} + +/* declargs() + * + * This routine adds an entry in the local symbol table for each argument + * found in the argument list. It returns the number of arguments. + */ +static int declargs(symbol *sym,int chkshadow) +{ + #define MAXTAGS 16 + char *ptr; + int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags; + cell val; + arginfo arg, *arglist; + char name[sNAMEMAX+1]; + int ident,fpublic,fconst; + int idx; + + /* if the function is already defined earlier, get the number of arguments + * of the existing definition + */ + oldargcnt=0; + if ((sym->usage & uPROTOTYPED)!=0) + while (sym->dim.arglist[oldargcnt].ident!=0) + oldargcnt++; + argcnt=0; /* zero aruments up to now */ + ident=iVARIABLE; + numtags=0; + fconst=FALSE; + fpublic= (sym->usage & uPUBLIC)!=0; + /* the '(' parantheses has already been parsed */ + if (!matchtoken(')')){ + do { /* there are arguments; process them */ + /* any legal name increases argument count (and stack offset) */ + tok=lex(&val,&ptr); + switch (tok) { + case 0: + /* nothing */ + break; + case '&': + if (ident!=iVARIABLE || numtags>0) + error(1,"-identifier-","&"); + ident=iREFERENCE; + break; + case tCONST: + if (ident!=iVARIABLE || numtags>0) + error(1,"-identifier-","const"); + fconst=TRUE; + break; + case tLABEL: + if (numtags>0) + error(1,"-identifier-","-tagname-"); + tags[0]=pc_addtag(ptr); + numtags=1; + break; + case '{': + if (numtags>0) + error(1,"-identifier-","-tagname-"); + numtags=0; + while (numtags=sMAXARGS) + error(45); /* too many function arguments */ + strcpy(name,ptr); /* save symbol name */ + if (name[0]==PUBLIC_CHAR) + error(56,name); /* function arguments cannot be public */ + if (numtags==0) + tags[numtags++]=0; /* default tag */ + /* Stack layout: + * base + 0*sizeof(cell) == previous "base" + * base + 1*sizeof(cell) == function return address + * base + 2*sizeof(cell) == number of arguments + * base + 3*sizeof(cell) == first argument of the function + * So the offset of each argument is "(argcnt+3) * sizeof(cell)". + */ + doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg); + if (fpublic && arg.hasdefault) + error(59,name); /* arguments of a public function may not have a default value */ + if ((sym->usage & uPROTOTYPED)==0) { + /* redimension the argument list, add the entry */ + sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo)); + if (sym->dim.arglist==0) + error(103); /* insufficient memory */ + memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */ + sym->dim.arglist[argcnt]=arg; + } else { + /* check the argument with the earlier definition */ + if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg)) + error(25); /* function definition does not match prototype */ + /* may need to free default array argument and the tag list */ + if (arg.ident==iREFARRAY && arg.hasdefault) + free(arg.defvalue.array.data); + else if (arg.ident==iVARIABLE + && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0)) + free(arg.defvalue.size.symname); + free(arg.tags); + } /* if */ + argcnt++; + ident=iVARIABLE; + numtags=0; + fconst=FALSE; + break; + case tELLIPS: + if (ident!=iVARIABLE) + error(10); /* illegal function or declaration */ + if (numtags==0) + tags[numtags++]=0; /* default tag */ + if ((sym->usage & uPROTOTYPED)==0) { + /* redimension the argument list, add the entry iVARARGS */ + sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo)); + if (sym->dim.arglist==0) + error(103); /* insufficient memory */ + memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */ + sym->dim.arglist[argcnt].ident=iVARARGS; + sym->dim.arglist[argcnt].hasdefault=FALSE; + sym->dim.arglist[argcnt].defvalue.val=0; + sym->dim.arglist[argcnt].defvalue_tag=0; + sym->dim.arglist[argcnt].numtags=numtags; + sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]); + if (sym->dim.arglist[argcnt].tags==NULL) + error(103); /* insufficient memory */ + memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]); + } else { + if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS) + error(25); /* function definition does not match prototype */ + } /* if */ + argcnt++; + break; + default: + error(10); /* illegal function or declaration */ + } /* switch */ + } while (tok=='&' || tok==tLABEL || tok==tCONST + || tok!=tELLIPS && matchtoken(',')); /* more? */ + /* if the next token is not ",", it should be ")" */ + needtoken(')'); + } /* if */ + /* resolve any "sizeof" arguments (now that all arguments are known) */ + assert(sym->dim.arglist!=NULL); + arglist=sym->dim.arglist; + for (idx=0; idx=argcnt) { + error(17,ptr); /* undefined symbol */ + } else { + assert(arglist[idx].defvalue.size.symname!=NULL); + /* check the level against the number of dimensions */ + if (arglist[idx].defvalue.size.level>0 + && arglist[idx].defvalue.size.level>=arglist[altidx].numdim) + error(28,arglist[idx].name); /* invalid subscript */ + /* check the type of the argument whose size to take; for a iVARIABLE + * or a iREFERENCE, this is always 1 (so the code is redundant) + */ + assert(arglist[altidx].ident!=iVARARGS); + if (arglist[altidx].ident!=iREFARRAY && (arglist[idx].hasdefault & uSIZEOF)!=0) { + if ((arglist[idx].hasdefault & uTAGOF)!=0) { + error(81,arglist[idx].name); /* cannot take "tagof" an indexed array */ + } else { + assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE); + error(223,ptr); /* redundant sizeof */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* for */ + + sym->usage|=uPROTOTYPED; + errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/ + return argcnt; +} + +/* doarg - declare one argument type + * + * this routine is called from "declargs()" and adds an entry in the local + * symbol table for one argument. + * + * "fpublic" indicates whether the function for this argument list is public. + * The arguments themselves are never public. + */ +static void doarg(char *name,int ident,int offset,int tags[],int numtags, + int fpublic,int fconst,int chkshadow,arginfo *arg) +{ + symbol *argsym; + constvalue *enumroot; + cell size; + + strcpy(arg->name,name); + arg->hasdefault=FALSE; /* preset (most common case) */ + arg->defvalue.val=0; /* clear */ + arg->defvalue_tag=0; + arg->numdim=0; + if (matchtoken('[')) { + if (ident==iREFERENCE) + error(67,name); /* illegal declaration ("&name[]" is unsupported) */ + do { + if (arg->numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return; + } /* if */ + size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + arg->dim[arg->numdim]=(int)size; + arg->numdim+=1; + } while (matchtoken('[')); + ident=iREFARRAY; /* "reference to array" (is a pointer) */ + if (matchtoken('=')) { + lexpush(); /* initials() needs the "=" token again */ + assert(litidx==0); /* at the start of a function, this is reset */ + assert(numtags>0); + initials(ident,tags[0],&size,arg->dim,arg->numdim,enumroot); + assert(size>=litidx); + /* allocate memory to hold the initial values */ + arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell)); + if (arg->defvalue.array.data!=NULL) { + int i; + memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell)); + arg->hasdefault=TRUE; /* argument has default value */ + arg->defvalue.array.size=litidx; + arg->defvalue.array.addr=-1; + /* calulate size to reserve on the heap */ + arg->defvalue.array.arraysize=1; + for (i=0; inumdim; i++) + arg->defvalue.array.arraysize*=arg->dim[i]; + if (arg->defvalue.array.arraysize < arg->defvalue.array.size) + arg->defvalue.array.arraysize = arg->defvalue.array.size; + } /* if */ + litidx=0; /* reset */ + } /* if */ + } else { + if (matchtoken('=')) { + unsigned char size_tag_token; + assert(ident==iVARIABLE || ident==iREFERENCE); + arg->hasdefault=TRUE; /* argument has a default value */ + size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0); + if (size_tag_token==0) + size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0); + if (size_tag_token!=0) { + int paranthese; + if (ident==iREFERENCE) + error(66,name); /* argument may not be a reference */ + paranthese=0; + while (matchtoken('(')) + paranthese++; + if (needtoken(tSYMBOL)) { + /* save the name of the argument whose size id to take */ + char *name; + cell val; + tokeninfo(&val,&name); + if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL) + error(103); /* insufficient memory */ + arg->defvalue.size.level=0; + if (size_tag_token==uSIZEOF) { + while (matchtoken('[')) { + arg->defvalue.size.level+=(short)1; + needtoken(']'); + } /* while */ + } /* if */ + if (ident==iVARIABLE) /* make sure we set this only if not a reference */ + arg->hasdefault |= size_tag_token; /* uSIZEOF or uTAGOF */ + } /* if */ + while (paranthese--) + needtoken(')'); + } else { + constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL); + assert(numtags>0); + if (!matchtag(tags[0],arg->defvalue_tag,TRUE)) + error(213); /* tagname mismatch */ + } /* if */ + } /* if */ + } /* if */ + arg->ident=(char)ident; + arg->usage=(char)(fconst ? uCONST : 0); + arg->numtags=numtags; + arg->tags=(int*)malloc(numtags*sizeof tags[0]); + if (arg->tags==NULL) + error(103); /* insufficient memory */ + memcpy(arg->tags,tags,numtags*sizeof tags[0]); + argsym=findloc(name); + if (argsym!=NULL) { + error(21,name); /* symbol already defined */ + } else { + if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN) + error(219,name); /* variable shadows another symbol */ + /* add details of type and address */ + assert(numtags>0); + argsym=addvariable(name,offset,ident,sLOCAL,tags[0], + arg->dim,arg->numdim,arg->idxtag); + argsym->compound=0; + if (ident==iREFERENCE) + argsym->usage|=uREAD; /* because references are passed back */ + if (fpublic) + argsym->usage|=uREAD; /* arguments of public functions are always "used" */ + if (fconst) + argsym->usage|=uCONST; + } /* if */ +} + +static int count_referrers(symbol *entry) +{ + int i,count; + + count=0; + for (i=0; inumrefers; i++) + if (entry->refer[i]!=NULL) + count++; + return count; +} + +#if !defined SC_LIGHT +static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue, + char **outer_start,int *outer_length, + char **inner_start,int *inner_length) +{ + char *ptr,*inner_end; + int xmltag_len,xmlparam_len,xmlvalue_len; + int match; + + assert(source!=NULL); + assert(xmltag!=NULL); + assert(outer_start!=NULL); + assert(outer_length!=NULL); + assert(inner_start!=NULL); + assert(inner_length!=NULL); + + /* both NULL or both non-NULL */ + assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL); + + xmltag_len=strlen(xmltag); + xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0; + xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0; + ptr=source; + /* find an opening '<' */ + while ((ptr=strchr(ptr,'<'))!=NULL) { + *outer_start=ptr; /* be optimistic... */ + match=FALSE; /* ...and pessimistic at the same time */ + ptr++; /* skip '<' */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) { + /* xml tag found, optionally check the parameter */ + ptr+=xmltag_len; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (xmlparam!=NULL) { + if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) { + ptr+=xmlparam_len; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (*ptr=='=') { + ptr++; /* skip '=' */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (*ptr=='"' || *ptr=='\'') + ptr++; /* skip " or ' */ + assert(xmlvalue!=NULL); + if (strncmp(ptr,xmlvalue,xmlvalue_len)==0 + && (*(ptr+xmlvalue_len)<=' ' + || *(ptr+xmlvalue_len)=='>' + || *(ptr+xmlvalue_len)=='"' + || *(ptr+xmlvalue_len)=='\'')) + match=TRUE; /* found it */ + } /* if */ + } /* if */ + } else { + match=TRUE; /* don't check the parameter */ + } /* if */ + } /* if */ + if (match) { + /* now find the end of the opening tag */ + while (*ptr!='\0' && *ptr!='>') + ptr++; + if (*ptr=='>') + ptr++; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + *inner_start=ptr; + /* find the start of the closing tag (assume no nesting) */ + while ((ptr=strchr(ptr,'<'))!=NULL) { + inner_end=ptr; + ptr++; /* skip '<' */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (*ptr=='/') { + ptr++; /* skip / */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) { + /* find the end of the closing tag */ + while (*ptr!='\0' && *ptr!='>') + ptr++; + if (*ptr=='>') + ptr++; + /* set the lengths of the inner and outer segment */ + assert(*inner_start!=NULL); + *inner_length=(int)(inner_end-*inner_start); + assert(*outer_start!=NULL); + *outer_length=(int)(ptr-*outer_start); + break; /* break out of the loop */ + } /* if */ + } /* if */ + } /* while */ + return TRUE; + } /* if */ + } /* while */ + return FALSE; /* not found */ +} + +static char *xmlencode(char *dest,char *source) +{ + char temp[2*sNAMEMAX+20],*ptr; + + /* replace < by < and such; normally, such a symbol occurs at most once in + * a symbol name (e.g. "operator<") + */ + ptr=temp; + while (*source!='\0') { + switch (*source) { + case '<': + strcpy(ptr,"<"); + ptr+=4; + break; + case '>': + strcpy(ptr,">"); + ptr+=4; + break; + case '&': + strcpy(ptr,"&"); + ptr+=5; + break; + default: + *ptr++=*source; + } /* switch */ + source++; + } /* while */ + *ptr='\0'; + strcpy(dest,temp); + return dest; +} + +static void make_report(symbol *root,FILE *log,char *sourcefile) +{ + char symname[_MAX_PATH]; + int i,arg; + symbol *sym,*ref; + constvalue *tagsym; + constvalue *enumroot; + char *ptr; + + /* adapt the installation directory */ + strcpy(symname,sc_rootpath); + #if DIRSEP_CHAR=='\\' + while ((ptr=strchr(symname,':'))!=NULL) + *ptr='|'; + while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL) + *ptr='/'; + #endif + + /* the XML header */ + fprintf(log,"\n"); + fprintf(log,"\n",symname); + fprintf(log,"\n",sourcefile); + ptr=strrchr(sourcefile,DIRSEP_CHAR); + if (ptr!=NULL) + ptr++; + else + ptr=sourcefile; + fprintf(log,"\t\n\t\t%s\n\t\n",ptr); + + /* attach the global documentation, if any */ + if (sc_documentation!=NULL) { + fprintf(log,"\n\t\n"); + fprintf(log,"\t\n\t\t"); + fputs(sc_documentation,log); + fprintf(log,"\n\t\n\n"); + } /* if */ + + /* use multiple passes to print constants variables and functions in + * separate sections + */ + fprintf(log,"\t\n"); + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE + || sym->ident==iARRAY || sym->ident==iFUNCTN); + if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0) + continue; + if ((sym->usage & uREAD)==0) + continue; + fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name),sym->addr); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + /* browse through all fields */ + if ((enumroot=sym->dim.enumlist)!=NULL) { + enumroot=enumroot->next; /* skip root */ + while (enumroot!=NULL) { + fprintf(log,"\t\t\t\n",funcdisplayname(symname,enumroot->name),enumroot->value); + /* find the constant with this name and get the tag */ + ref=findglb(enumroot->name,sGLOBAL); + if (ref!=NULL) { + if (ref->x.tags.index!=0) { + tagsym=find_tag_byval(ref->x.tags.index); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\t\n",tagsym->name); + } /* if */ + if (ref->dim.array.length!=1) + fprintf(log,"\t\t\t\t\n",(long)ref->dim.array.length); + } /* if */ + fprintf(log,"\t\t\t\n"); + enumroot=enumroot->next; + } /* while */ + } /* if */ + assert(sym->refer!=NULL); + for (i=0; inumrefers; i++) { + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE + || sym->ident==iARRAY || sym->ident==iFUNCTN); + if (sym->ident!=iCONSTEXPR) + continue; + if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0) + continue; + fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name),sym->addr); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + assert(sym->refer!=NULL); + for (i=0; inumrefers; i++) { + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + if (sym->ident!=iVARIABLE && sym->ident!=iARRAY) + continue; + fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name)); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + assert(sym->refer!=NULL); + if ((sym->usage & uPUBLIC)!=0) + fprintf(log,"\t\t\t\n"); + for (i=0; inumrefers; i++) { + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + if (sym->ident!=iFUNCTN) + continue; + if ((sym->usage & (uREAD | uNATIVE))==uNATIVE) + continue; /* unused native function */ + funcdisplayname(symname,sym->name); + xmlencode(symname,symname); + fprintf(log,"\t\tdim.arglist!=NULL); + for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) { + int dim; + if (arg>0) + fprintf(log,", "); + switch (sym->dim.arglist[arg].ident) { + case iVARIABLE: + fprintf(log,"%s",sym->dim.arglist[arg].name); + break; + case iREFERENCE: + fprintf(log,"&%s",sym->dim.arglist[arg].name); + break; + case iREFARRAY: + fprintf(log,"%s",sym->dim.arglist[arg].name); + for (dim=0; dimdim.arglist[arg].numdim;dim++) + fprintf(log,"[]"); + break; + case iVARARGS: + fprintf(log,"..."); + break; + } /* switch */ + } /* for */ + /* ??? should also print an "array return" size */ + fprintf(log,")\">\n"); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + /* check whether this function is called from the outside */ + if ((sym->usage & uNATIVE)!=0) + fprintf(log,"\t\t\t\n"); + if ((sym->usage & uPUBLIC)!=0) + fprintf(log,"\t\t\t\n"); + if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0) + fprintf(log,"\t\t\t\n"); + if ((sym->usage & uNATIVE)==0) + fprintf(log,"\t\t\t\n",(long)sym->x.stacksize); + if (sym->states!=NULL) { + constvalue *stlist=sym->states->next; + assert(stlist!=NULL); /* there should be at least one state item */ + while (stlist!=NULL && stlist->index==-1) + stlist=stlist->next; + assert(stlist!=NULL); /* state id should be found */ + i=state_getfsa(stlist->index); + assert(i>=0); /* automaton 0 exists */ + stlist=automaton_findid(i); + assert(stlist!=NULL); /* automaton should be found */ + fprintf(log,"\t\t\t\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)"); + //??? dump state decision table + } /* if */ + assert(sym->refer!=NULL); + for (i=0; inumrefers; i++) + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + /* print all symbols that are required for this function to compile */ + for (ref=root->next; ref!=NULL; ref=ref->next) { + if (ref==sym) + continue; + for (i=0; inumrefers; i++) + if (ref->refer[i]==sym) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + /* print parameter list, with tag & const information, plus descriptions */ + assert(sym->dim.arglist!=NULL); + for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) { + int dim,paraminfo; + char *outer_start,*inner_start; + int outer_length,inner_length; + if (sym->dim.arglist[arg].ident==iVARARGS) + fprintf(log,"\t\t\t\n"); + else + fprintf(log,"\t\t\t\n",sym->dim.arglist[arg].name); + /* print the tag name(s) for each parameter */ + assert(sym->dim.arglist[arg].numtags>0); + assert(sym->dim.arglist[arg].tags!=NULL); + paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) + || sym->dim.arglist[arg].ident==iREFERENCE + || sym->dim.arglist[arg].ident==iREFARRAY; + if (paraminfo) + fprintf(log,"\t\t\t\t"); + if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) { + assert(paraminfo); + if (sym->dim.arglist[arg].numtags>1) + fprintf(log," {"); + for (i=0; idim.arglist[arg].numtags; i++) { + if (i>0) + fprintf(log,","); + tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]); + assert(tagsym!=NULL); + fprintf(log,"%s",tagsym->name); + } /* for */ + if (sym->dim.arglist[arg].numtags>1) + fprintf(log,"}"); + } /* if */ + switch (sym->dim.arglist[arg].ident) { + case iREFERENCE: + fprintf(log," &"); + break; + case iREFARRAY: + fprintf(log," "); + for (dim=0; dimdim.arglist[arg].numdim; dim++) { + if (sym->dim.arglist[arg].dim[dim]==0) { + fprintf(log,"[]"); + } else { + //??? find index tag + fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]); + } /* if */ + } /* for */ + break; + } /* switch */ + if (paraminfo) + fprintf(log," \n"); + /* print the user description of the parameter (parse through + * sym->documentation) + */ + if (sym->documentation!=NULL + && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name, + &outer_start, &outer_length, &inner_start, &inner_length)) + { + char *tail; + fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start); + /* delete from documentation string */ + tail=outer_start+outer_length; + memmove(outer_start,tail,strlen(tail)+1); + } /* if */ + fprintf(log,"\t\t\t\n"); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\n"); + fprintf(log,"\n"); +} +#endif + +/* Every symbol has a referrer list, that contains the functions that use + * the symbol. Now, if function "apple" is accessed by functions "banana" and + * "citron", but neither function "banana" nor "citron" are used by anyone + * else, then, by inference, function "apple" is not used either. + */ +static void reduce_referrers(symbol *root) +{ + int i,restart; + symbol *sym,*ref; + + do { + restart=0; + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + if (sym->ident==iFUNCTN + && (sym->usage & uNATIVE)==0 + && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0 + && count_referrers(sym)==0) + { + sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */ + /* find all symbols that are referred by this symbol */ + for (ref=root->next; ref!=NULL; ref=ref->next) { + if (ref->parent!=NULL) + continue; /* hierarchical data type */ + assert(ref->refer!=NULL); + for (i=0; inumrefers && ref->refer[i]!=sym; i++) + /* nothing */; + if (inumrefers) { + assert(ref->refer[i]==sym); + ref->refer[i]=NULL; + restart++; + } /* if */ + } /* for */ + } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY) + && (sym->usage & uPUBLIC)==0 + && sym->parent==NULL + && count_referrers(sym)==0) + { + sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */ + } /* if */ + } /* for */ + /* after removing a symbol, check whether more can be removed */ + } while (restart>0); +} + +#if !defined SC_LIGHT +static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion) +{ + long size,maxsize; + int i; + + assert(sym!=NULL); + assert(sym->ident==iFUNCTN); + assert((sym->usage & uNATIVE)==0); + assert(recursion!=NULL); + + maxsize=sym->x.stacksize; + for (i=0; inumrefers; i++) { + if (sym->refer[i]!=NULL) { + assert(sym->refer[i]->ident==iFUNCTN); + assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */ + if (sym->refer[i]==sourcesym) { /* recursion detection */ + *recursion=1; + break; /* recursion was detected, quit loop */ + } /* if */ + size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion); + if (maxsizeusage & uPUBLIC)!=0) { + /* Find out how many parameters a public function has, then see if this + * is bigger than some maximum + */ + arginfo *arg=sym->dim.arglist; + int count=0; + assert(arg!=0); + while (arg->ident!=0) { + count++; + arg++; + } /* while */ + assert(pubfuncparams!=0); + if (count>*pubfuncparams) + *pubfuncparams=count; + } /* if */ + + return maxsize+basesize; +} + +static long max_stacksize(symbol *root,int *recursion) +{ + /* Loop over all non-native functions. For each function, loop + * over all of its referrers, accumulating the stack requirements. + * Detect (indirect) recursion with a "mark-and-sweep" algorithm. + * I (mis-)use the "compound" field of the symbol structure for + * the marker, as this field is unused for functions. + * + * Note that the stack is shared with the heap. A host application + * may "eat" cells from the heap as well, through amx_Allot(). The + * stack requirements are thus only an estimate. + */ + long size,maxsize; + int maxparams; + symbol *sym; + + assert(root!=NULL); + assert(recursion!=NULL); + #if !defined NDEBUG + for (sym=root->next; sym!=NULL; sym=sym->next) + if (sym->ident==iFUNCTN) + assert(sym->compound==0); + #endif + + maxsize=0; + maxparams=0; + *recursion=0; /* assume no recursion */ + for (sym=root->next; sym!=NULL; sym=sym->next) { + /* drop out if this is not a user-implemented function */ + if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0) + continue; + /* accumulate stack size for this symbol */ + size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion); + assert(size>=0); + if (maxsizenext; + while (sym!=NULL && sym->compound>=level) { + switch (sym->ident) { + case iLABEL: + if (testlabs) { + if ((sym->usage & uDEFINE)==0) { + error(19,sym->name); /* not a label: ... */ + } else if ((sym->usage & uREAD)==0) { + errorset(sSETPOS,sym->lnumber); + error(203,sym->name); /* symbol isn't used: ... */ + } /* if */ + } /* if */ + break; + case iFUNCTN: + if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) { + funcdisplayname(symname,sym->name); + if (strlen(symname)>0) + error(203,symname); /* symbol isn't used ... (and not public/native/stock) */ + } /* if */ + if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0) + entry=TRUE; /* there is an entry point */ + /* also mark the function to the debug information */ + if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0) + insert_dbgsymbol(sym); + break; + case iCONSTEXPR: + if (testconst && (sym->usage & uREAD)==0) { + errorset(sSETPOS,sym->lnumber); + error(203,sym->name); /* symbol isn't used: ... */ + } /* if */ + break; + default: + /* a variable */ + if (sym->parent!=NULL) + break; /* hierarchical data type */ + if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) { + if (testconst) + errorset(sSETPOS,sym->lnumber); + error(203,sym->name,sym->lnumber); /* symbol isn't used (and not stock) */ + } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) { + errorset(sSETPOS,sym->lnumber); + error(204,sym->name); /* value assigned to symbol is never used */ +#if 0 // ??? not sure whether it is a good idea to force people use "const" + } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) { + errorset(sSETPOS,sym->lnumber); + error(214,sym->name); /* make array argument "const" */ +#endif + } /* if */ + /* also mark the variable (local or global) to the debug information */ + if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0) + insert_dbgsymbol(sym); + } /* if */ + sym=sym->next; + } /* while */ + + return entry; +} + +static cell calc_array_datasize(symbol *sym, cell *offset) +{ + cell length; + + assert(sym!=NULL); + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + length=sym->dim.array.length; + if (sym->dim.array.level > 0) { + cell sublength=calc_array_datasize(finddepend(sym),offset); + if (offset!=NULL) + *offset=length*(*offset+sizeof(cell)); + if (sublength>0) + length*=length*sublength; + else + length=0; + } else { + if (offset!=NULL) + *offset=0; + } /* if */ + return length; +} + +static void destructsymbols(symbol *root,int level) +{ + cell offset=0; + int savepri=FALSE; + symbol *sym=root->next; + while (sym!=NULL && sym->compound>=level) { + if (sym->ident==iVARIABLE || sym->ident==iARRAY) { + char symbolname[16]; + symbol *opsym; + cell elements; + /* check that the '~' operator is defined for this tag */ + operator_symname(symbolname,"~",sym->tag,0,1,0); + if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) { + /* save PRI, in case of a return statment */ + if (!savepri) { + pushreg(sPRI); /* right-hand operand is in PRI */ + savepri=TRUE; + } /* if */ + /* if the variable is an array, get the number of elements */ + if (sym->ident==iARRAY) { + elements=calc_array_datasize(sym,&offset); + /* "elements" can be zero when the variable is declared like + * new mytag: myvar[2][] = { {1, 2}, {3, 4} } + * one should declare all dimensions! + */ + if (elements==0) + error(46,sym->name); /* array size is unknown */ + } else { + elements=1; + offset=0; + } /* if */ + pushval(elements); + /* call the '~' operator */ + address(sym,sPRI); + addconst(offset); /* add offset to array data to the address */ + pushreg(sPRI); + pushval(2*sizeof(cell));/* 2 parameters */ + assert(opsym->ident==iFUNCTN); + ffcall(opsym,NULL,1); + if (sc_status!=statSKIP) + markusage(opsym,uREAD); /* do not mark as "used" when this call itself is skipped */ + if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL) + opsym->x.lib->value += 1; /* increment "usage count" of the library */ + } /* if */ + } /* if */ + sym=sym->next; + } /* while */ + /* restore PRI, if it was saved */ + if (savepri) + popreg(sPRI); +} + +static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val, + int index) +{ + constvalue *cur; + + if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(103); /* insufficient memory (fatal error) */ + memset(cur,0,sizeof(constvalue)); + if (name!=NULL) { + assert(strlen(name)name,name); + } /* if */ + cur->value=val; + cur->index=index; + cur->next=next; + prev->next=cur; + return cur; +} + +SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index) +{ + constvalue *cur,*prev; + + /* find the end of the constant table */ + for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next) + /* nothing */; + return insert_constval(prev,NULL,name,val,index); +} + +SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index) +{ + constvalue *ptr = table->next; + + while (ptr!=NULL) { + if (strcmp(name,ptr->name)==0 && ptr->index==index) + return ptr; + ptr=ptr->next; + } /* while */ + return NULL; +} + +static constvalue *find_constval_byval(constvalue *table,cell val) +{ + constvalue *ptr = table->next; + + while (ptr!=NULL) { + if (ptr->value==val) + return ptr; + ptr=ptr->next; + } /* while */ + return NULL; +} + +#if 0 /* never used */ +static int delete_constval(constvalue *table,char *name) +{ + constvalue *prev = table; + constvalue *cur = prev->next; + + while (cur!=NULL) { + if (strcmp(name,cur->name)==0) { + prev->next=cur->next; + free(cur); + return TRUE; + } /* if */ + prev=cur; + cur=cur->next; + } /* while */ + return FALSE; +} +#endif + +SC_FUNC void delete_consttable(constvalue *table) +{ + constvalue *cur=table->next, *next; + + while (cur!=NULL) { + next=cur->next; + free(cur); + cur=next; + } /* while */ + memset(table,0,sizeof(constvalue)); +} + +/* add_constant + * + * Adds a symbol to the symbol table. Returns NULL on failure. + */ +SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag) +{ + symbol *sym; + + /* Test whether a global or local symbol with the same name exists. Since + * constants are stored in the symbols table, this also finds previously + * defind constants. */ + sym=findglb(name,sSTATEVAR); + if (!sym) + sym=findloc(name); + if (sym) { + int redef=0; + if (sym->ident!=iCONSTEXPR) + redef=1; /* redefinition a function/variable to a constant is not allowed */ + if ((sym->usage & uENUMFIELD)!=0) { + /* enum field, special case if it has a different tag and the new symbol is also an enum field */ + constvalue *tagid; + symbol *tagsym; + if (sym->tag==tag) + redef=1; /* enumeration field is redefined (same tag) */ + tagid=find_tag_byval(tag); + if (tagid==NULL) { + redef=1; /* new constant does not have a tag */ + } else { + tagsym=findconst(tagid->name,NULL); + if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0) + redef=1; /* new constant is not an enumeration field */ + } /* if */ + /* in this particular case (enumeration field that is part of a different + * enum, and non-conflicting with plain constants) we want to be able to + * redefine it + */ + if (!redef) + goto redef_enumfield; + } else if (sym->tag!=tag) { + redef=1; /* redefinition of a constant (non-enum) to a different tag is not allowed */ + } /* if */ + if (redef) { + error(21,name); /* symbol already defined */ + return NULL; + } else if (sym->addr!=val) { + error(201,name); /* redefinition of constant (different value) */ + sym->addr=val; /* set new value */ + } /* if */ + /* silently ignore redefinitions of constants with the same value & tag */ + return sym; + } /* if */ + + /* constant doesn't exist yet (or is allowed to be redefined) */ +redef_enumfield: + sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE); + assert(sym!=NULL); /* fatal error 103 must be given on error */ + if (sc_status == statIDLE) + sym->usage |= uPREDEF; + return sym; +} + +/* statement - The Statement Parser + * + * This routine is called whenever the parser needs to know what statement + * it encounters (i.e. whenever program syntax requires a statement). + */ +static void statement(int *lastindent,int allow_decl) +{ + int tok; + cell val; + char *st; + + if (!freading) { + error(36); /* empty statement */ + return; + } /* if */ + errorset(sRESET,0); + + tok=lex(&val,&st); + if (tok!='{') { + insert_dbgline(fline); + setline(TRUE); + } /* if */ + /* lex() has set stmtindent */ + if (lastindent!=NULL && tok!=tLABEL) { + if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0) + error(217); /* loose indentation */ + *lastindent=stmtindent; + indent_nowarn=FALSE; /* if warning was blocked, re-enable it */ + } /* if */ + switch (tok) { + case 0: + /* nothing */ + break; + case tNEW: + if (allow_decl) { + declloc(FALSE); + lastst=tNEW; + } else { + error(3); /* declaration only valid in a block */ + } /* if */ + break; + case tSTATIC: + if (allow_decl) { + declloc(TRUE); + lastst=tNEW; + } else { + error(3); /* declaration only valid in a block */ + } /* if */ + break; + case '{': + tok=fline; + if (!matchtoken('}')) /* {} is the empty statement */ + compound(tok==fline); + /* lastst (for "last statement") does not change */ + break; + case ';': + error(36); /* empty statement */ + break; + case tIF: + doif(); + lastst=tIF; + break; + case tWHILE: + dowhile(); + lastst=tWHILE; + break; + case tDO: + dodo(); + lastst=tDO; + break; + case tFOR: + dofor(); + lastst=tFOR; + break; + case tSWITCH: + doswitch(); + lastst=tSWITCH; + break; + case tCASE: + case tDEFAULT: + error(14); /* not in switch */ + break; + case tGOTO: + dogoto(); + lastst=tGOTO; + break; + case tLABEL: + dolabel(); + lastst=tLABEL; + break; + case tRETURN: + doreturn(); + lastst=tRETURN; + break; + case tBREAK: + dobreak(); + lastst=tBREAK; + break; + case tCONTINUE: + docont(); + lastst=tCONTINUE; + break; + case tEXIT: + doexit(); + lastst=tEXIT; + break; + case tASSERT: + doassert(); + lastst=tASSERT; + break; + case tSLEEP: + dosleep(); + lastst=tSLEEP; + break; + case tSTATE: + dostate(); + lastst=tSTATE; + break; + case tCONST: + decl_const(sLOCAL); + break; + case tENUM: + decl_enum(sLOCAL); + break; + default: /* non-empty expression */ + sc_allowproccall=optproccall; + lexpush(); /* analyze token later */ + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); + needtoken(tTERM); + lastst=tEXPR; + sc_allowproccall=FALSE; + } /* switch */ +} + +static void compound(int stmt_sameline) +{ + int indent=-1; + cell save_decl=declared; + int count_stmt=0; + int block_start=fline; /* save line where the compound block started */ + + /* if there is more text on this line, we should adjust the statement indent */ + if (stmt_sameline) { + int i; + const unsigned char *p=lptr; + /* go back to the opening brace */ + while (*p!='{') { + assert(p>pline); + p--; + } /* while */ + assert(*p=='{'); /* it should be found */ + /* go forward, skipping white-space */ + p++; + while (*p<=' ' && *p!='\0') + p++; + assert(*p!='\0'); /* a token should be found */ + stmtindent=0; + for (i=0; i<(int)(p-pline); i++) + if (pline[i]=='\t' && sc_tabsize>0) + stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize); + else + stmtindent++; + } /* if */ + + nestlevel+=1; /* increase compound statement level */ + while (matchtoken('}')==0){ /* repeat until compound statement is closed */ + if (!freading){ + error(30,block_start); /* compound block not closed at end of file */ + break; + } else { + if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE)) + error(225); /* unreachable code */ + statement(&indent,TRUE); /* do a statement */ + count_stmt++; + } /* if */ + } /* while */ + if (lastst!=tRETURN) + destructsymbols(&loctab,nestlevel); + if (lastst!=tRETURN && lastst!=tGOTO) + modstk((int)(declared-save_decl)*sizeof(cell)); /* delete local variable space */ + testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ + declared=save_decl; + delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but + * retain block local labels + * (within the function) */ + nestlevel-=1; /* decrease compound statement level */ +} + +/* doexpr + * + * Global references: stgidx (referred to only) + */ +static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr, + int *tag,symbol **symptr,int chkfuncresult) +{ + int index,ident; + int localstaging=FALSE; + cell val; + + if (!staging) { + stgset(TRUE); /* start stage-buffering */ + localstaging=TRUE; + assert(stgidx==0); + } /* if */ + index=stgidx; + errorset(sEXPRMARK,0); + do { + /* on second round through, mark the end of the previous expression */ + if (index!=stgidx) + markexpr(sEXPR,NULL,0); + sideeffect=FALSE; + ident=expression(&val,tag,symptr,chkfuncresult); + if (!allowarray && (ident==iARRAY || ident==iREFARRAY)) + error(33,"-unknown-"); /* array must be indexed */ + if (chkeffect && !sideeffect) + error(215); /* expression has no effect */ + sc_allowproccall=FALSE; /* cannot use "procedure call" syntax anymore */ + } while (comma && matchtoken(',')); /* more? */ + if (mark_endexpr) + markexpr(sEXPR,NULL,0); /* optionally, mark the end of the expression */ + errorset(sEXPRRELEASE,0); + if (localstaging) { + stgout(index); + stgset(FALSE); /* stop staging */ + } /* if */ + return ident; +} + +/* constexpr + */ +SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr) +{ + int ident,index; + cell cidx; + + stgset(TRUE); /* start stage-buffering */ + stgget(&index,&cidx); /* mark position in code generator */ + errorset(sEXPRMARK,0); + ident=expression(val,tag,symptr,FALSE); + stgdel(index,cidx); /* scratch generated code */ + stgset(FALSE); /* stop stage-buffering */ + if (ident!=iCONSTEXPR) { + error(8); /* must be constant expression */ + if (val!=NULL) + *val=0; + if (tag!=NULL) + *tag=0; + if (symptr!=NULL) + *symptr=NULL; + } /* if */ + errorset(sEXPRRELEASE,0); + return (ident==iCONSTEXPR); +} + +/* test + * + * In the case a "simple assignment" operator ("=") is used within a test, + * the warning "possibly unintended assignment" is displayed. This routine + * sets the global variable "sc_intest" to true, it is restored upon termination. + * In the case the assignment was intended, use parantheses around the + * expression to avoid the warning; primary() sets "sc_intest" to 0. + * + * Global references: sc_intest (altered, but restored upon termination) + */ +static void test(int label,int parens,int invert) +{ + int index,tok; + cell cidx; + int ident,tag; + cell constval; + symbol *sym; + int localstaging=FALSE; + + if (!staging) { + stgset(TRUE); /* start staging */ + localstaging=TRUE; + #if !defined NDEBUG + stgget(&index,&cidx); /* should start at zero if started locally */ + assert(index==0); + #endif + } /* if */ + + PUSHSTK_I(sc_intest); + sc_intest=TRUE; + if (parens) + needtoken('('); + do { + stgget(&index,&cidx); /* mark position (of last expression) in + * code generator */ + ident=expression(&constval,&tag,&sym,TRUE); + tok=matchtoken(','); + if (tok) + markexpr(sEXPR,NULL,0); + } while (tok); /* do */ + if (parens) + needtoken(')'); + if (ident==iARRAY || ident==iREFARRAY) { + char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } /* if */ + if (ident==iCONSTEXPR) { /* constant expression */ + sc_intest=(short)POPSTK_I();/* restore stack */ + stgdel(index,cidx); + if (constval) { /* code always executed */ + error(206); /* redundant test: always non-zero */ + } else { + error(205); /* redundant code: never executed */ + jumplabel(label); + } /* if */ + if (localstaging) { + stgout(0); /* write "jumplabel" code */ + stgset(FALSE); /* stop staging */ + } /* if */ + return; + } /* if */ + if (tag!=0 && tag!=pc_addtag("bool")) + if (check_userop(lneg,tag,0,1,NULL,&tag)) + invert= !invert; /* user-defined ! operator inverted result */ + if (invert) + jmp_ne0(label); /* jump to label if true (different from 0) */ + else + jmp_eq0(label); /* jump to label if false (equal to 0) */ + markexpr(sEXPR,NULL,0); /* end expression (give optimizer a chance) */ + sc_intest=(short)POPSTK_I(); /* double typecast to avoid warning with Microsoft C */ + if (localstaging) { + stgout(0); /* output queue from the very beginning (see + * assert() when localstaging is set to TRUE) */ + stgset(FALSE); /* stop staging */ + } /* if */ +} + +static void doif(void) +{ + int flab1,flab2; + int ifindent; + + ifindent=stmtindent; /* save the indent of the "if" instruction */ + flab1=getlabel(); /* get label number for false branch */ + test(flab1,TRUE,FALSE); /* get expression, branch to flab1 if false */ + statement(NULL,FALSE); /* if true, do a statement */ + if (matchtoken(tELSE)==0){ /* if...else ? */ + setlabel(flab1); /* no, simple if..., print false label */ + } else { + /* to avoid the "dangling else" error, we want a warning if the "else" + * has a lower indent than the matching "if" */ + if (stmtindent0) + error(217); /* loose indentation */ + flab2=getlabel(); + if ((lastst!=tRETURN) && (lastst!=tGOTO)) + jumplabel(flab2); + setlabel(flab1); /* print false label */ + statement(NULL,FALSE); /* do "else" clause */ + setlabel(flab2); /* print true label */ + } /* endif */ +} + +static void dowhile(void) +{ + int wq[wqSIZE]; /* allocate local queue */ + + addwhile(wq); /* add entry to queue for "break" */ + setlabel(wq[wqLOOP]); /* loop label */ + /* The debugger uses the "break" opcode to be able to "break" out of + * a loop. To make sure that each loop has a break opcode, even for the + * tiniest loop, set it below the top of the loop + */ + setline(TRUE); + test(wq[wqEXIT],TRUE,FALSE); /* branch to wq[wqEXIT] if false */ + statement(NULL,FALSE); /* if so, do a statement */ + jumplabel(wq[wqLOOP]); /* and loop to "while" start */ + setlabel(wq[wqEXIT]); /* exit label */ + delwhile(); /* delete queue entry */ +} + +/* + * Note that "continue" will in this case not jump to the top of the loop, but + * to the end: just before the TRUE-or-FALSE testing code. + */ +static void dodo(void) +{ + int wq[wqSIZE],top; + + addwhile(wq); /* see "dowhile" for more info */ + top=getlabel(); /* make a label first */ + setlabel(top); /* loop label */ + statement(NULL,FALSE); + needtoken(tWHILE); + setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */ + setline(TRUE); + test(wq[wqEXIT],TRUE,FALSE); + jumplabel(top); + setlabel(wq[wqEXIT]); + delwhile(); + needtoken(tTERM); +} + +static void dofor(void) +{ + int wq[wqSIZE],skiplab; + cell save_decl; + int save_nestlevel,index; + int *ptr; + + save_decl=declared; + save_nestlevel=nestlevel; + + addwhile(wq); + skiplab=getlabel(); + needtoken('('); + if (matchtoken(';')==0) { + /* new variable declarations are allowed here */ + if (matchtoken(tNEW)) { + /* The variable in expr1 of the for loop is at a + * 'compound statement' level of it own. + */ + nestlevel++; + declloc(FALSE); /* declare local variable */ + } else { + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */ + needtoken(';'); + } /* if */ + } /* if */ + /* Adjust the "declared" field in the "while queue", in case that + * local variables were declared in the first expression of the + * "for" loop. These are deleted in separately, so a "break" or a "continue" + * must ignore these fields. + */ + ptr=readwhile(); + assert(ptr!=NULL); + ptr[wqBRK]=(int)declared; + ptr[wqCONT]=(int)declared; + jumplabel(skiplab); /* skip expression 3 1st time */ + setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */ + setline(TRUE); + /* Expressions 2 and 3 are reversed in the generated code: expression 3 + * precedes expression 2. When parsing, the code is buffered and marks for + * the start of each expression are insterted in the buffer. + */ + assert(!staging); + stgset(TRUE); /* start staging */ + assert(stgidx==0); + index=stgidx; + stgmark(sSTARTREORDER); + stgmark((char)(sEXPRSTART+0)); /* mark start of 2nd expression in stage */ + setlabel(skiplab); /* jump to this point after 1st expression */ + if (matchtoken(';')==0) { + test(wq[wqEXIT],FALSE,FALSE); /* expression 2 (jump to wq[wqEXIT] if false) */ + needtoken(';'); + } /* if */ + stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */ + if (matchtoken(')')==0) { + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 3 */ + needtoken(')'); + } /* if */ + stgmark(sENDREORDER); /* mark end of reversed evaluation */ + stgout(index); + stgset(FALSE); /* stop staging */ + statement(NULL,FALSE); + jumplabel(wq[wqLOOP]); + setlabel(wq[wqEXIT]); + delwhile(); + + assert(nestlevel>=save_nestlevel); + if (nestlevel>save_nestlevel) { + /* Clean up the space and the symbol table for the local + * variable in "expr1". + */ + destructsymbols(&loctab,nestlevel); + modstk((int)(declared-save_decl)*sizeof(cell)); + testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ + declared=save_decl; + delete_symbols(&loctab,nestlevel,FALSE,TRUE); + nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */ + } /* if */ +} + +/* The switch statement is incompatible with its C sibling: + * 1. the cases are not drop through + * 2. only one instruction may appear below each case, use a compound + * instruction to execute multiple instructions + * 3. the "case" keyword accepts a comma separated list of values to + * match, it also accepts a range using the syntax "1 .. 4" + * + * SWITCH param + * PRI = expression result + * param = table offset (code segment) + * + */ +static void doswitch(void) +{ + int lbl_table,lbl_exit,lbl_case; + int tok,swdefault,casecount; + cell val; + char *str; + constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */ + constvalue *cse,*csp; + char labelname[sNAMEMAX+1]; + + needtoken('('); + doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */ + needtoken(')'); + /* generate the code for the switch statement, the label is the address + * of the case table (to be generated later). + */ + lbl_table=getlabel(); + lbl_case=0; /* just to avoid a compiler warning */ + ffswitch(lbl_table); + + needtoken('{'); + lbl_exit=getlabel(); /* get label number for jumping out of switch */ + swdefault=FALSE; + casecount=0; + do { + tok=lex(&val,&str); /* read in (new) token */ + switch (tok) { + case tCASE: + if (swdefault!=FALSE) + error(15); /* "default" case must be last in switch statement */ + lbl_case=getlabel(); + PUSHSTK_I(sc_allowtags); + sc_allowtags=FALSE; /* do not allow tagnames here */ + do { + casecount++; + + /* ??? enforce/document that, in a switch, a statement cannot start + * with a label. Then, you can search for: + * * the first semicolon (marks the end of a statement) + * * an opening brace (marks the start of a compound statement) + * and search for the right-most colon before that statement + * Now, by replacing the ':' by a special COLON token, you can + * parse all expressions until that special token. + */ + + constexpr(&val,NULL,NULL); + /* Search the insertion point (the table is kept in sorted order, so + * that advanced abstract machines can sift the case table with a + * binary search). Check for duplicate case values at the same time. + */ + for (csp=&caselist, cse=caselist.next; + cse!=NULL && cse->valuenext) + /* nothing */; + if (cse!=NULL && cse->value==val) + error(40,val); /* duplicate "case" label */ + /* Since the label is stored as a string in the "constvalue", the + * size of an identifier must be at least 8, as there are 8 + * hexadecimal digits in a 32-bit number. + */ + #if sNAMEMAX < 8 + #error Length of identifier (sNAMEMAX) too small. + #endif + assert(csp!=NULL); + assert(csp->next==cse); + insert_constval(csp,cse,itoh(lbl_case),val,0); + if (matchtoken(tDBLDOT)) { + cell end; + constexpr(&end,NULL,NULL); + if (end<=val) + error(50); /* invalid range */ + while (++val<=end) { + casecount++; + /* find the new insertion point */ + for (csp=&caselist, cse=caselist.next; + cse!=NULL && cse->valuenext) + /* nothing */; + if (cse!=NULL && cse->value==val) + error(40,val); /* duplicate "case" label */ + assert(csp!=NULL); + assert(csp->next==cse); + insert_constval(csp,cse,itoh(lbl_case),val,0); + } /* if */ + } /* if */ + } while (matchtoken(',')); + needtoken(':'); /* ':' ends the case */ + sc_allowtags=(short)POPSTK_I(); /* reset */ + setlabel(lbl_case); + statement(NULL,FALSE); + jumplabel(lbl_exit); + break; + case tDEFAULT: + if (swdefault!=FALSE) + error(16); /* multiple defaults in switch */ + lbl_case=getlabel(); + setlabel(lbl_case); + needtoken(':'); + swdefault=TRUE; + statement(NULL,FALSE); + /* Jump to lbl_exit, even thouh this is the last clause in the + * switch, because the jump table is generated between the last + * clause of the switch and the exit label. + */ + jumplabel(lbl_exit); + break; + case '}': + /* nothing, but avoid dropping into "default" */ + break; + default: + error(2); + indent_nowarn=TRUE; /* disable this check */ + tok='}'; /* break out of the loop after an error */ + } /* switch */ + } while (tok!='}'); + + #if !defined NDEBUG + /* verify that the case table is sorted (unfortunatly, duplicates can + * occur; there really shouldn't be duplicate cases, but the compiler + * may not crash or drop into an assertion for a user error). */ + for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next) + assert(cse->value <= cse->next->value); + #endif + /* generate the table here, before lbl_exit (general jump target) */ + setlabel(lbl_table); + assert(swdefault==FALSE || swdefault==TRUE); + if (swdefault==FALSE) { + /* store lbl_exit as the "none-matched" label in the switch table */ + strcpy(labelname,itoh(lbl_exit)); + } else { + /* lbl_case holds the label of the "default" clause */ + strcpy(labelname,itoh(lbl_case)); + } /* if */ + ffcase(casecount,labelname,TRUE); + /* generate the rest of the table */ + for (cse=caselist.next; cse!=NULL; cse=cse->next) + ffcase(cse->value,cse->name,FALSE); + + setlabel(lbl_exit); + delete_consttable(&caselist); /* clear list of case labels */ +} + +static void doassert(void) +{ + int flab1,index; + cell cidx; + + if ((sc_debug & sCHKBOUNDS)!=0) { + flab1=getlabel(); /* get label number for "OK" branch */ + test(flab1,FALSE,TRUE); /* get expression and branch to flab1 if true */ + insert_dbgline(fline); /* make sure we can find the correct line number */ + ffabort(xASSERTION); + setlabel(flab1); + } else { + stgset(TRUE); /* start staging */ + stgget(&index,&cidx); /* mark position in code generator */ + do { + expression(NULL,NULL,NULL,FALSE); + stgdel(index,cidx); /* just scrap the code */ + } while (matchtoken(',')); + stgset(FALSE); /* stop staging */ + } /* if */ + needtoken(tTERM); +} + +static void dogoto(void) +{ + char *st; + cell val; + symbol *sym; + + if (lex(&val,&st)==tSYMBOL) { + sym=fetchlab(st); + jumplabel((int)sym->addr); + sym->usage|=uREAD; /* set "uREAD" bit */ + // ??? if the label is defined (check sym->usage & uDEFINE), check + // sym->compound (nesting level of the label) against nestlevel; + // if sym->compound < nestlevel, call the destructor operator + } else { + error(20,st); /* illegal symbol name */ + } /* if */ + needtoken(tTERM); +} + +static void dolabel(void) +{ + char *st; + cell val; + symbol *sym; + + tokeninfo(&val,&st); /* retrieve label name again */ + if (find_constval(&tagname_tab,st,0)!=NULL) + error(221,st); /* label name shadows tagname */ + sym=fetchlab(st); + setlabel((int)sym->addr); + /* since one can jump around variable declarations or out of compound + * blocks, the stack must be manually adjusted + */ + setstk(-declared*sizeof(cell)); + sym->usage|=uDEFINE; /* label is now defined */ +} + +/* fetchlab + * + * Finds a label from the (local) symbol table or adds one to it. + * Labels are local in scope. + * + * Note: The "_usage" bit is set to zero. The routines that call "fetchlab()" + * must set this bit accordingly. + */ +static symbol *fetchlab(char *name) +{ + symbol *sym; + + sym=findloc(name); /* labels are local in scope */ + if (sym){ + if (sym->ident!=iLABEL) + error(19,sym->name); /* not a label: ... */ + } else { + sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0); + assert(sym!=NULL); /* fatal error 103 must be given on error */ + sym->x.declared=(int)declared; + sym->compound=nestlevel; + } /* if */ + return sym; +} + +/* doreturn + * + * Global references: rettype (altered) + */ +static void doreturn(void) +{ + int tag,ident; + int level; + symbol *sym,*sub; + + if (!matchtoken(tTERM)) { + /* "return " */ + if ((rettype & uRETNONE)!=0) + error(78); /* mix "return;" and "return value;" */ + ident=doexpr(TRUE,FALSE,TRUE,TRUE,&tag,&sym,TRUE); + needtoken(tTERM); + /* see if this function already has a sub type (an array attached) */ + sub=finddepend(curfunc); + assert(sub==NULL || sub->ident==iREFARRAY); + if ((rettype & uRETVALUE)!=0) { + int retarray=(ident==iARRAY || ident==iREFARRAY); + /* there was an earlier "return" statement in this function */ + if (sub==NULL && retarray || sub!=NULL && !retarray) + error(79); /* mixing "return array;" and "return value;" */ + if (retarray && (curfunc->usage & uPUBLIC)!=0) + error(90,curfunc->name); /* public function may not return array */ + } /* if */ + rettype|=uRETVALUE; /* function returns a value */ + /* check tagname with function tagname */ + assert(curfunc!=NULL); + if (!matchtag(curfunc->tag,tag,TRUE)) + error(213); /* tagname mismatch */ + if (ident==iARRAY || ident==iREFARRAY) { + int dim[sDIMEN_MAX],numdim; + cell arraysize; + assert(sym!=NULL); + if (sub!=NULL) { + assert(sub->ident==iREFARRAY); + /* this function has an array attached already; check that the current + * "return" statement returns exactly the same array + */ + level=sym->dim.array.level; + if (sub->dim.array.level!=level) { + error(48); /* array dimensions must match */ + } else { + for (numdim=0; numdim<=level; numdim++) { + dim[numdim]=(int)sub->dim.array.length; + if (sym->dim.array.length!=dim[numdim]) + error(47); /* array sizes must match */ + if (numdimdim.array.level; + for (numdim=0; numdim<=level; numdim++) { + dim[numdim]=(int)sub->dim.array.length; + idxtag[numdim]=sub->x.tags.index; + if (numdimname); + } /* for */ + /* the address of the array is stored in a hidden parameter; the address + * of this parameter is 1 + the number of parameters (times the size of + * a cell) + the size of the stack frame and the return address + * base + 0*sizeof(cell) == previous "base" + * base + 1*sizeof(cell) == function return address + * base + 2*sizeof(cell) == number of arguments + * base + 3*sizeof(cell) == first argument of the function + * ... + * base + ((n-1)+3)*sizeof(cell) == last argument of the function + * base + (n+3)*sizeof(cell) == hidden parameter with array address + */ + assert(curfunc!=NULL); + assert(curfunc->dim.arglist!=NULL); + for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++) + /* nothing */; + sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag); + sub->parent=curfunc; + } /* if */ + /* get the hidden parameter, copy the array (the array is on the heap; + * it stays on the heap for the moment, and it is removed -usually- at + * the end of the expression/statement, see expression() in SC3.C) + */ + address(sub,sALT); /* ALT = destination */ + arraysize=calc_arraysize(dim,numdim,0); + memcopy(arraysize*sizeof(cell)); /* source already in PRI */ + /* moveto1(); is not necessary, callfunction() does a popreg() */ + } /* if */ + } else { + /* this return statement contains no expression */ + ldconst(0,sPRI); + if ((rettype & uRETVALUE)!=0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + assert(curfunc!=NULL); + funcdisplayname(symname,curfunc->name); + error(209,symname); /* function should return a value */ + } /* if */ + rettype|=uRETNONE; /* function does not return anything */ + } /* if */ + destructsymbols(&loctab,0); /* call destructor for *all* locals */ + modstk((int)declared*sizeof(cell)); /* end of function, remove *all* + * local variables */ + ffret(strcmp(curfunc->name,uENTRYFUNC)!=0); +} + +static void dobreak(void) +{ + int *ptr; + + ptr=readwhile(); /* readwhile() gives an error if not in loop */ + needtoken(tTERM); + if (ptr==NULL) + return; + destructsymbols(&loctab,nestlevel); + modstk(((int)declared-ptr[wqBRK])*sizeof(cell)); + jumplabel(ptr[wqEXIT]); +} + +static void docont(void) +{ + int *ptr; + + ptr=readwhile(); /* readwhile() gives an error if not in loop */ + needtoken(tTERM); + if (ptr==NULL) + return; + destructsymbols(&loctab,nestlevel); + modstk(((int)declared-ptr[wqCONT])*sizeof(cell)); + jumplabel(ptr[wqLOOP]); +} + +SC_FUNC void exporttag(int tag) +{ + /* find the tag by value in the table, then set the top bit to mark it + * "public" + */ + if (tag!=0 && (tag & PUBLICTAG)==0) { + constvalue *ptr; + for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next) + /* nothing */; + if (ptr!=NULL) + ptr->value |= PUBLICTAG; + } /* if */ +} + +static void doexit(void) +{ + int tag=0; + + if (matchtoken(tTERM)==0){ + doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE); + needtoken(tTERM); + } else { + ldconst(0,sPRI); + } /* if */ + ldconst(tag,sALT); + exporttag(tag); + destructsymbols(&loctab,0); /* call destructor for *all* locals */ + ffabort(xEXIT); +} + +static void dosleep(void) +{ + int tag=0; + + if (matchtoken(tTERM)==0){ + doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE); + needtoken(tTERM); + } else { + ldconst(0,sPRI); + } /* if */ + ldconst(tag,sALT); + exporttag(tag); + ffabort(xSLEEP); + + /* for stack usage checking, mark the use of the sleep instruction */ + pc_memflags |= suSLEEP_INSTR; +} + +static void dostate(void) +{ + char name[sNAMEMAX+1]; + constvalue *automaton; + constvalue *state; + constvalue *stlist; + int flabel; + symbol *sym; + #if !defined SC_LIGHT + int length,index,listid,listindex,stateindex; + char *doc; + #endif + + /* check for an optional condition */ + if (matchtoken('(')) { + flabel=getlabel(); /* get label number for "false" branch */ + pc_docexpr=TRUE; /* attach expression as a documentation string */ + test(flabel,FALSE,FALSE); /* get expression, branch to flabel if false */ + pc_docexpr=FALSE; + needtoken(')'); + } else { + flabel=-1; + } /* if */ + + if (!sc_getstateid(&automaton,&state)) { + delete_autolisttable(); + return; + } /* if */ + needtoken(tTERM); + + /* store the new state id */ + assert(state!=NULL); + ldconst(state->value,sPRI); + assert(automaton!=NULL); + assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0); + storereg(automaton->value,sPRI); + + /* find the optional entry() function for the state */ + sym=findglb(uENTRYFUNC,sGLOBAL); + if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) { + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + assert(strlen(stlist->name)!=0); + if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value)) + break; /* found! */ + } /* for */ + assert(stlist==NULL || state_inlist(stlist->index,state->value)); + if (stlist!=NULL) { + /* the label to jump to is in stlist->name */ + ffcall(sym,stlist->name,0); + } /* if */ + } /* if */ + + if (flabel>=0) + setlabel(flabel); /* condition was false, jump around the state switch */ + + #if !defined SC_LIGHT + /* mark for documentation */ + if (sc_status==statFIRST) { + char *str; + /* get the last list id attached to the function, this contains the source states */ + assert(curfunc!=NULL); + if (curfunc->states!=NULL) { + stlist=curfunc->states->next; + assert(stlist!=NULL); + while (stlist->next!=NULL) + stlist=stlist->next; + listid=stlist->index; + } else { + listid=-1; + } /* if */ + listindex=0; + length=strlen(name)+70; /* +70 for the fixed part "\n" */ + /* see if there are any condition strings to attach */ + for (index=0; (str=get_autolist(index))!=NULL; index++) + length+=strlen(str); + if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) { + do { + sprintf(doc,"=0) { + /* get the source state */ + stateindex=state_listitem(listid,listindex); + state=state_findid(stateindex); + assert(state!=NULL); + sprintf(doc+strlen(doc)," source=\"%s\"",state->name); + } /* if */ + if (get_autolist(0)!=NULL) { + /* add the condition */ + strcat(doc," condition=\""); + for (index=0; (str=get_autolist(index))!=NULL; index++) { + /* remove the ')' token that may be appended before detecting that the expression has ended */ + if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL) + strcat(doc,str); + } /* for */ + strcat(doc,"\""); + } /* if */ + strcat(doc,"/>\n"); + insert_docstring(doc); + } while (listid>=0 && ++listindex=(wq+wqTABSZ-wqSIZE)) + error(102,"loop table"); /* loop table overflow (too many active loops)*/ + k=0; + while (kwq) + wqptr-=wqSIZE; +} + +static int *readwhile(void) +{ + if (wqptr<=wq){ + error(24); /* out of context */ + return NULL; + } else { + return (wqptr-wqSIZE); + } /* if */ +} + diff --git a/compiler-init/sc2.c b/compiler-init/sc2.c new file mode 100644 index 00000000..667e6d44 --- /dev/null +++ b/compiler-init/sc2.c @@ -0,0 +1,2801 @@ +/* Pawn compiler - File input, preprocessing and lexical analysis functions + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc2.c 3590 2006-06-24 14:16:39Z thiadmer $ + */ +#include +#include +#include +#include +#include +#include +#include "lstring.h" +#include "sc.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + +#if defined FORTIFY + #include +#endif + +/* flags for litchar() */ +#define RAWMODE 1 +#define UTF8MODE 2 +static cell litchar(const unsigned char **lptr,int flags); +static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag); + +static void substallpatterns(unsigned char *line,int buffersize); +static int match(char *st,int end); +static int alpha(char c); + +#define SKIPMODE 1 /* bit field in "#if" stack */ +#define PARSEMODE 2 /* bit field in "#if" stack */ +#define HANDLED_ELSE 4 /* bit field in "#if" stack */ +#define SKIPPING (skiplevel>0 && (ifstack[skiplevel-1] & SKIPMODE)==SKIPMODE) + +static short icomment; /* currently in multiline comment? */ +static char ifstack[sCOMP_STACK]; /* "#if" stack */ +static short iflevel; /* nesting level if #if/#else/#endif */ +static short skiplevel; /* level at which we started skipping (including nested #if .. #endif) */ +static unsigned char term_expr[] = ""; +static int listline=-1; /* "current line" for the list file */ + + +/* pushstk & popstk + * + * Uses a LIFO stack to store information. The stack is used by doinclude(), + * doswitch() (to hold the state of "swactive") and some other routines. + * + * Porting note: I made the bold assumption that an integer will not be + * larger than a pointer (it may be smaller). That is, the stack element + * is typedef'ed as a pointer type, but I also store integers on it. See + * SC.H for "stkitem" + * + * Global references: stack,stkidx,stktop (private to pushstk(), popstk() + * and clearstk()) + */ +static stkitem *stack=NULL; +static int stkidx=0,stktop=0; + +SC_FUNC void pushstk(stkitem val) +{ + assert(stkidx<=stktop); + if (stkidx==stktop) { + stkitem *newstack; + int newsize= (stktop==0) ? 16 : 2*stktop; + /* try to resize the stack */ + assert(newsize>stktop); + newstack=(stkitem*)malloc(newsize*sizeof(stkitem)); + if (newstack==NULL) + error(102,"parser stack"); /* stack overflow (recursive include?) */ + /* swap the stacks */ + memcpy(newstack,stack,stkidx*sizeof(stkitem)); + if (stack!=NULL) + free(stack); + stack=newstack; + stktop=newsize; + } /* if */ + assert(stkidx'); /* termination character */ + lptr++; + while (*lptr<=' ' && *lptr!='\0') /* skip whitespace after quote */ + lptr++; + } else { + c='\0'; + } /* if */ + + i=0; + while (*lptr!=c && *lptr!='\0' && i0 && name[i-1]<=' ') + i--; /* strip trailing whitespace */ + assert(i>=0 && i are only read from the list of include directories. + */ + result=plungefile(name,(c!='>'),TRUE); + if (result) + add_constant(symname,1,sGLOBAL,0); + else if (!silent) + error(100,name); /* cannot read from ... (fatal error) */ + } /* if */ +} + +/* readline + * + * Reads in a new line from the input file pointed to by "inpf". readline() + * concatenates lines that end with a \ with the next line. If no more data + * can be read from the file, readline() attempts to pop off the previous file + * from the stack. If that fails too, it sets "freading" to 0. + * + * Global references: inpf,fline,inpfname,freading,icomment (altered) + */ +static void readline(unsigned char *line) +{ + int i,num,cont; + unsigned char *ptr; + + if (lptr==term_expr) + return; + num=sLINEMAX; + cont=FALSE; + do { + if (inpf==NULL || pc_eofsrc(inpf)) { + if (cont) + error(49); /* invalid line continuation */ + if (inpf!=NULL && inpf!=inpf_org) + pc_closesrc(inpf); + i=POPSTK_I(); + if (i==-1) { /* All's done; popstk() returns "stack is empty" */ + freading=FALSE; + *line='\0'; + /* when there is nothing more to read, the #if/#else stack should + * be empty and we should not be in a comment + */ + assert(iflevel>=0); + if (iflevel>0) + error(1,"#endif","-end of file-"); + else if (icomment!=0) + error(1,"*/","-end of file-"); + return; + } /* if */ + fline=i; + fcurrent=(short)POPSTK_I(); + icomment=(short)POPSTK_I(); + sc_is_utf8=(short)POPSTK_I(); + iflevel=(short)POPSTK_I(); + skiplevel=iflevel; /* this condition held before including the file */ + assert(!SKIPPING); /* idem ditto */ + curlibrary=(constvalue *)POPSTK_P(); + free(inpfname); /* return memory allocated for the include file name */ + inpfname=(char *)POPSTK_P(); + inpf=(FILE *)POPSTK_P(); + insert_dbgfile(inpfname); + setfiledirect(inpfname); + listline=-1; /* force a #line directive when changing the file */ + } /* if */ + + if (pc_readsrc(inpf,line,num)==NULL) { + *line='\0'; /* delete line */ + cont=FALSE; + } else { + /* check whether to erase leading spaces */ + if (cont) { + unsigned char *ptr=line; + while (*ptr<=' ' && *ptr!='\0') + ptr++; + if (ptr!=line) + memmove(line,ptr,strlen((char*)ptr)+1); + } /* if */ + cont=FALSE; + /* check whether a full line was read */ + if (strchr((char*)line,'\n')==NULL && !pc_eofsrc(inpf)) + error(75); /* line too long */ + /* check if the next line must be concatenated to this line */ + if ((ptr=(unsigned char*)strchr((char*)line,'\n'))==NULL) + ptr=(unsigned char*)strchr((char*)line,'\r'); + if (ptr!=NULL && ptr>line) { + assert(*(ptr+1)=='\0'); /* '\n' or '\r' should be last in the string */ + while (ptr>line && *ptr<=' ') + ptr--; /* skip trailing whitespace */ + if (*ptr=='\\') { + cont=TRUE; + /* set '\a' at the position of '\\' to make it possible to check + * for a line continuation in a single line comment (error 49) + */ + *ptr++='\a'; + *ptr='\0'; /* erase '\n' (and any trailing whitespace) */ + } /* if */ + } /* if */ + num-=strlen((char*)line); + line+=strlen((char*)line); + } /* if */ + fline+=1; + } while (num>=0 && cont); +} + +/* stripcom + * + * Replaces all comments from the line by space characters. It updates + * a global variable ("icomment") for multiline comments. + * + * This routine also supports the C++ extension for single line comments. + * These comments are started with "//" and end at the end of the line. + * + * The function also detects (and manages) "documentation comments". The + * global variable "icomment" is set to 2 for documentation comments. + * + * Global references: icomment (private to "stripcom") + */ +static void stripcom(unsigned char *line) +{ + char c; + #if !defined SC_LIGHT + #define COMMENT_LIMIT 100 + #define COMMENT_MARGIN 40 /* length of the longest word */ + char comment[COMMENT_LIMIT+COMMENT_MARGIN]; + int commentidx=0; + int skipstar=TRUE; + static int prev_singleline=FALSE; + int singleline=prev_singleline; + + prev_singleline=FALSE; /* preset */ + #endif + + while (*line){ + if (icomment!=0) { + if (*line=='*' && *(line+1)=='/') { + #if !defined SC_LIGHT + if (icomment==2) { + assert(commentidx0) + insert_docstring(comment); + } /* if */ + #endif + icomment=0; /* comment has ended */ + *line=' '; /* replace '*' and '/' characters by spaces */ + *(line+1)=' '; + line+=2; + } else { + if (*line=='/' && *(line+1)=='*') + error(216); /* nested comment */ + #if !defined SC_LIGHT + /* collect the comment characters in a string */ + if (icomment==2) { + if (skipstar && (*line!='\0' && *line<=' ' || *line=='*')) { + /* ignore leading whitespace and '*' characters */ + } else if (commentidxCOMMENT_LIMIT && *line!='\0' && *line<=' ') { + comment[commentidx]='\0'; + insert_docstring(comment); + commentidx=0; + } /* if */ + skipstar=FALSE; + } /* if */ + } /* if */ + #endif + *line=' '; /* replace comments by spaces */ + line+=1; + } /* if */ + } else { + if (*line=='/' && *(line+1)=='*'){ + icomment=1; /* start comment */ + #if !defined SC_LIGHT + /* there must be two "*" behind the slash and then white space */ + if (*(line+2)=='*' && *(line+3)<=' ') { + /* if we are not in a function, we must attach the previous block + * to the global documentation + */ + if (curfunc==NULL && get_docstring(0)!=NULL) + sc_attachdocumentation(NULL); + icomment=2; /* documentation comment */ + } /* if */ + commentidx=0; + skipstar=TRUE; + #endif + *line=' '; /* replace '/' and '*' characters by spaces */ + *(line+1)=' '; + line+=2; + if (icomment==2) + *line++=' '; + } else if (*line=='/' && *(line+1)=='/'){ /* comment to end of line */ + if (strchr((char*)line,'\a')!=NULL) + error(49); /* invalid line continuation */ + #if !defined SC_LIGHT + if (*(line+2)=='/' && *(line+3)<=' ') { + /* documentation comment */ + char *str=(char*)line+3; + char *end; + while (*str<=' ' && *str!='\0') + str++; /* skip leading whitespace */ + if ((end=strrchr(str,'\n'))!=NULL) + *end='\0';/* erase trailing '\n' */ + /* if there is a disjunct block, we may need to attach the previous + * block to the global documentation + */ + if (!singleline && curfunc==NULL && get_docstring(0)!=NULL) + sc_attachdocumentation(NULL); + insert_docstring(str); + prev_singleline=TRUE; + } /* if */ + #endif + *line++='\n'; /* put "newline" at first slash */ + *line='\0'; /* put "zero-terminator" at second slash */ + } else { + if (*line=='\"' || *line=='\''){ /* leave literals unaltered */ + c=*line; /* ending quote, single or double */ + line+=1; + while ((*line!=c || *(line-1)==sc_ctrlchar) && *line!='\0') + line+=1; + line+=1; /* skip final quote */ + } else { + line+=1; + } /* if */ + } /* if */ + } /* if */ + } /* while */ + #if !defined SC_LIGHT + if (icomment==2) { + assert(commentidx0) + insert_docstring(comment); + } /* if */ + #endif +} + +/* btoi + * + * Attempts to interpret a numeric symbol as a boolean value. On success + * it returns the number of characters processed (so the line pointer can be + * adjusted) and the value is stored in "val". Otherwise it returns 0 and + * "val" is garbage. + * + * A boolean value must start with "0b" + */ +static int btoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + + *val=0; + ptr=curptr; + if (*ptr=='0' && *(ptr+1)=='b') { + ptr+=2; + while (*ptr=='0' || *ptr=='1' || *ptr=='_') { + if (*ptr!='_') + *val=(*val<<1) | (*ptr-'0'); + ptr++; + } /* while */ + } else { + return 0; + } /* if */ + if (alphanum(*ptr)) /* number must be delimited by non-alphanumeric char */ + return 0; + else + return (int)(ptr-curptr); +} + +/* dtoi + * + * Attempts to interpret a numeric symbol as a decimal value. On success + * it returns the number of characters processed and the value is stored in + * "val". Otherwise it returns 0 and "val" is garbage. + */ +static int dtoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + + *val=0; + ptr=curptr; + if (!isdigit(*ptr)) /* should start with digit */ + return 0; + while (isdigit(*ptr) || *ptr=='_') { + if (*ptr!='_') + *val=(*val*10)+(*ptr-'0'); + ptr++; + } /* while */ + if (alphanum(*ptr)) /* number must be delimited by non-alphanumerical */ + return 0; + if (*ptr=='.' && isdigit(*(ptr+1))) + return 0; /* but a fractional part must not be present */ + return (int)(ptr-curptr); +} + +/* htoi + * + * Attempts to interpret a numeric symbol as a hexadecimal value. On + * success it returns the number of characters processed and the value is + * stored in "val". Otherwise it return 0 and "val" is garbage. + */ +static int htoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + + *val=0; + ptr=curptr; + if (!isdigit(*ptr)) /* should start with digit */ + return 0; + if (*ptr=='0' && *(ptr+1)=='x') { /* C style hexadecimal notation */ + ptr+=2; + while (ishex(*ptr) || *ptr=='_') { + if (*ptr!='_') { + assert(ishex(*ptr)); + *val= *val<<4; + if (isdigit(*ptr)) + *val+= (*ptr-'0'); + else + *val+= (tolower(*ptr)-'a'+10); + } /* if */ + ptr++; + } /* while */ + } else { + return 0; + } /* if */ + if (alphanum(*ptr)) + return 0; + else + return (int)(ptr-curptr); +} + +#if defined __GNUC__ +static double pow10(int value) +{ + double res=1.0; + while (value>=4) { + res*=10000.0; + value-=5; + } /* while */ + while (value>=2) { + res*=100.0; + value-=2; + } /* while */ + while (value>=1) { + res*=10.0; + value-=1; + } /* while */ + return res; +} +#endif + +/* ftoi + * + * Attempts to interpret a numeric symbol as a rational number, either as + * IEEE 754 single/double precision floating point or as a fixed point integer. + * On success it returns the number of characters processed and the value is + * stored in "val". Otherwise it returns 0 and "val" is unchanged. + * + * Pawn has stricter definition for rational numbers than most: + * o the value must start with a digit; ".5" is not a valid number, you + * should write "0.5" + * o a period must appear in the value, even if an exponent is given; "2e3" + * is not a valid number, you should write "2.0e3" + * o at least one digit must follow the period; "6." is not a valid number, + * you should write "6.0" + */ +static int ftoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + double fnum,ffrac,fmult; + unsigned long dnum,dbase; + int i, ignore; + + assert(rational_digits>=0 && rational_digits<9); + for (i=0,dbase=1; i0 && !ignore) { + error(222); /* number of digits exceeds rational number precision */ + ignore=TRUE; + } /* if */ + } /* if */ + ptr++; + } /* while */ + fnum += ffrac*fmult; /* form the number so far */ + if (*ptr=='e') { /* optional fractional part */ + int exp,sign; + ptr++; + if (*ptr=='-') { + sign=-1; + ptr++; + } else { + sign=1; + } /* if */ + if (!isdigit(*ptr)) /* 'e' should be followed by a digit */ + return 0; + exp=0; + while (isdigit(*ptr)) { + exp=(exp*10)+(*ptr-'0'); + ptr++; + } /* while */ + #if defined __GNUC__ + fmult=pow10(exp*sign); + #else + fmult=pow(10,exp*sign); + #endif + fnum *= fmult; + dnum *= (unsigned long)(fmult+0.5); + } /* if */ + + /* decide how to store the number */ + if (sc_rationaltag==0) { + error(70); /* rational number support was not enabled */ + *val=0; + } else if (rational_digits==0) { + /* floating point */ + #if PAWN_CELL_SIZE==32 + float value=(float)fnum; + *val=*((cell *)&value); + #if !defined NDEBUG + /* I assume that the C/C++ compiler stores "float" values in IEEE 754 + * format (as mandated in the ANSI standard). Test this assumption + * anyway. + * Note: problems have been reported with GCC 3.2.x, version 3.3.x works. + */ + { float test1 = 0.0, test2 = 50.0, test3 = -50.0; + uint32_t bit = 1; + /* test 0.0 == all bits 0 */ + assert(*(uint32_t*)&test1==0x00000000L); + /* test sign & magnitude format */ + assert(((*(uint32_t*)&test2) ^ (*(uint32_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1))); + /* test a known value */ + assert(*(uint32_t*)&test2==0x42480000L); + } + #endif + #elif PAWN_CELL_SIZE==64 + *val=*((cell *)&fnum); + #if !defined NDEBUG + /* I assume that the C/C++ compiler stores "double" values in IEEE 754 + * format (as mandated in the ANSI standard). + */ + { float test1 = 0.0, test2 = 50.0, test3 = -50.0; + uint64_t bit = 1; + /* test 0.0 == all bits 0 */ + assert(*(uint64_t*)&test1==0x00000000L); + /* test sign & magnitude format */ + assert(((*(uint64_t*)&test2) ^ (*(uint64_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1))); + } + #endif + #else + #error Unsupported cell size + #endif + } else { + /* fixed point */ + *val=(cell)dnum; + } /* if */ + + return (int)(ptr-curptr); +} + +/* number + * + * Reads in a number (binary, decimal or hexadecimal). It returns the number + * of characters processed or 0 if the symbol couldn't be interpreted as a + * number (in this case the argument "val" remains unchanged). This routine + * relies on the 'early dropout' implementation of the logical or (||) + * operator. + * + * Note: the routine doesn't check for a sign (+ or -). The - is checked + * for at "hier2()" (in fact, it is viewed as an operator, not as a + * sign) and the + is invalid (as in K&R C, and unlike ANSI C). + */ +static int number(cell *val,const unsigned char *curptr) +{ + int i; + cell value; + + if ((i=btoi(&value,curptr))!=0 /* binary? */ + || (i=htoi(&value,curptr))!=0 /* hexadecimal? */ + || (i=dtoi(&value,curptr))!=0) /* decimal? */ + { + *val=value; + return i; + } else { + return 0; /* else not a number */ + } /* if */ +} + +static void chrcat(char *str,char chr) +{ + str=strchr(str,'\0'); + *str++=chr; + *str='\0'; +} + +static int preproc_expr(cell *val,int *tag) +{ + int result; + int index; + cell code_index; + char *term; + + /* Disable staging; it should be disabled already because + * expressions may not be cut off half-way between conditional + * compilations. Reset the staging index, but keep the code + * index. + */ + if (stgget(&index,&code_index)) { + error(57); /* unfinished expression */ + stgdel(0,code_index); + stgset(FALSE); + } /* if */ + assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must point inside the string */ + #if !defined NO_DEFINE + /* preprocess the string */ + substallpatterns(pline,sLINEMAX); + assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must STILL point inside the string */ + #endif + /* append a special symbol to the string, so the expression + * analyzer won't try to read a next line when it encounters + * an end-of-line + */ + assert(strlen((char*)pline)=0); + if (iflevel>=sCOMP_STACK) + error(102,"Conditional compilation stack"); /* table overflow */ + iflevel++; + if (SKIPPING) + break; /* break out of switch */ + skiplevel=iflevel; + preproc_expr(&val,NULL); /* get value (or 0 on error) */ + ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE); + check_empty(lptr); + break; + case tpELSE: + case tpELSEIF: + ret=CMD_IF; + assert(iflevel>=0); + if (iflevel==0) { + error(26); /* no matching #if */ + errorset(sRESET,0); + } else { + /* check for earlier #else */ + if ((ifstack[iflevel-1] & HANDLED_ELSE)==HANDLED_ELSE) { + if (tok==tpELSEIF) + error(61); /* #elseif directive may not follow an #else */ + else + error(60); /* multiple #else directives between #if ... #endif */ + errorset(sRESET,0); + } else { + assert(iflevel>0); + /* if there has been a "parse mode" on this level, set "skip mode", + * otherwise, clear "skip mode" + */ + if ((ifstack[iflevel-1] & PARSEMODE)==PARSEMODE) { + /* there has been a parse mode already on this level, so skip the rest */ + ifstack[iflevel-1] |= (char)SKIPMODE; + } else { + /* previous conditions were all FALSE */ + if (tok==tpELSEIF) { + /* get new expression */ + preproc_expr(&val,NULL); /* get value (or 0 on error) */ + ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE); + } else { + /* a simple #else, clear skip mode */ + ifstack[iflevel-1] &= (char)~SKIPMODE; + } /* if */ + } /* if */ + } /* if */ + } /* if */ + check_empty(lptr); + break; + case tpENDIF: + ret=CMD_IF; + if (iflevel==0){ + error(26); /* no matching "#if" */ + errorset(sRESET,0); + } else { + iflevel--; + if (iflevel0) { + free(inpfname); + inpfname=duplicatestring(pathname); + if (inpfname==NULL) + error(103); /* insufficient memory */ + } /* if */ + } /* if */ + check_empty(lptr); + break; + case tpLINE: + if (!SKIPPING) { + if (lex(&val,&str)!=tNUMBER) + error(8); /* invalid/non-constant expression */ + fline=(int)val; + } /* if */ + check_empty(lptr); + break; + case tpASSERT: + if (!SKIPPING && (sc_debug & sCHKBOUNDS)!=0) { + for (str=(char*)lptr; *str<=' ' && *str!='\0'; str++) + /* nothing */; /* save start of expression */ + preproc_expr(&val,NULL); /* get constant expression (or 0 on error) */ + if (!val) + error(110,str); /* assertion failed */ + check_empty(lptr); + } /* if */ + break; + case tpPRAGMA: + if (!SKIPPING) { + if (lex(&val,&str)==tSYMBOL) { + if (strcmp(str,"amxlimit")==0) { + preproc_expr(&pc_amxlimit,NULL); + } else if (strcmp(str,"amxram")==0) { + preproc_expr(&pc_amxram,NULL); + } else if (strcmp(str,"codepage")==0) { + char name[sNAMEMAX+1]; + while (*lptr<=' ' && *lptr!='\0') + lptr++; + if (*lptr=='"') { + lptr=getstring((unsigned char*)name,sizeof name,lptr); + } else { + int i; + for (i=0; i9) { + error(68); /* invalid rational number precision */ + digits=0; + } /* if */ + if (*lptr==')') + lptr++; + } /* if */ + /* add the tag (make it public) and check the values */ + i=pc_addtag(name); + exporttag(i); + if (sc_rationaltag==0 || (sc_rationaltag==i && rational_digits==(int)digits)) { + sc_rationaltag=i; + rational_digits=(int)digits; + } else { + error(69); /* rational number format already set, can only be set once */ + } /* if */ + } else if (strcmp(str,"semicolon")==0) { + cell val; + preproc_expr(&val,NULL); + sc_needsemicolon=(int)val; + } else if (strcmp(str,"tabsize")==0) { + cell val; + preproc_expr(&val,NULL); + sc_tabsize=(int)val; + } else if (strcmp(str,"align")==0) { + sc_alignnext=TRUE; + } else if (strcmp(str,"unused")==0) { + char name[sNAMEMAX+1]; + int i,comma; + symbol *sym; + do { + /* get the name */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + for (i=0; iusage |= uREAD; + if (sym->ident==iVARIABLE || sym->ident==iREFERENCE + || sym->ident==iARRAY || sym->ident==iREFARRAY) + sym->usage |= uWRITTEN; + } else { + error(17,name); /* undefined symbol */ + } /* if */ + /* see if a comma follows the name */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + comma= (*lptr==','); + if (comma) + lptr++; + } while (comma); + } else { + error(207); /* unknown #pragma */ + } /* if */ + } else { + error(207); /* unknown #pragma */ + } /* if */ + check_empty(lptr); + } /* if */ + break; + case tpENDINPUT: + case tpENDSCRPT: + if (!SKIPPING) { + check_empty(lptr); + assert(inpf!=NULL); + if (inpf!=inpf_org) + pc_closesrc(inpf); + inpf=NULL; + } /* if */ + break; +#if !defined NOEMIT + case tpEMIT: { + /* write opcode to output file */ + char name[40]; + int i; + while (*lptr<=' ' && *lptr!='\0') + lptr++; + for (i=0; i<40 && (isalpha(*lptr) || *lptr=='.'); i++,lptr++) + name[i]=(char)tolower(*lptr); + name[i]='\0'; + stgwrite("\t"); + stgwrite(name); + stgwrite(" "); + code_idx+=opcodes(1); + /* write parameter (if any) */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + if (*lptr!='\0') { + symbol *sym; + tok=lex(&val,&str); + switch (tok) { + case tNUMBER: + case tRATIONAL: + outval(val,FALSE); + code_idx+=opargs(1); + break; + case tSYMBOL: + sym=findloc(str); + if (sym==NULL) + sym=findglb(str,sSTATEVAR); + if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) { + error(17,str); /* undefined symbol */ + } else { + outval(sym->addr,FALSE); + /* mark symbol as "used", unknown whether for read or write */ + markusage(sym,uREAD | uWRITTEN); + code_idx+=opargs(1); + } /* if */ + break; + default: { + char s2[20]; + extern char *sc_tokens[];/* forward declaration */ + if (tok<256) + sprintf(s2,"%c",(char)tok); + else + strcpy(s2,sc_tokens[tok-tFIRST]); + error(1,sc_tokens[tSYMBOL-tFIRST],s2); + break; + } /* case */ + } /* switch */ + } /* if */ + stgwrite("\n"); + check_empty(lptr); + break; + } /* case */ +#endif +#if !defined NO_DEFINE + case tpDEFINE: { + ret=CMD_DEFINE; + if (!SKIPPING) { + char *pattern,*substitution; + const unsigned char *start,*end; + int count,prefixlen; + stringpair *def; + /* find the pattern to match */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + start=lptr; /* save starting point of the match pattern */ + count=0; + while (*lptr>' ' && *lptr!='\0') { + litchar(&lptr,0); /* litchar() advances "lptr" and handles escape characters */ + count++; + } /* while */ + end=lptr; + /* check pattern to match */ + if (!alpha(*start)) { + error(74); /* pattern must start with an alphabetic character */ + break; + } /* if */ + /* store matched pattern */ + pattern=(char*)malloc(count+1); + if (pattern==NULL) + error(103); /* insufficient memory */ + lptr=start; + count=0; + while (lptr!=end) { + assert(lptr=2 && isdigit(pattern[count-1]) && pattern[count-2]=='%') + pattern[count-2]='\0'; + /* find substitution string */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + start=lptr; /* save starting point of the match pattern */ + count=0; + end=NULL; + while (*lptr!='\0') { + /* keep position of the start of trailing whitespace */ + if (*lptr<=' ') { + if (end==NULL) + end=lptr; + } else { + end=NULL; + } /* if */ + count++; + lptr++; + } /* while */ + if (end==NULL) + end=lptr; + /* store matched substitution */ + substitution=(char*)malloc(count+1); /* +1 for '\0' */ + if (substitution==NULL) + error(103); /* insufficient memory */ + lptr=start; + count=0; + while (lptr!=end) { + assert(lptr0); + if ((def=find_subst(pattern,prefixlen))!=NULL) { + if (strcmp(def->first,pattern)!=0 || strcmp(def->second,substitution)!=0) + error(201,pattern); /* redefinition of macro (non-identical) */ + delete_subst(pattern,prefixlen); + } /* if */ + /* add the pattern/substitution pair to the list */ + assert(strlen(pattern)>0); + insert_subst(pattern,substitution,prefixlen); + free(pattern); + free(substitution); + } /* if */ + break; + } /* case */ + case tpUNDEF: + if (!SKIPPING) { + if (lex(&val,&str)==tSYMBOL) { + ret=delete_subst(str,strlen(str)); + if (!ret) { + /* also undefine normal constants */ + symbol *sym=findconst(str,NULL); + if (sym!=NULL && (sym->usage & (uENUMROOT | uENUMFIELD))==0) { + delete_symbol(&glbtab,sym); + ret=TRUE; + } /* if */ + } /* if */ + if (!ret) + error(17,str); /* undefined symbol */ + } else { + error(20,str); /* invalid symbol name */ + } /* if */ + check_empty(lptr); + } /* if */ + break; +#endif + case tpERROR: + while (*lptr<=' ' && *lptr!='\0') + lptr++; + if (!SKIPPING) + error(111,lptr); /* user error */ + break; + default: + error(31); /* unknown compiler directive */ + ret=SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* process as normal line */ + } /* switch */ + return ret; +} + +#if !defined NO_DEFINE +static int is_startstring(const unsigned char *string) +{ + if (*string=='\"' || *string=='\'') + return TRUE; /* "..." */ + + if (*string=='!') { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* !"..." */ + if (*string==sc_ctrlchar) { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* !\"..." */ + } /* if */ + } else if (*string==sc_ctrlchar) { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* \"..." */ + if (*string=='!') { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* \!"..." */ + } /* if */ + } /* if */ + + return FALSE; +} + +static const unsigned char *skipstring(const unsigned char *string) +{ + char endquote; + int flags=0; + + while (*string=='!' || *string==sc_ctrlchar) { + if (*string==sc_ctrlchar) + flags=RAWMODE; + string++; + } /* while */ + + endquote=*string; + assert(endquote=='"' || endquote=='\''); + string++; /* skip open quote */ + while (*string!=endquote && *string!='\0') + litchar(&string,flags); + return string; +} + +static const unsigned char *skippgroup(const unsigned char *string) +{ + int nest=0; + char open=*string; + char close; + + switch (open) { + case '(': + close=')'; + break; + case '{': + close='}'; + break; + case '[': + close=']'; + break; + case '<': + close='>'; + break; + default: + assert(0); + close='\0'; /* only to avoid a compiler warning */ + }/* switch */ + + string++; + while (*string!=close || nest>0) { + if (*string==open) + nest++; + else if (*string==close) + nest--; + else if (is_startstring(string)) + string=skipstring(string); + if (*string=='\0') + break; + string++; + } /* while */ + return string; +} + +static char *strdel(char *str,size_t len) +{ + size_t length=strlen(str); + if (len>length) + len=length; + memmove(str, str+len, length-len+1); /* include EOS byte */ + return str; +} + +static char *strins(char *dest,char *src,size_t srclen) +{ + size_t destlen=strlen(dest); + assert(srclen<=strlen(src)); + memmove(dest+srclen, dest, destlen+1);/* include EOS byte */ + memcpy(dest, src, srclen); + return dest; +} + +static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char *substitution) +{ + int prefixlen; + const unsigned char *p,*s,*e; + unsigned char *args[10]; + int match,arg,len; + + memset(args,0,sizeof args); + + /* check the length of the prefix */ + for (prefixlen=0,s=(unsigned char*)pattern; alphanum(*s); prefixlen++,s++) + /* nothing */; + assert(prefixlen>0); + assert(strncmp((char*)line,pattern,prefixlen)==0); + + /* pattern prefix matches; match the rest of the pattern, gather + * the parameters + */ + s=line+prefixlen; + p=(unsigned char*)pattern+prefixlen; + match=TRUE; /* so far, pattern matches */ + while (match && *s!='\0' && *p!='\0') { + if (*p=='%') { + p++; /* skip '%' */ + if (isdigit(*p)) { + arg=*p-'0'; + assert(arg>=0 && arg<=9); + p++; /* skip parameter id */ + assert(*p!='\0'); + /* match the source string up to the character after the digit + * (skipping strings in the process + */ + e=s; + while (*e!=*p && *e!='\0' && *e!='\n') { + if (is_startstring(e)) /* skip strings */ + e=skipstring(e); + else if (strchr("({[",*e)!=NULL) /* skip parenthized groups */ + e=skippgroup(e); + if (*e!='\0') + e++; /* skip non-alphapetic character (or closing quote of + * a string, or the closing paranthese of a group) */ + } /* while */ + /* store the parameter (overrule any earlier) */ + if (args[arg]!=NULL) + free(args[arg]); + len=(int)(e-s); + args[arg]=(unsigned char*)malloc(len+1); + if (args[arg]==NULL) + error(103); /* insufficient memory */ + strlcpy((char*)args[arg],(char*)s,len+1); + /* character behind the pattern was matched too */ + if (*e==*p) { + s=e+1; + } else if (*e=='\n' && *p==';' && *(p+1)=='\0' && !sc_needsemicolon) { + s=e; /* allow a trailing ; in the pattern match to end of line */ + } else { + assert(*e=='\0' || *e=='\n'); + match=FALSE; + s=e; + } /* if */ + p++; + } else { + match=FALSE; + } /* if */ + } else if (*p==';' && *(p+1)=='\0' && !sc_needsemicolon) { + /* source may be ';' or end of the line */ + while (*s<=' ' && *s!='\0') + s++; /* skip white space */ + if (*s!=';' && *s!='\0') + match=FALSE; + p++; /* skip the semicolon in the pattern */ + } else { + cell ch; + /* skip whitespace between two non-alphanumeric characters, except + * for two identical symbols + */ + assert((char*)p>pattern); + if (!alphanum(*p) && *(p-1)!=*p) + while (*s<=' ' && *s!='\0') + s++; /* skip white space */ + ch=litchar(&p,0); /* this increments "p" */ + if (*s!=ch) + match=FALSE; + else + s++; /* this character matches */ + } /* if */ + } /* while */ + + if (match && *p=='\0') { + /* if the last character to match is an alphanumeric character, the + * current character in the source may not be alphanumeric + */ + assert(p>(unsigned char*)pattern); + if (alphanum(*(p-1)) && alphanum(*s)) + match=FALSE; + } /* if */ + + if (match) { + /* calculate the length of the substituted string */ + for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) { + if (*e=='%' && isdigit(*(e+1))) { + arg=*(e+1)-'0'; + assert(arg>=0 && arg<=9); + if (args[arg]!=NULL) + len+=strlen((char*)args[arg]); + else + len+=2; /* copy '%' plus digit */ + e++; /* skip %, digit is skipped later */ + } else { + len++; + } /* if */ + } /* for */ + /* check length of the string after substitution */ + if (strlen((char*)line) + len - (int)(s-line) > buffersize) { + error(75); /* line too long */ + } else { + /* substitute pattern */ + strdel((char*)line,(int)(s-line)); + for (e=(unsigned char*)substitution,s=line; *e!='\0'; e++) { + if (*e=='%' && isdigit(*(e+1))) { + arg=*(e+1)-'0'; + assert(arg>=0 && arg<=9); + if (args[arg]!=NULL) { + strins((char*)s,(char*)args[arg],strlen((char*)args[arg])); + s+=strlen((char*)args[arg]); + } else { + error(236); /* parameter does not exist, incorrect #define pattern */ + strins((char*)s,(char*)e,2); + s+=2; + } /* if */ + e++; /* skip %, digit is skipped later */ + } else { + strins((char*)s,(char*)e,1); + s++; + } /* if */ + } /* for */ + } /* if */ + } /* if */ + + for (arg=0; arg<10; arg++) + if (args[arg]!=NULL) + free(args[arg]); + + return match; +} + +static void substallpatterns(unsigned char *line,int buffersize) +{ + unsigned char *start, *end; + int prefixlen; + stringpair *subst; + + start=line; + while (*start!='\0') { + /* find the start of a prefix (skip all non-alphabetic characters), + * also skip strings + */ + while (!alpha(*start) && *start!='\0') { + /* skip strings */ + if (is_startstring(start)) { + start=(unsigned char *)skipstring(start); + if (*start=='\0') + break; /* abort loop on error */ + } /* if */ + start++; /* skip non-alphapetic character (or closing quote of a string) */ + } /* while */ + if (*start=='\0') + break; /* abort loop on error */ + /* if matching the operator "defined", skip it plus the symbol behind it */ + if (strncmp((char*)start,"defined",7)==0 && *(start+7)<=' ') { + start+=7; /* skip "defined" */ + /* skip white space & parantheses */ + while (*start<=' ' && *start!='\0' || *start=='(') + start++; + /* skip the symbol behind it */ + while (alphanum(*start)) + start++; + /* drop back into the main loop */ + continue; + } /* if */ + /* get the prefix (length), look for a matching definition */ + prefixlen=0; + end=start; + while (alphanum(*end)) { + prefixlen++; + end++; + } /* while */ + assert(prefixlen>0); + subst=find_subst((char*)start,prefixlen); + if (subst!=NULL) { + /* properly match the pattern and substitute */ + if (!substpattern(start,buffersize-(int)(start-line),subst->first,subst->second)) + start=end; /* match failed, skip this prefix */ + /* match succeeded: do not update "start", because the substitution text + * may be matched by other macros + */ + } else { + start=end; /* no macro with this prefix, skip this prefix */ + } /* if */ + } /* while */ +} +#endif + +/* preprocess + * + * Reads a line by readline() into "pline" and performs basic preprocessing: + * deleting comments, skipping lines with false "#if.." code and recognizing + * other compiler directives. There is an indirect recursion: lex() calls + * preprocess() if a new line must be read, preprocess() calls command(), + * which at his turn calls lex() to identify the token. + * + * Global references: lptr (altered) + * pline (altered) + * freading (referred to only) + */ +SC_FUNC void preprocess(void) +{ + int iscommand; + + if (!freading) + return; + do { + readline(pline); + stripcom(pline); /* ??? no need for this when reading back from list file (in the second pass) */ + lptr=pline; /* set "line pointer" to start of the parsing buffer */ + iscommand=command(); + if (iscommand!=CMD_NONE) + errorset(sRESET,0); /* reset error flag ("panic mode") on empty line or directive */ + #if !defined NO_DEFINE + if (iscommand==CMD_NONE) { + assert(lptr!=term_expr); + substallpatterns(pline,sLINEMAX); + lptr=pline; /* reset "line pointer" to start of the parsing buffer */ + } /* if */ + #endif + if (sc_status==statFIRST && sc_listing && freading + && (iscommand==CMD_NONE || iscommand==CMD_EMPTYLINE || iscommand==CMD_DIRECTIVE)) + { + listline++; + if (fline!=listline) { + listline=fline; + setlinedirect(fline); + } /* if */ + if (iscommand==CMD_EMPTYLINE) + pc_writeasm(outf,"\n"); + else + pc_writeasm(outf,(char*)pline); + } /* if */ + } while (iscommand!=CMD_NONE && iscommand!=CMD_TERM && freading); /* enddo */ +} + +static const unsigned char *unpackedstring(const unsigned char *lptr,int flags) +{ + while (*lptr!='\"' && *lptr!='\0') { + if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ + lptr++; + continue; + } /* if */ + litadd(litchar(&lptr,flags | UTF8MODE)); /* litchar() alters "lptr" */ + } /* while */ + litadd(0); /* terminate string */ + return lptr; +} + +static const unsigned char *packedstring(const unsigned char *lptr,int flags) +{ + int i; + ucell val,c; + + i=sizeof(ucell)-(sCHARBITS/8); /* start at most significant byte */ + val=0; + while (*lptr!='\"' && *lptr!='\0') { + if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ + lptr++; + continue; + } /* if */ + c=litchar(&lptr,flags); /* litchar() alters "lptr" */ + if (c>=(ucell)(1 << sCHARBITS)) + error(43); /* character constant exceeds range */ + val |= (c << 8*i); + if (i==0) { + litadd(val); + val=0; + } /* if */ + i=(i+sizeof(ucell)-(sCHARBITS/8)) % sizeof(ucell); + } /* if */ + /* save last code; make sure there is at least one terminating zero character */ + if (i!=(int)(sizeof(ucell)-(sCHARBITS/8))) + litadd(val); /* at least one zero character in "val" */ + else + litadd(0); /* add full cell of zeros */ + return lptr; +} + +/* lex(lexvalue,lexsym) Lexical Analysis + * + * lex() first deletes leading white space, then checks for multi-character + * operators, keywords (including most compiler directives), numbers, + * labels, symbols and literals (literal characters are converted to a number + * and are returned as such). If every check fails, the line must contain + * a single-character operator. So, lex() returns this character. In the other + * case (something did match), lex() returns the number of the token. All + * these tokens have been assigned numbers above 255. + * + * Some tokens have "attributes": + * tNUMBER the value of the number is return in "lexvalue". + * tRATIONAL the value is in IEEE 754 encoding or in fixed point + * encoding in "lexvalue". + * tSYMBOL the first sNAMEMAX characters of the symbol are + * stored in a buffer, a pointer to this buffer is + * returned in "lexsym". + * tLABEL the first sNAMEMAX characters of the label are + * stored in a buffer, a pointer to this buffer is + * returned in "lexsym". + * tSTRING the string is stored in the literal pool, the index + * in the literal pool to this string is stored in + * "lexvalue". + * + * lex() stores all information (the token found and possibly its attribute) + * in global variables. This allows a token to be examined twice. If "_pushed" + * is true, this information is returned. + * + * Global references: lptr (altered) + * fline (referred to only) + * litidx (referred to only) + * _lextok, _lexval, _lexstr + * _pushed + */ + +static int _pushed; +static int _lextok; +static cell _lexval; +static char _lexstr[sLINEMAX+1]; +static int _lexnewline; + +SC_FUNC void lexinit(void) +{ + stkidx=0; /* index for pushstk() and popstk() */ + iflevel=0; /* preprocessor: nesting of "#if" is currently 0 */ + skiplevel=0; /* preprocessor: not currently skipping */ + icomment=0; /* currently not in a multiline comment */ + _pushed=FALSE; /* no token pushed back into lex */ + _lexnewline=FALSE; +} + +char *sc_tokens[] = { + "*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=", + "||", "&&", "==", "!=", "<=", ">=", "<<", ">>>", ">>", "++", "--", + "...", "..", "::", + "assert", "break", "case", "char", "const", "continue", "default", + "defined", "do", "else", "enum", "exit", "for", "forward", "goto", + "if", "native", "new", "operator", "public", "return", "sizeof", + "sleep", "state", "static", "stock", "switch", "tagof", "while", + "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput", + "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma", + "#tryinclude", "#undef", + ";", ";", "-integer value-", "-rational value-", "-identifier-", + "-label-", "-string-" + }; + +SC_FUNC int lex(cell *lexvalue,char **lexsym) +{ + int i,toolong,newline,stringflags; + char **tokptr; + const unsigned char *starttoken; + + if (_pushed) { + _pushed=FALSE; /* reset "_pushed" flag */ + *lexvalue=_lexval; + *lexsym=_lexstr; + return _lextok; + } /* if */ + + _lextok=0; /* preset all values */ + _lexval=0; + _lexstr[0]='\0'; + *lexvalue=_lexval; + *lexsym=_lexstr; + _lexnewline=FALSE; + if (!freading) + return 0; + + newline= (lptr==pline); /* does lptr point to start of line buffer */ + while (*lptr<=' ') { /* delete leading white space */ + if (*lptr=='\0') { + preprocess(); /* preprocess resets "lptr" */ + if (!freading) + return 0; + if (lptr==term_expr) /* special sequence to terminate a pending expression */ + return (_lextok=tENDEXPR); + _lexnewline=TRUE; /* set this after preprocess(), because + * preprocess() calls lex() recursively */ + newline=TRUE; + } else { + lptr+=1; + } /* if */ + } /* while */ + if (newline) { + stmtindent=0; + for (i=0; i<(int)(lptr-pline); i++) + if (pline[i]=='\t' && sc_tabsize>0) + stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize); + else + stmtindent++; + } /* if */ + + i=tFIRST; + tokptr=sc_tokens; + while (i<=tMIDDLE) { /* match multi-character operators */ + if (*lptr==**tokptr && match(*tokptr,FALSE)) { + _lextok=i; + if (pc_docexpr) /* optionally concatenate to documentation string */ + insert_autolist(*tokptr); + return _lextok; + } /* if */ + i+=1; + tokptr+=1; + } /* while */ + while (i<=tLAST) { /* match reserved words and compiler directives */ + if (*lptr==**tokptr && match(*tokptr,TRUE)) { + _lextok=i; + errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/ + if (pc_docexpr) /* optionally concatenate to documentation string */ + insert_autolist(*tokptr); + return _lextok; + } /* if */ + i+=1; + tokptr+=1; + } /* while */ + + starttoken=lptr; /* save start pointer (for concatenating to documentation string) */ + if ((i=number(&_lexval,lptr))!=0) { /* number */ + _lextok=tNUMBER; + *lexvalue=_lexval; + lptr+=i; + } else if ((i=ftoi(&_lexval,lptr))!=0) { + _lextok=tRATIONAL; + *lexvalue=_lexval; + lptr+=i; + } else if (alpha(*lptr)) { /* symbol or label */ + /* Note: only sNAMEMAX characters are significant. The compiler + * generates a warning if a symbol exceeds this length. + */ + _lextok=tSYMBOL; + i=0; + toolong=0; + while (alphanum(*lptr)){ + _lexstr[i]=*lptr; + lptr+=1; + if (i=litmax) { + cell *p; + + litmax+=sDEF_LITMAX; + p=(cell *)realloc(litq,litmax*sizeof(cell)); + if (p==NULL) + error(102,"literal table"); /* literal table overflow (fatal error) */ + litq=p; + } /* if */ +} + +/* litadd + * + * Adds a value at the end of the literal queue. The literal queue is used + * for literal strings used in functions and for initializing array variables. + * + * Global references: litidx (altered) + * litq (altered) + */ +SC_FUNC void litadd(cell value) +{ + chk_grow_litq(); + assert(litidx=0 && pos<=litidx); + memmove(litq+(pos+1),litq+pos,(litidx-pos)*sizeof(cell)); + litidx++; + litq[pos]=value; +} + +/* litchar + * + * Return current literal character and increase the pointer to point + * just behind this literal character. + * + * Note: standard "escape sequences" are suported, but the backslash may be + * replaced by another character; the syntax '\ddd' is supported, + * but ddd must be decimal! + */ +static cell litchar(const unsigned char **lptr,int flags) +{ + cell c=0; + const unsigned char *cptr; + + cptr=*lptr; + if ((flags & RAWMODE)!=0 || *cptr!=sc_ctrlchar) { /* no escape character */ + #if !defined NO_UTF8 + if (sc_is_utf8 && (flags & UTF8MODE)!=0) { + c=get_utf8_char(cptr,&cptr); + assert(c>=0); /* file was already scanned for conformance to UTF-8 */ + } else { + #endif + #if !defined NO_CODEPAGE + c=cp_translate(cptr,&cptr); + #else + c=*cptr; + cptr+=1; + #endif + #if !defined NO_UTF8 + } /* if */ + #endif + } else { + cptr+=1; + if (*cptr==sc_ctrlchar) { + c=*cptr; /* \\ == \ (the escape character itself) */ + cptr+=1; + } else { + switch (*cptr) { + case 'a': /* \a == audible alarm */ + c=7; + cptr+=1; + break; + case 'b': /* \b == backspace */ + c=8; + cptr+=1; + break; + case 'e': /* \e == escape */ + c=27; + cptr+=1; + break; + case 'f': /* \f == form feed */ + c=12; + cptr+=1; + break; + case 'n': /* \n == NewLine character */ + c=10; + cptr+=1; + break; + case 'r': /* \r == carriage return */ + c=13; + cptr+=1; + break; + case 't': /* \t == horizontal TAB */ + c=9; + cptr+=1; + break; + case 'v': /* \v == vertical TAB */ + c=11; + cptr+=1; + break; + case 'x': + cptr+=1; + c=0; + while (ishex(*cptr)) { + if (isdigit(*cptr)) + c=(c<<4)+(*cptr-'0'); + else + c=(c<<4)+(tolower(*cptr)-'a'+10); + cptr++; + } /* while */ + if (*cptr==';') + cptr++; /* swallow a trailing ';' */ + break; + case '\'': /* \' == ' (single quote) */ + case '"': /* \" == " (single quote) */ + case '%': /* \% == % (percent) */ + c=*cptr; + cptr+=1; + break; + default: + if (isdigit(*cptr)) { /* \ddd */ + c=0; + while (*cptr>='0' && *cptr<='9') /* decimal! */ + c=c*10 + *cptr++ - '0'; + if (*cptr==';') + cptr++; /* swallow a trailing ';' */ + } else { + error(27); /* invalid character constant */ + } /* if */ + } /* switch */ + } /* if */ + } /* if */ + *lptr=cptr; + assert(c>=0); + return c; +} + +/* alpha + * + * Test if character "c" is alphabetic ("a".."z"), an underscore ("_") + * or an "at" sign ("@"). The "@" is an extension to standard C. + */ +static int alpha(char c) +{ + return (isalpha(c) || c=='_' || c==PUBLIC_CHAR); +} + +/* alphanum + * + * Test if character "c" is alphanumeric ("a".."z", "0".."9", "_" or "@") + */ +SC_FUNC int alphanum(char c) +{ + return (alpha(c) || isdigit(c)); +} + +/* ishex + * + * Test if character "c" is a hexadecimal digit ("0".."9" or "a".."f"). + */ +SC_FUNC int ishex(char c) +{ + return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); +} + +/* The local variable table must be searched backwards, so that the deepest + * nesting of local variables is searched first. The simplest way to do + * this is to insert all new items at the head of the list. + * In the global list, the symbols are kept in sorted order, so that the + * public functions are written in sorted order. + */ +static symbol *add_symbol(symbol *root,symbol *entry,int sort) +{ + symbol *newsym; + + if (sort) + while (root->next!=NULL && strcmp(entry->name,root->next->name)>0) + root=root->next; + + if ((newsym=(symbol *)malloc(sizeof(symbol)))==NULL) { + error(103); + return NULL; + } /* if */ + memcpy(newsym,entry,sizeof(symbol)); + newsym->next=root->next; + root->next=newsym; + return newsym; +} + +static void free_symbol(symbol *sym) +{ + arginfo *arg; + + /* free all sub-symbol allocated memory blocks, depending on the + * kind of the symbol + */ + assert(sym!=NULL); + if (sym->ident==iFUNCTN) { + /* run through the argument list; "default array" arguments + * must be freed explicitly; the tag list must also be freed */ + assert(sym->dim.arglist!=NULL); + for (arg=sym->dim.arglist; arg->ident!=0; arg++) { + if (arg->ident==iREFARRAY && arg->hasdefault) + free(arg->defvalue.array.data); + else if (arg->ident==iVARIABLE + && ((arg->hasdefault & uSIZEOF)!=0 || (arg->hasdefault & uTAGOF)!=0)) + free(arg->defvalue.size.symname); + assert(arg->tags!=NULL); + free(arg->tags); + } /* for */ + free(sym->dim.arglist); + if (sym->states!=NULL) { + delete_consttable(sym->states); + free(sym->states); + } /* if */ + } else if (sym->ident==iVARIABLE || sym->ident==iARRAY) { + if (sym->states!=NULL) { + delete_consttable(sym->states); + free(sym->states); + } /* if */ + } else if (sym->ident==iCONSTEXPR && (sym->usage & uENUMROOT)==uENUMROOT) { + /* free the constant list of an enum root */ + assert(sym->dim.enumlist!=NULL); + delete_consttable(sym->dim.enumlist); + free(sym->dim.enumlist); + } /* if */ + assert(sym->refer!=NULL); + free(sym->refer); + if (sym->documentation!=NULL) + free(sym->documentation); + free(sym); +} + +SC_FUNC void delete_symbol(symbol *root,symbol *sym) +{ + /* find the symbol and its predecessor + * (this function assumes that you will never delete a symbol that is not + * in the table pointed at by "root") + */ + assert(root!=sym); + while (root->next!=sym) { + root=root->next; + assert(root!=NULL); + } /* while */ + + /* unlink it, then free it */ + root->next=sym->next; + free_symbol(sym); +} + +SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_functions) +{ + symbol *sym,*parent_sym; + constvalue *stateptr; + int mustdelete; + + /* erase only the symbols with a deeper nesting level than the + * specified nesting level */ + while (root->next!=NULL) { + sym=root->next; + if (sym->compoundident) { + case iLABEL: + mustdelete=delete_labels; + break; + case iVARIABLE: + case iARRAY: + /* do not delete global variables if functions are preserved */ + mustdelete=delete_functions; + break; + case iREFERENCE: + /* always delete references (only exist as function parameters) */ + mustdelete=TRUE; + break; + case iREFARRAY: + /* a global iREFARRAY symbol is the return value of a function: delete + * this only if "globals" must be deleted; other iREFARRAY instances + * (locals) are also deleted + */ + mustdelete=delete_functions; + for (parent_sym=sym->parent; parent_sym!=NULL && parent_sym->ident!=iFUNCTN; parent_sym=parent_sym->parent) + assert(parent_sym->ident==iREFARRAY); + assert(parent_sym==NULL || (parent_sym->ident==iFUNCTN && parent_sym->parent==NULL)); + if (parent_sym==NULL || parent_sym->ident!=iFUNCTN) + mustdelete=TRUE; + break; + case iCONSTEXPR: + /* delete constants, except predefined constants */ + mustdelete=delete_functions || (sym->usage & uPREDEF)==0; + break; + case iFUNCTN: + /* optionally preserve globals (variables & functions), but + * NOT native functions + */ + mustdelete=delete_functions || (sym->usage & uNATIVE)!=0; + assert(sym->parent==NULL); + break; + case iARRAYCELL: + case iARRAYCHAR: + case iEXPRESSION: + case iVARARGS: + default: + assert(0); + break; + } /* switch */ + if (mustdelete) { + root->next=sym->next; + free_symbol(sym); + } else { + /* if the function was prototyped, but not implemented in this source, + * mark it as such, so that its use can be flagged + */ + if (sym->ident==iFUNCTN && (sym->usage & uDEFINE)==0) + sym->usage |= uMISSING; + if (sym->ident==iFUNCTN || sym->ident==iVARIABLE || sym->ident==iARRAY) + sym->usage &= ~uDEFINE; /* clear "defined" flag */ + /* set all states as "undefined" too */ + if (sym->states!=NULL) + for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next) + stateptr->value=0; + /* for user defined operators, also remove the "prototyped" flag, as + * user-defined operators *must* be declared before use + */ + if (sym->ident==iFUNCTN && !alpha(*sym->name)) + sym->usage &= ~uPROTOTYPED; + root=sym; /* skip the symbol */ + } /* if */ + } /* if */ +} + +/* The purpose of the hash is to reduce the frequency of a "name" + * comparison (which is costly). There is little interest in avoiding + * clusters in similar names, which is why this function is plain simple. + */ +SC_FUNC uint32_t namehash(const char *name) +{ + const unsigned char *ptr=(const unsigned char *)name; + int len=strlen(name); + if (len==0) + return 0L; + assert(len<256); + return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]); +} + +static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag) +{ + symbol *firstmatch=NULL; + symbol *sym=root->next; + int count=0; + unsigned long hash=namehash(name); + while (sym!=NULL) { + if (hash==sym->hash && strcmp(name,sym->name)==0 /* check name */ + && (sym->parent==NULL || sym->ident==iCONSTEXPR) /* sub-types (hierarchical types) are skipped, except for enum fields */ + && (sym->fnumber<0 || sym->fnumber==fnumber)) /* check file number for scope */ + { + assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */ + if (sym->ident==iFUNCTN + || automaton<0 && sym->states==NULL + || automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton) + { + if (cmptag==NULL) + return sym; /* return first match */ + /* return closest match or first match; count number of matches */ + if (firstmatch==NULL) + firstmatch=sym; + assert(cmptag!=NULL); + if (*cmptag==0) + count++; + if (*cmptag==sym->tag) { + *cmptag=1; /* good match found, set number of matches to 1 */ + return sym; + } /* if */ + } /* if */ + } /* */ + sym=sym->next; + } /* while */ + if (cmptag!=NULL && firstmatch!=NULL) + *cmptag=count; + return firstmatch; +} + +static symbol *find_symbol_child(const symbol *root,const symbol *sym) +{ + symbol *ptr=root->next; + while (ptr!=NULL) { + if (ptr->parent==sym) + return ptr; + ptr=ptr->next; + } /* while */ + return NULL; +} + +/* Adds "bywhom" to the list of referrers of "entry". Typically, + * bywhom will be the function that uses a variable or that calls + * the function. + */ +SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom) +{ + int count; + + assert(bywhom!=NULL); /* it makes no sense to add a "void" referrer */ + assert(entry!=NULL); + assert(entry->refer!=NULL); + + /* see if it is already there */ + for (count=0; countnumrefers && entry->refer[count]!=bywhom; count++) + /* nothing */; + if (countnumrefers) { + assert(entry->refer[count]==bywhom); + return TRUE; + } /* if */ + + /* see if there is an empty spot in the referrer list */ + for (count=0; countnumrefers && entry->refer[count]!=NULL; count++) + /* nothing */; + assert(count <= entry->numrefers); + if (count==entry->numrefers) { + symbol **refer; + int newsize=2*entry->numrefers; + assert(newsize>0); + /* grow the referrer list */ + refer=(symbol**)realloc(entry->refer,newsize*sizeof(symbol*)); + if (refer==NULL) + return FALSE; /* insufficient memory */ + /* initialize the new entries */ + entry->refer=refer; + for (count=entry->numrefers; countrefer[count]=NULL; + count=entry->numrefers; /* first empty spot */ + entry->numrefers=newsize; + } /* if */ + + /* add the referrer */ + assert(entry->refer[count]==NULL); + entry->refer[count]=bywhom; + return TRUE; +} + +SC_FUNC void markusage(symbol *sym,int usage) +{ + assert(sym!=NULL); + sym->usage |= (char)usage; + if ((usage & uWRITTEN)!=0) + sym->lnumber=fline; + /* check if (global) reference must be added to the symbol */ + if ((usage & (uREAD | uWRITTEN))!=0) { + /* only do this for global symbols */ + if (sym->vclass==sGLOBAL) { + /* "curfunc" should always be valid, since statements may not occurs + * outside functions; in the case of syntax errors, however, the + * compiler may arrive through this function + */ + if (curfunc!=NULL) + refer_symbol(sym,curfunc); + } /* if */ + } /* if */ +} + + +/* findglb + * + * Returns a pointer to the global symbol (if found) or NULL (if not found) + */ +SC_FUNC symbol *findglb(const char *name,int filter) +{ + /* find a symbol with a matching automaton first */ + symbol *sym=NULL; + + if (filter>sGLOBAL && sc_curstates>0) { + /* find a symbol whose state list matches the current fsa */ + sym=find_symbol(&glbtab,name,fcurrent,state_getfsa(sc_curstates),NULL); + if (sym!=NULL && sym->ident!=iFUNCTN) { + /* if sym!=NULL, we found a variable in the automaton; now we should + * also verify whether there is an intersection between the symbol's + * state list and the current state list + */ + assert(sym->states!=NULL && sym->states->next!=NULL); + if (!state_conflict_id(sc_curstates,sym->states->next->index)) + sym=NULL; + } /* if */ + } /* if */ + + /* if no symbol with a matching automaton exists, find a variable/function + * that has no state(s) attached to it + */ + if (sym==NULL) + sym=find_symbol(&glbtab,name,fcurrent,-1,NULL); + return sym; +} + +/* findloc + * + * Returns a pointer to the local symbol (if found) or NULL (if not found). + * See add_symbol() how the deepest nesting level is searched first. + */ +SC_FUNC symbol *findloc(const char *name) +{ + return find_symbol(&loctab,name,-1,-1,NULL); +} + +SC_FUNC symbol *findconst(const char *name,int *cmptag) +{ + symbol *sym; + + sym=find_symbol(&loctab,name,-1,-1,cmptag); /* try local symbols first */ + if (sym==NULL || sym->ident!=iCONSTEXPR) /* not found, or not a constant */ + sym=find_symbol(&glbtab,name,fcurrent,-1,cmptag); + if (sym==NULL || sym->ident!=iCONSTEXPR) + return NULL; + assert(sym->parent==NULL || (sym->usage & uENUMFIELD)!=0); + /* ^^^ constants have no hierarchy, but enumeration fields may have a parent */ + return sym; +} + +SC_FUNC symbol *finddepend(const symbol *parent) +{ + symbol *sym; + + sym=find_symbol_child(&loctab,parent); /* try local symbols first */ + if (sym==NULL) /* not found */ + sym=find_symbol_child(&glbtab,parent); + return sym; +} + +/* addsym + * + * Adds a symbol to the symbol table (either global or local variables, + * or global and local constants). + */ +SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage) +{ + symbol entry, **refer; + + /* labels may only be defined once */ + assert(ident!=iLABEL || findloc(name)==NULL); + + /* create an empty referrer list */ + if ((refer=(symbol**)malloc(sizeof(symbol*)))==NULL) { + error(103); /* insufficient memory */ + return NULL; + } /* if */ + *refer=NULL; + + /* first fill in the entry */ + memset(&entry,0,sizeof entry); + strcpy(entry.name,name); + entry.hash=namehash(name); + entry.addr=addr; + entry.codeaddr=code_idx; + entry.vclass=(char)vclass; + entry.ident=(char)ident; + entry.tag=tag; + entry.usage=(char)usage; + entry.fnumber=-1; /* assume global visibility (ignored for local symbols) */ + entry.lnumber=fline; + entry.numrefers=1; + entry.refer=refer; + + /* then insert it in the list */ + if (vclass==sGLOBAL) + return add_symbol(&glbtab,&entry,TRUE); + else + return add_symbol(&loctab,&entry,FALSE); +} + +SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag, + int dim[],int numdim,int idxtag[]) +{ + symbol *sym; + + /* global variables may only be defined once + * One complication is that functions returning arrays declare an array + * with the same name as the function, so the assertion must allow for + * this special case. Another complication is that variables may be + * "redeclared" if they are local to an automaton (and findglb() will find + * the symbol without states if no symbol with states exists). + */ + assert(vclass!=sGLOBAL || (sym=findglb(name,sGLOBAL))==NULL || (sym->usage & uDEFINE)==0 + || sym->ident==iFUNCTN && sym==curfunc + || sym->states==NULL && sc_curstates>0); + + if (ident==iARRAY || ident==iREFARRAY) { + symbol *parent=NULL,*top; + int level; + sym=NULL; /* to avoid a compiler warning */ + for (level=0; leveldim.array.length=dim[level]; + top->dim.array.level=(short)(numdim-level-1); + top->x.tags.index=idxtag[level]; + top->parent=parent; + parent=top; + if (level==0) + sym=top; + } /* for */ + } else { + sym=addsym(name,addr,ident,vclass,tag,uDEFINE); + } /* if */ + return sym; +} + +/* getlabel + * + * Returns te next internal label number. The global variable sc_labnum is + * initialized to zero. + */ +SC_FUNC int getlabel(void) +{ + return sc_labnum++; +} + +/* itoh + * + * Converts a number to a hexadecimal string and returns a pointer to that + * string. This function is NOT re-entrant. + */ +SC_FUNC char *itoh(ucell val) +{ +static char itohstr[30]; + char *ptr; + int i,nibble[16]; /* a 64-bit hexadecimal cell has 16 nibbles */ + int max; + + #if PAWN_CELL_SIZE==16 + max=4; + #elif PAWN_CELL_SIZE==32 + max=8; + #elif PAWN_CELL_SIZE==64 + max=16; + #else + #error Unsupported cell size + #endif + ptr=itohstr; + for (i=0; i>=4; + } /* endfor */ + i=max-1; + while (nibble[i]==0 && i>0) /* search for highest non-zero nibble */ + i-=1; + while (i>=0){ + if (nibble[i]>=10) + *ptr++=(char)('a'+(nibble[i]-10)); + else + *ptr++=(char)('0'+nibble[i]); + i-=1; + } /* while */ + *ptr='\0'; /* and a zero-terminator */ + return itohstr; +} + diff --git a/compiler-init/sc3.c b/compiler-init/sc3.c new file mode 100644 index 00000000..65fec149 --- /dev/null +++ b/compiler-init/sc3.c @@ -0,0 +1,2453 @@ +/* Pawn compiler - Recursive descend expresion parser + * + * Copyright (c) ITB CompuPhase, 1997-2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc3.c 3598 2006-07-04 13:44:04Z thiadmer $ + */ +#include +#include +#include /* for _MAX_PATH */ +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval, + int (*hier)(value*),value *lval); +static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval); +static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval, + char *forcetag,int chkbitwise); +static int plnge1(int (*hier)(value *lval),value *lval); +static void plnge2(void (*oper)(void), + int (*hier)(value *lval), + value *lval1,value *lval2); +static cell calc(cell left,void (*oper)(),cell right,char *boolresult); +static int hier14(value *lval); +static int hier13(value *lval); +static int hier12(value *lval); +static int hier11(value *lval); +static int hier10(value *lval); +static int hier9(value *lval); +static int hier8(value *lval); +static int hier7(value *lval); +static int hier6(value *lval); +static int hier5(value *lval); +static int hier4(value *lval); +static int hier3(value *lval); +static int hier2(value *lval); +static int hier1(value *lval1); +static int primary(value *lval); +static void clear_value(value *lval); +static void callfunction(symbol *sym,value *lval_result,int matchparanthesis); +static int dbltest(void (*oper)(),value *lval1,value *lval2); +static int commutative(void (*oper)()); +static int constant(value *lval); + +static char lastsymbol[sNAMEMAX+1]; /* name of last function/variable */ +static int bitwise_opercount; /* count of bitwise operators in an expression */ +static int decl_heap=0; + +/* Function addresses of binary operators for signed operations */ +static void (*op1[17])(void) = { + os_mult,os_div,os_mod, /* hier3, index 0 */ + ob_add,ob_sub, /* hier4, index 3 */ + ob_sal,os_sar,ou_sar, /* hier5, index 5 */ + ob_and, /* hier6, index 8 */ + ob_xor, /* hier7, index 9 */ + ob_or, /* hier8, index 10 */ + os_le,os_ge,os_lt,os_gt, /* hier9, index 11 */ + ob_eq,ob_ne, /* hier10, index 15 */ +}; +/* These two functions are defined because the functions inc() and dec() in + * SC4.C have a different prototype than the other code generation functions. + * The arrays for user-defined functions use the function pointers for + * identifying what kind of operation is requested; these functions must all + * have the same prototype. As inc() and dec() are special cases already, it + * is simplest to add two "do-nothing" functions. + */ +static void user_inc(void) {} +static void user_dec(void) {} + +/* + * Searches for a binary operator a list of operators. The list is stored in + * the array "list". The last entry in the list should be set to 0. + * + * The index of an operator in "list" (if found) is returned in "opidx". If + * no operator is found, nextop() returns 0. + * + * If an operator is found in the expression, it cannot be used in a function + * call with omitted parantheses. Mark this... + * + * Global references: sc_allowproccall (modified) + */ +static int nextop(int *opidx,int *list) +{ + *opidx=0; + while (*list){ + if (matchtoken(*list)){ + sc_allowproccall=FALSE; + return TRUE; /* found! */ + } else { + list+=1; + *opidx+=1; + } /* if */ + } /* while */ + return FALSE; /* entire list scanned, nothing found */ +} + +SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, + value *lval,int *resulttag) +{ +static char *binoperstr[] = { "*", "/", "%", "+", "-", "", "", "", + "", "", "", "<=", ">=", "<", ">", "==", "!=" }; +static int binoper_savepri[] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, + TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }; +static char *unoperstr[] = { "!", "-", "++", "--" }; +static void (*unopers[])(void) = { lneg, neg, user_inc, user_dec }; + char opername[4] = "", symbolname[sNAMEMAX+1]; + int i,swapparams,savepri,savealt; + int paramspassed; + symbol *sym; + + /* since user-defined operators on untagged operands are forbidden, we have + * a quick exit. + */ + assert(numparam==1 || numparam==2); + if (tag1==0 && (numparam==1 || tag2==0)) + return FALSE; + + savepri=savealt=FALSE; + /* find the name with the operator */ + if (numparam==2) { + if (oper==NULL) { + /* assignment operator: a special case */ + strcpy(opername,"="); + if (lval!=NULL && (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR)) + savealt=TRUE; + } else { + assert( (sizeof binoperstr / sizeof binoperstr[0]) == (sizeof op1 / sizeof op1[0]) ); + for (i=0; iusage & uDEFINE)==0*/) { /* ??? should not check uDEFINE; first pass clears these bits */ + /* check for commutative operators */ + if (tag1==tag2 || oper==NULL || !commutative(oper)) + return FALSE; /* not commutative, cannot swap operands */ + /* if arrived here, the operator is commutative and the tags are different, + * swap tags and try again + */ + assert(numparam==2); /* commutative operator must be a binary operator */ + operator_symname(symbolname,opername,tag2,tag1,numparam,tag1); + swapparams=TRUE; + sym=findglb(symbolname,sGLOBAL); + if (sym==NULL /*|| (sym->usage & uDEFINE)==0*/) + return FALSE; + } /* if */ + + /* check existance and the proper declaration of this function */ + if ((sym->usage & uMISSING)!=0 || (sym->usage & uPROTOTYPED)==0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + if ((sym->usage & uMISSING)!=0) + error(4,symname); /* function not defined */ + if ((sym->usage & uPROTOTYPED)==0) + error(71,symname); /* operator must be declared before use */ + } /* if */ + + /* we don't want to use the redefined operator in the function that + * redefines the operator itself, otherwise the snippet below gives + * an unexpected recursion: + * fixed:operator+(fixed:a, fixed:b) + * return a + b + */ + if (sym==curfunc) + return FALSE; + + /* for increment and decrement operators, the symbol must first be loaded + * (and stored back afterwards) + */ + if (oper==user_inc || oper==user_dec) { + assert(!savepri); + assert(lval!=NULL); + if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR) + pushreg(sPRI); /* save current address in PRI */ + rvalue(lval); /* get the symbol's value in PRI */ + } /* if */ + + assert(!savepri || !savealt); /* either one MAY be set, but not both */ + if (savepri) { + /* the chained comparison operators require that the ALT register is + * unmodified, so we save it here; actually, we save PRI because the normal + * instruction sequence (without user operator) swaps PRI and ALT + */ + pushreg(sPRI); /* right-hand operand is in PRI */ + } else if (savealt) { + /* for the assignment operator, ALT may contain an address at which the + * result must be stored; this address must be preserved accross the + * call + */ + assert(lval!=NULL); /* this was checked earlier */ + assert(lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); /* checked earlier */ + pushreg(sALT); + } /* if */ + + /* push parameters, call the function */ + paramspassed= (oper==NULL) ? 1 : numparam; + switch (paramspassed) { + case 1: + pushreg(sPRI); + break; + case 2: + /* note that 1) a function expects that the parameters are pushed + * in reversed order, and 2) the left operand is in the secondary register + * and the right operand is in the primary register */ + if (swapparams) { + pushreg(sALT); + pushreg(sPRI); + } else { + pushreg(sPRI); + pushreg(sALT); + } /* if */ + break; + default: + assert(0); + } /* switch */ + markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ + pushval((cell)paramspassed*sizeof(cell)); + assert(sym->ident==iFUNCTN); + ffcall(sym,NULL,paramspassed); + if (sc_status!=statSKIP) + markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ + if ((sym->usage & uNATIVE)!=0 && sym->x.lib!=NULL) + sym->x.lib->value += 1; /* increment "usage count" of the library */ + sideeffect=TRUE; /* assume functions carry out a side-effect */ + assert(resulttag!=NULL); + *resulttag=sym->tag; /* save tag of the called function */ + + if (savepri || savealt) + popreg(sALT); /* restore the saved PRI/ALT that into ALT */ + if (oper==user_inc || oper==user_dec) { + assert(lval!=NULL); + if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR) + popreg(sALT); /* restore address (in ALT) */ + store(lval); /* store PRI in the symbol */ + moveto1(); /* make sure PRI is restored on exit */ + } /* if */ + return TRUE; +} + +SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce) +{ + if (formaltag!=actualtag) { + /* if the formal tag is zero and the actual tag is not "fixed", the actual + * tag is "coerced" to zero + */ + if (!allowcoerce || formaltag!=0 || (actualtag & FIXEDTAG)!=0) + return FALSE; + } /* if */ + return TRUE; +} + +/* + * The AMX pseudo-processor has no direct support for logical (boolean) + * operations. These have to be done via comparing and jumping. Since we are + * already jumping through the code, we might as well implement an "early + * drop-out" evaluation (also called "short-circuit"). This conforms to + * standard C: + * + * expr1 || expr2 expr2 will only be evaluated if expr1 is false. + * expr1 && expr2 expr2 will only be evaluated if expr1 is true. + * + * expr1 || expr2 && expr3 expr2 will only be evaluated if expr1 is false + * and expr3 will only be evaluated if expr1 is + * false and expr2 is true. + * + * Code generation for the last example proceeds thus: + * + * evaluate expr1 + * operator || found + * jump to "l1" if result of expr1 not equal to 0 + * evaluate expr2 + * -> operator && found; skip to higher level in hierarchy diagram + * jump to "l2" if result of expr2 equal to 0 + * evaluate expr3 + * jump to "l2" if result of expr3 equal to 0 + * set expression result to 1 (true) + * jump to "l3" + * l2: set expression result to 0 (false) + * l3: + * <- drop back to previous hierarchy level + * jump to "l1" if result of expr2 && expr3 not equal to 0 + * set expression result to 0 (false) + * jump to "l4" + * l1: set expression result to 1 (true) + * l4: + * + */ + +/* Skim over terms adjoining || and && operators + * dropval The value of the expression after "dropping out". An "or" drops + * out when the left hand is TRUE, so dropval must be 1 on "or" + * expressions. + * endval The value of the expression when no expression drops out. In an + * "or" expression, this happens when both the left hand and the + * right hand are FALSE, so endval must be 0 for "or" expressions. + */ +static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval, + int (*hier)(value*),value *lval) +{ + int lvalue,hits,droplab,endlab,opidx; + int allconst,foundop; + cell constval; + int index; + cell cidx; + + stgget(&index,&cidx); /* mark position in code generator */ + hits=FALSE; /* no logical operators "hit" yet */ + allconst=TRUE; /* assume all values "const" */ + constval=0; + droplab=0; /* to avoid a compiler warning */ + for ( ;; ) { + lvalue=plnge1(hier,lval); /* evaluate left expression */ + + allconst= allconst && (lval->ident==iCONSTEXPR); + if (allconst) { + if (hits) { + /* one operator was already found */ + if (testfunc==jmp_ne0) + lval->constval= lval->constval || constval; + else + lval->constval= lval->constval && constval; + } /* if */ + constval=lval->constval; /* save result accumulated so far */ + } /* if */ + + foundop=nextop(&opidx,opstr); + if ((foundop || hits) && (lval->ident==iARRAY || lval->ident==iREFARRAY)) + error(33, lval->sym ? (lval->sym->name ? lval->sym->name : "-unknown") : "-unknown-"); /* array was not indexed in an expression */ + if (foundop) { + if (!hits) { + /* this is the first operator in the list */ + hits=TRUE; + droplab=getlabel(); + } /* if */ + dropout(lvalue,testfunc,droplab,lval); + } else if (hits) { /* no (more) identical operators */ + dropout(lvalue,testfunc,droplab,lval); /* found at least one operator! */ + ldconst(endval,sPRI); + jumplabel(endlab=getlabel()); + setlabel(droplab); + ldconst(dropval,sPRI); + setlabel(endlab); + lval->sym=NULL; + lval->tag=pc_addtag("bool"); /* force tag to be "bool" */ + if (allconst) { + lval->ident=iCONSTEXPR; + lval->constval=constval; + stgdel(index,cidx); /* scratch generated code and calculate */ + } else { + lval->ident=iEXPRESSION; + lval->constval=0; + } /* if */ + return FALSE; + } else { + return lvalue; /* none of the operators in "opstr" were found */ + } /* if */ + + } /* while */ +} + +/* + * Reads into the primary register the variable pointed to by lval if + * plunging through the hierarchy levels detected an lvalue. Otherwise + * if a constant was detected, it is loaded. If there is no constant and + * no lvalue, the primary register must already contain the expression + * result. + * + * After that, the compare routines "jmp_ne0" or "jmp_eq0" are called, which + * compare the primary register against 0, and jump to the "early drop-out" + * label "exit1" if the condition is true. + */ +static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval) +{ + if (lvalue) + rvalue(lval); + else if (lval->ident==iCONSTEXPR) + ldconst(lval->constval,sPRI); + (*testfunc)(exit1); +} + +static void checkfunction(value *lval) +{ + symbol *sym=lval->sym; + + if (sym==NULL || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) + return; /* no known symbol, or not a function result */ + + if ((sym->usage & uDEFINE)!=0) { + /* function is defined, can now check the return value (but make an + * exception for directly recursive functions) + */ + if (sym!=curfunc && (sym->usage & uRETVALUE)==0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + error(209,symname); /* function should return a value */ + } /* if */ + } else { + /* function not yet defined, set */ + sym->usage|=uRETVALUE; /* make sure that a future implementation of + * the function uses "return " */ + } /* if */ +} + +/* + * Plunge to a lower level + */ +static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval, + char *forcetag,int chkbitwise) +{ + int lvalue,opidx; + int count; + value lval2 = {0}; + + lvalue=plnge1(hier,lval); + if (nextop(&opidx,opstr)==0) + return lvalue; /* no operator in "opstr" found */ + if (lvalue) + rvalue(lval); + count=0; + do { + if (chkbitwise && count++>0 && bitwise_opercount!=0) + error(212); + opidx+=opoff; /* add offset to index returned by nextop() */ + plnge2(op1[opidx],hier,lval,&lval2); + if (op1[opidx]==ob_and || op1[opidx]==ob_or) + bitwise_opercount++; + if (forcetag!=NULL) + lval->tag=pc_addtag(forcetag); + } while (nextop(&opidx,opstr)); /* do */ + return FALSE; /* result of expression is not an lvalue */ +} + +/* plnge_rel + * + * Binary plunge to lower level; this is very simular to plnge, but + * it has special code generation sequences for chained operations. + */ +static int plnge_rel(int *opstr,int opoff,int (*hier)(value *lval),value *lval) +{ + int lvalue,opidx; + value lval2={0}; + int count; + + /* this function should only be called for relational operators */ + assert(op1[opoff]==os_le); + lvalue=plnge1(hier,lval); + if (nextop(&opidx,opstr)==0) + return lvalue; /* no operator in "opstr" found */ + if (lvalue) + rvalue(lval); + count=0; + lval->boolresult=TRUE; + do { + /* same check as in plnge(), but "chkbitwise" is always TRUE */ + if (count>0 && bitwise_opercount!=0) + error(212); + if (count>0) { + relop_prefix(); + *lval=lval2; /* copy right hand expression of the previous iteration */ + } /* if */ + opidx+=opoff; + plnge2(op1[opidx],hier,lval,&lval2); + if (count++>0) + relop_suffix(); + } while (nextop(&opidx,opstr)); /* enddo */ + lval->constval=lval->boolresult; + lval->tag=pc_addtag("bool"); /* force tag to be "bool" */ + return FALSE; /* result of expression is not an lvalue */ +} + +/* plnge1 + * + * Unary plunge to lower level + * Called by: skim(), plnge(), plnge2(), plnge_rel(), hier14() and hier13() + */ +static int plnge1(int (*hier)(value *lval),value *lval) +{ + int lvalue,index; + cell cidx; + + stgget(&index,&cidx); /* mark position in code generator */ + lvalue=(*hier)(lval); + if (lval->ident==iCONSTEXPR) + stgdel(index,cidx); /* load constant later */ + return lvalue; +} + +/* plnge2 + * + * Binary plunge to lower level + * Called by: plnge(), plnge_rel(), hier14() and hier1() + */ +static void plnge2(void (*oper)(void), + int (*hier)(value *lval), + value *lval1,value *lval2) +{ + int index; + cell cidx; + + stgget(&index,&cidx); /* mark position in code generator */ + if (lval1->ident==iCONSTEXPR) { /* constant on left side; it is not yet loaded */ + if (plnge1(hier,lval2)) + rvalue(lval2); /* load lvalue now */ + else if (lval2->ident==iCONSTEXPR) + ldconst(lval2->constval<constval<ident==iCONSTEXPR) { /* constant on right side */ + if (commutative(oper)) { /* test for commutative operators */ + value lvaltmp = {0}; + stgdel(index,cidx); /* scratch pushreg() and constant fetch (then + * fetch the constant again */ + ldconst(lval2->constval<constval<ident==iARRAY || lval1->ident==iREFARRAY) { + char *ptr=(lval1->sym!=NULL) ? lval1->sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } else if (lval2->ident==iARRAY || lval2->ident==iREFARRAY) { + char *ptr=(lval2->sym!=NULL) ? lval2->sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } /* if */ + /* ??? ^^^ should do same kind of error checking with functions */ + + /* check whether an "operator" function is defined for the tag names + * (a constant expression cannot be optimized in that case) + */ + if (check_userop(oper,lval1->tag,lval2->tag,2,NULL,&lval1->tag)) { + lval1->ident=iEXPRESSION; + lval1->constval=0; + } else if (lval1->ident==iCONSTEXPR && lval2->ident==iCONSTEXPR) { + /* only constant expression if both constant */ + stgdel(index,cidx); /* scratch generated code and calculate */ + if (!matchtag(lval1->tag,lval2->tag,FALSE)) + error(213); /* tagname mismatch */ + lval1->constval=calc(lval1->constval,oper,lval2->constval,&lval1->boolresult); + } else { + if (!matchtag(lval1->tag,lval2->tag,FALSE)) + error(213); /* tagname mismatch */ + (*oper)(); /* do the (signed) operation */ + lval1->ident=iEXPRESSION; + } /* if */ + } /* if */ +} + +static cell truemodulus(cell a,cell b) +{ + return (a % b + b) % b; +} + +static cell calc(cell left,void (*oper)(),cell right,char *boolresult) +{ + if (oper==ob_or) + return (left | right); + else if (oper==ob_xor) + return (left ^ right); + else if (oper==ob_and) + return (left & right); + else if (oper==ob_eq) + return (left == right); + else if (oper==ob_ne) + return (left != right); + else if (oper==os_le) + return *boolresult &= (char)(left <= right), right; + else if (oper==os_ge) + return *boolresult &= (char)(left >= right), right; + else if (oper==os_lt) + return *boolresult &= (char)(left < right), right; + else if (oper==os_gt) + return *boolresult &= (char)(left > right), right; + else if (oper==os_sar) + return (left >> (int)right); + else if (oper==ou_sar) + return ((ucell)left >> (ucell)right); + else if (oper==ob_sal) + return ((ucell)left << (int)right); + else if (oper==ob_add) + return (left + right); + else if (oper==ob_sub) + return (left - right); + else if (oper==os_mult) + return (left * right); + else if (oper==os_div) + return (left - truemodulus(left,right)) / right; + else if (oper==os_mod) + return truemodulus(left,right); + else + error(29); /* invalid expression, assumed 0 (this should never occur) */ + return 0; +} + +SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult) +{ + int locheap=decl_heap; + value lval={0}; + + if (hier14(&lval)) + rvalue(&lval); + /* scrap any arrays left on the heap */ + assert(decl_heap>=locheap); + modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ + decl_heap=locheap; + + if (lval.ident==iCONSTEXPR && val!=NULL) /* constant expression */ + *val=lval.constval; + if (tag!=NULL) + *tag=lval.tag; + if (symptr!=NULL) + *symptr=lval.sym; + if (chkfuncresult) + checkfunction(&lval); + return lval.ident; +} + +SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state) +{ + char name[sNAMEMAX+1]; + cell val; + char *str; + int fsa,islabel; + + assert(automaton!=NULL); + assert(state!=NULL); + if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL)) + return 0; + + tokeninfo(&val,&str); + assert(strlen(str)index>0); + assert(strlen(str)index==0); + } /* if */ + assert(*automaton!=NULL); + fsa=(*automaton)->index; + + assert(*automaton!=NULL); + *state=state_find(name,fsa); + if (*state==NULL) { + char *fsaname=(*automaton)->name; + if (*fsaname=='\0') + fsaname="

"; + error(87,name,fsaname); /* unknown state for automaton */ + return 0; + } /* if */ + + return 1; +} + +SC_FUNC cell array_totalsize(symbol *sym) +{ + cell length; + + assert(sym!=NULL); + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + length=sym->dim.array.length; + if (sym->dim.array.level > 0) { + cell sublength=array_totalsize(finddepend(sym)); + if (sublength>0) + length=length+length*sublength; + else + length=0; + } /* if */ + return length; +} + +static cell array_levelsize(symbol *sym,int level) +{ + assert(sym!=NULL); + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + assert(level <= sym->dim.array.level); + while (level-- > 0) { + sym=finddepend(sym); + assert(sym!=NULL); + } /* if */ + return sym->dim.array.length; +} + +/* hier14 + * + * Lowest hierarchy level (except for the , operator). + * + * Global references: sc_intest (reffered to only) + * sc_allowproccall (modified) + */ +static int hier14(value *lval1) +{ + int lvalue; + value lval2={0},lval3={0}; + void (*oper)(void); + int tok,level,i; + cell val; + char *st; + int bwcount,leftarray; + cell arrayidx1[sDIMEN_MAX],arrayidx2[sDIMEN_MAX]; /* last used array indices */ + cell *org_arrayidx; + + bwcount=bitwise_opercount; + bitwise_opercount=0; + /* initialize the index arrays with unlikely constant indices; note that + * these indices will only be changed when the array is indexed with a + * constant, and that negative array indices are invalid (so actually, any + * negative value would do). + */ + for (i=0; iarrayidx; /* save current pointer, to reset later */ + if (lval1->arrayidx==NULL) + lval1->arrayidx=arrayidx1; + lvalue=plnge1(hier13,lval1); + if (lval1->ident!=iARRAYCELL && lval1->ident!=iARRAYCHAR) + lval1->arrayidx=NULL; + if (lval1->ident==iCONSTEXPR) /* load constant here */ + ldconst(lval1->constval,sPRI); + tok=lex(&val,&st); + switch (tok) { + case taOR: + oper=ob_or; + break; + case taXOR: + oper=ob_xor; + break; + case taAND: + oper=ob_and; + break; + case taADD: + oper=ob_add; + break; + case taSUB: + oper=ob_sub; + break; + case taMULT: + oper=os_mult; + break; + case taDIV: + oper=os_div; + break; + case taMOD: + oper=os_mod; + break; + case taSHRU: + oper=ou_sar; + break; + case taSHR: + oper=os_sar; + break; + case taSHL: + oper=ob_sal; + break; + case '=': /* simple assignment */ + oper=NULL; + if (sc_intest) + error(211); /* possibly unintended assignment */ + break; + default: + lexpush(); + bitwise_opercount=bwcount; + lval1->arrayidx=org_arrayidx; /* restore array index pointer */ + return lvalue; + } /* switch */ + + /* if we get here, it was an assignment; first check a few special cases + * and then the general */ + if (lval1->ident==iARRAYCHAR) { + /* special case, assignment to packed character in a cell is permitted */ + lvalue=TRUE; + } else if (lval1->ident==iARRAY || lval1->ident==iREFARRAY) { + /* array assignment is permitted too (with restrictions) */ + if (oper) + return error(23); /* array assignment must be simple assigment */ + assert(lval1->sym!=NULL); + if (array_totalsize(lval1->sym)==0) + return error(46,lval1->sym->name); /* unknown array size */ + lvalue=TRUE; + } /* if */ + + /* operand on left side of assignment must be lvalue */ + if (!lvalue) + return error(22); /* must be lvalue */ + /* may not change "constant" parameters */ + assert(lval1->sym!=NULL); + if ((lval1->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + sc_allowproccall=FALSE; /* may no longer use "procedure call" syntax */ + + lval3=*lval1; /* save symbol to enable storage of expresion result */ + lval1->arrayidx=org_arrayidx; /* restore array index pointer */ + if (lval1->ident==iARRAYCELL || lval1->ident==iARRAYCHAR + || lval1->ident==iARRAY || lval1->ident==iREFARRAY) + { + /* if indirect fetch: save PRI (cell address) */ + if (oper) { + pushreg(sPRI); + rvalue(lval1); + } /* if */ + lval2.arrayidx=arrayidx2; + plnge2(oper,hier14,lval1,&lval2); + if (lval2.ident!=iARRAYCELL && lval2.ident!=iARRAYCHAR) + lval2.arrayidx=NULL; + if (oper) + popreg(sALT); + if (!oper && lval3.arrayidx!=NULL && lval2.arrayidx!=NULL + && lval3.ident==lval2.ident && lval3.sym==lval2.sym) + { + int same=TRUE; + assert(lval2.arrayidx==arrayidx2); + for (i=0; iname); /* self-assignment */ + } /* if */ + } else { + if (oper){ + rvalue(lval1); + plnge2(oper,hier14,lval1,&lval2); + } else { + /* if direct fetch and simple assignment: no "push" + * and "pop" needed -> call hier14() directly, */ + if (hier14(&lval2)) + rvalue(&lval2); /* instead of plnge2(). */ + else if (lval2.ident==iVARIABLE) + lval2.ident=iEXPRESSION;/* mark as "rvalue" if it is not an "lvalue" */ + checkfunction(&lval2); + /* check whether lval2 and lval3 (old lval1) refer to the same variable */ + if (lval2.ident==iVARIABLE && lval3.ident==lval2.ident && lval3.sym==lval2.sym) { + assert(lval3.sym!=NULL); + error(226,lval3.sym->name); /* self-assignment */ + } /* if */ + } /* if */ + } /* if */ + /* Array elements are sometimes considered as sub-arrays --when the + * array index is an enumeration field and the enumeration size is greater + * than 1. If the expression on the right side of the assignment is a cell, + * or if an operation is in effect, this does not apply. + */ + leftarray= lval3.ident==iARRAY || lval3.ident==iREFARRAY + || ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR) + && lval3.constval>1 && lval3.sym->dim.array.level==0 + && !oper && (lval2.ident==iARRAY || lval2.ident==iREFARRAY)); + if (leftarray) { + /* Left operand is an array, right operand should be an array variable + * of the same size and the same dimension, an array literal (of the + * same size) or a literal string. For single-dimensional arrays without + * tag for the index, it is permitted to assign a smaller array into a + * larger one (without warning). This is to make it easier to work with + * strings. + */ + int exactmatch=TRUE; + int idxtag=0; + int ltlength=(int)lval3.sym->dim.array.length; + if ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR) + && lval3.constval>0 && lval3.sym->dim.array.level==0) + { + ltlength=(int)lval3.constval; + } /* if */ + if (lval2.ident!=iARRAY && lval2.ident!=iREFARRAY + && (lval2.sym==NULL || lval2.constval<=0)) + error(33,lval3.sym->name); /* array must be indexed */ + if (lval2.sym!=NULL) { + if (lval2.constval==0) { + val=lval2.sym->dim.array.length;/* array variable */ + } else { + val=lval2.constval; + if (lval2.sym->dim.array.level!=0) + error(28,lval2.sym->name); + } /* if */ + level=lval2.sym->dim.array.level; + idxtag=lval2.sym->x.tags.index; + if (level==0 && idxtag==0 && lval3.sym->x.tags.index==0) + exactmatch=FALSE; + } else { + val=lval2.constval; /* literal array */ + level=0; + /* If val is negative, it means that lval2 is a literal string. + * The string array size may be smaller than the destination + * array, provided that the destination array does not have an + * index tag. + */ + if (val<0) { + val=-val; + if (lval3.sym->x.tags.index==0) + exactmatch=FALSE; + } /* if */ + } /* if */ + if (lval3.sym->dim.array.level!=level) + return error(48); /* array dimensions must match */ + else if (ltlengthval || val==0) + return error(47); /* array sizes must match */ + else if (lval3.ident!=iARRAYCELL && !matchtag(lval3.sym->x.tags.index,idxtag,TRUE)) + error(229,(lval2.sym!=NULL) ? lval2.sym->name : lval3.sym->name); /* index tag mismatch */ + if (level>0) { + /* check the sizes of all sublevels too */ + symbol *sym1 = lval3.sym; + symbol *sym2 = lval2.sym; + int i; + assert(sym1!=NULL && sym2!=NULL); + /* ^^^ sym2 must be valid, because only variables can be + * multi-dimensional (there are no multi-dimensional literals), + * sym1 must be valid because it must be an lvalue + */ + assert(exactmatch); + for (i=0; idim.array.length!=sym2->dim.array.length) + error(47); /* array sizes must match */ + else if (!matchtag(sym1->x.tags.index,sym2->x.tags.index,TRUE)) + error(229,sym2->name); /* index tag mismatch */ + } /* for */ + /* get the total size in cells of the multi-dimensional array */ + val=array_totalsize(lval3.sym); + assert(val>0); /* already checked */ + } /* if */ + } else { + /* left operand is not an array, right operand should then not be either */ + if (lval2.ident==iARRAY || lval2.ident==iREFARRAY) + error(6); /* must be assigned to an array */ + } /* if */ + if (leftarray) { + memcopy(val*sizeof(cell)); + } else { + check_userop(NULL,lval2.tag,lval3.tag,2,&lval3,&lval2.tag); + store(&lval3); /* now, store the expression result */ + } /* if */ + if (!oper && !matchtag(lval3.tag,lval2.tag,TRUE)) + error(213); /* tagname mismatch (if "oper", warning already given in plunge2()) */ + if (lval3.sym) + markusage(lval3.sym,uWRITTEN); + sideeffect=TRUE; + bitwise_opercount=bwcount; + lval1->ident=iEXPRESSION; + return FALSE; /* expression result is never an lvalue */ +} + +static int hier13(value *lval) +{ + int lvalue=plnge1(hier12,lval); + if (matchtoken('?')) { + int flab1=getlabel(); + int flab2=getlabel(); + value lval2={0}; + int array1,array2; + + if (lvalue) { + rvalue(lval); + } else if (lval->ident==iCONSTEXPR) { + ldconst(lval->constval,sPRI); + error(lval->constval ? 206 : 205); /* redundant test */ + } /* if */ + jmp_eq0(flab1); /* go to second expression if primary register==0 */ + PUSHSTK_I(sc_allowtags); + sc_allowtags=FALSE; /* do not allow tagnames here (colon is a special token) */ + if (hier13(lval)) + rvalue(lval); + if (lval->ident==iCONSTEXPR) /* load constant here */ + ldconst(lval->constval,sPRI); + sc_allowtags=(short)POPSTK_I(); /* restore */ + jumplabel(flab2); + setlabel(flab1); + needtoken(':'); + if (hier13(&lval2)) + rvalue(&lval2); + if (lval2.ident==iCONSTEXPR) /* load constant here */ + ldconst(lval2.constval,sPRI); + array1= (lval->ident==iARRAY || lval->ident==iREFARRAY); + array2= (lval2.ident==iARRAY || lval2.ident==iREFARRAY); + if (array1 && !array2) { + char *ptr=(lval->sym->name!=NULL) ? lval->sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } else if (!array1 && array2) { + char *ptr=(lval2.sym->name!=NULL) ? lval2.sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } /* if */ + /* ??? if both are arrays, should check dimensions */ + if (!matchtag(lval->tag,lval2.tag,FALSE)) + error(213); /* tagname mismatch ('true' and 'false' expressions) */ + setlabel(flab2); + if (lval->ident==iARRAY) + lval->ident=iREFARRAY; /* iARRAY becomes iREFARRAY */ + else if (lval->ident!=iREFARRAY) + lval->ident=iEXPRESSION; /* iREFARRAY stays iREFARRAY, rest becomes iEXPRESSION */ + return FALSE; /* conditional expression is no lvalue */ + } else { + return lvalue; + } /* if */ +} + +/* the order of the operators in these lists is important and must be + * the same as the order of the operators in the array "op1" + */ +static int list3[] = {'*','/','%',0}; +static int list4[] = {'+','-',0}; +static int list5[] = {tSHL,tSHR,tSHRU,0}; +static int list6[] = {'&',0}; +static int list7[] = {'^',0}; +static int list8[] = {'|',0}; +static int list9[] = {tlLE,tlGE,'<','>',0}; +static int list10[] = {tlEQ,tlNE,0}; +static int list11[] = {tlAND,0}; +static int list12[] = {tlOR,0}; + +static int hier12(value *lval) +{ + return skim(list12,jmp_ne0,1,0,hier11,lval); +} + +static int hier11(value *lval) +{ + return skim(list11,jmp_eq0,0,1,hier10,lval); +} + +static int hier10(value *lval) +{ /* ==, != */ + return plnge(list10,15,hier9,lval,"bool",TRUE); +} /* ^ this variable is the starting index in the op1[] + * array of the operators of this hierarchy level */ + +static int hier9(value *lval) +{ /* <=, >=, <, > */ + return plnge_rel(list9,11,hier8,lval); +} + +static int hier8(value *lval) +{ /* | */ + return plnge(list8,10,hier7,lval,NULL,FALSE); +} + +static int hier7(value *lval) +{ /* ^ */ + return plnge(list7,9,hier6,lval,NULL,FALSE); +} + +static int hier6(value *lval) +{ /* & */ + return plnge(list6,8,hier5,lval,NULL,FALSE); +} + +static int hier5(value *lval) +{ /* <<, >>, >>> */ + return plnge(list5,5,hier4,lval,NULL,FALSE); +} + +static int hier4(value *lval) +{ /* +, - */ + return plnge(list4,3,hier3,lval,NULL,FALSE); +} + +static int hier3(value *lval) +{ /* *, /, % */ + return plnge(list3,0,hier2,lval,NULL,FALSE); +} + +static int hier2(value *lval) +{ + int lvalue,tok; + int tag,paranthese; + cell val; + char *st; + symbol *sym; + int saveresult; + + tok=lex(&val,&st); + switch (tok) { + case tINC: /* ++lval */ + if (!hier2(lval)) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag)) + inc(lval); /* increase variable first */ + rvalue(lval); /* and read the result into PRI */ + sideeffect=TRUE; + return FALSE; /* result is no longer lvalue */ + case tDEC: /* --lval */ + if (!hier2(lval)) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag)) + dec(lval); /* decrease variable first */ + rvalue(lval); /* and read the result into PRI */ + sideeffect=TRUE; + return FALSE; /* result is no longer lvalue */ + case '~': /* ~ (one's complement) */ + if (hier2(lval)) + rvalue(lval); + invert(); /* bitwise NOT */ + lval->constval=~lval->constval; + return FALSE; + case '!': /* ! (logical negate) */ + if (hier2(lval)) + rvalue(lval); + if (check_userop(lneg,lval->tag,0,1,NULL,&lval->tag)) { + lval->ident=iEXPRESSION; + lval->constval=0; + } else { + lneg(); /* 0 -> 1, !0 -> 0 */ + lval->constval=!lval->constval; + lval->tag=pc_addtag("bool"); + } /* if */ + return FALSE; + case '-': /* unary - (two's complement) */ + if (hier2(lval)) + rvalue(lval); + /* make a special check for a constant expression with the tag of a + * rational number, so that we can simple swap the sign of that constant. + */ + if (lval->ident==iCONSTEXPR && lval->tag==sc_rationaltag && sc_rationaltag!=0) { + if (rational_digits==0) { + #if PAWN_CELL_SIZE==32 + float *f = (float *)&lval->constval; + #elif PAWN_CELL_SIZE==64 + double *f = (double *)&lval->constval; + #else + #error Unsupported cell size + #endif + *f= - *f; /* this modifies lval->constval */ + } else { + /* the negation of a fixed point number is just an integer negation */ + lval->constval=-lval->constval; + } /* if */ + } else if (check_userop(neg,lval->tag,0,1,NULL,&lval->tag)) { + lval->ident=iEXPRESSION; + lval->constval=0; + } else { + neg(); /* arithmic negation */ + lval->constval=-lval->constval; + } /* if */ + return FALSE; + case tLABEL: /* tagname override */ + tag=pc_addtag(st); + lval->cmptag=tag; + lvalue=hier2(lval); + lval->tag=tag; + return lvalue; + case tDEFINED: + paranthese=0; + while (matchtoken('(')) + paranthese++; + tok=lex(&val,&st); + if (tok!=tSYMBOL) + return error(20,st); /* illegal symbol name */ + sym=findloc(st); + if (sym==NULL) + sym=findglb(st,sSTATEVAR); + if (sym!=NULL && sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) + sym=NULL; /* symbol is not a function, it is in the table, but not "defined" */ + val= (sym!=NULL); + if (!val && find_subst(st,strlen(st))!=NULL) + val=1; + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval= val; + lval->tag=pc_addtag("bool"); + ldconst(lval->constval,sPRI); + while (paranthese--) + needtoken(')'); + return FALSE; + case tSIZEOF: + paranthese=0; + while (matchtoken('(')) + paranthese++; + tok=lex(&val,&st); + if (tok!=tSYMBOL) + return error(20,st); /* illegal symbol name */ + sym=findloc(st); + if (sym==NULL) + sym=findglb(st,sSTATEVAR); + if (sym==NULL) + return error(17,st); /* undefined symbol */ + if (sym->ident==iCONSTEXPR) + error(39); /* constant symbol has no size */ + else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) + error(72); /* "function" symbol has no size */ + else if ((sym->usage & uDEFINE)==0) + return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval=1; /* preset */ + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + int level; + symbol *idxsym=NULL; + symbol *subsym=sym; + for (level=0; matchtoken('['); level++) { + idxsym=NULL; + if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) { + char *idxname; + int cmptag=subsym->x.tags.index; + tokeninfo(&val,&idxname); + if ((idxsym=findconst(idxname,&cmptag))==NULL) + error(80,idxname); /* unknown symbol, or non-constant */ + else if (cmptag>1) + error(91,idxname); /* ambiguous constant */ + } /* if */ + needtoken(']'); + if (subsym!=NULL) + subsym=finddepend(subsym); + } /* for */ + if (level>sym->dim.array.level+1) + error(28,sym->name); /* invalid subscript */ + else if (level==sym->dim.array.level+1) + lval->constval= (idxsym!=NULL && idxsym->dim.array.length>0) ? idxsym->dim.array.length : 1; + else + lval->constval=array_levelsize(sym,level); + if (lval->constval==0 && strchr((char *)lptr,PREPROC_TERM)==NULL) + error(224,st); /* indeterminate array size in "sizeof" expression */ + } /* if */ + ldconst(lval->constval,sPRI); + while (paranthese--) + needtoken(')'); + return FALSE; + case tTAGOF: + paranthese=0; + while (matchtoken('(')) + paranthese++; + tok=lex(&val,&st); + if (tok!=tSYMBOL && tok!=tLABEL) + return error(20,st); /* illegal symbol name */ + if (tok==tLABEL) { + constvalue *tagsym=find_constval(&tagname_tab,st,0); + tag=(int)((tagsym!=NULL) ? tagsym->value : 0); + } else { + sym=findloc(st); + if (sym==NULL) + sym=findglb(st,sSTATEVAR); + if (sym==NULL) + return error(17,st); /* undefined symbol */ + if ((sym->usage & uDEFINE)==0) + return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ + tag=sym->tag; + } /* if */ + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + int level; + symbol *idxsym=NULL; + symbol *subsym=sym; + for (level=0; matchtoken('['); level++) { + idxsym=NULL; + if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) { + char *idxname; + int cmptag=subsym->x.tags.index; + tokeninfo(&val,&idxname); + if ((idxsym=findconst(idxname,&cmptag))==NULL) + error(80,idxname); /* unknown symbol, or non-constant */ + else if (cmptag>1) + error(91,idxname); /* ambiguous constant */ + } /* if */ + needtoken(']'); + if (subsym!=NULL) + subsym=finddepend(subsym); + } /* for */ + if (level>sym->dim.array.level+1) + error(28,sym->name); /* invalid subscript */ + else if (level==sym->dim.array.level+1 && idxsym!=NULL) + tag= idxsym->x.tags.index; + } /* if */ + exporttag(tag); + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval=tag | PUBLICTAG; + ldconst(lval->constval,sPRI); + while (paranthese--) + needtoken(')'); + return FALSE; + case tSTATE: { + constvalue *automaton; + constvalue *state; + if (sc_getstateid(&automaton,&state)) { + assert(automaton!=NULL); + assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0); + loadreg(automaton->value,sALT); + assert(state!=NULL); + ldconst(state->value,sPRI); + ob_eq(); + clear_value(lval); + lval->ident=iEXPRESSION; + lval->tag=pc_addtag("bool"); + } /* if */ + return FALSE; + } /* case */ + default: + lexpush(); + lvalue=hier1(lval); + /* check for postfix operators */ + if (matchtoken(';')) { + /* Found a ';', do not look further for postfix operators */ + lexpush(); /* push ';' back after successful match */ + return lvalue; + } else if (matchtoken(tTERM)) { + /* Found a newline that ends a statement (this is the case when + * semicolons are optional). Note that an explicit semicolon was + * handled above. This case is similar, except that the token must + * not be pushed back. + */ + return lvalue; + } else { + tok=lex(&val,&st); + switch (tok) { + case tINC: /* lval++ */ + if (!lvalue) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + /* on incrementing array cells, the address in PRI must be saved for + * incremening the value, whereas the current value must be in PRI + * on exit. + */ + saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); + if (saveresult) + pushreg(sPRI); /* save address in PRI */ + rvalue(lval); /* read current value into PRI */ + if (saveresult) + swap1(); /* save PRI on the stack, restore address in PRI */ + if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag)) + inc(lval); /* increase variable afterwards */ + if (saveresult) + popreg(sPRI); /* restore PRI (result of rvalue()) */ + sideeffect=TRUE; + return FALSE; /* result is no longer lvalue */ + case tDEC: /* lval-- */ + if (!lvalue) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); + if (saveresult) + pushreg(sPRI); /* save address in PRI */ + rvalue(lval); /* read current value into PRI */ + if (saveresult) + swap1(); /* save PRI on the stack, restore address in PRI */ + if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag)) + dec(lval); /* decrease variable afterwards */ + if (saveresult) + popreg(sPRI); /* restore PRI (result of rvalue()) */ + sideeffect=TRUE; + return FALSE; + case tCHAR: /* char (compute required # of cells */ + if (lval->ident==iCONSTEXPR) { + lval->constval *= sCHARBITS/8; /* from char to bytes */ + lval->constval = (lval->constval + sizeof(cell)-1) / sizeof(cell); + } else { + if (lvalue) + rvalue(lval); /* fetch value if not already in PRI */ + char2addr(); /* from characters to bytes */ + addconst(sizeof(cell)-1); /* make sure the value is rounded up */ + addr2cell(); /* truncate to number of cells */ + } /* if */ + return FALSE; + default: + lexpush(); + return lvalue; + } /* switch */ + } /* if */ + } /* switch */ +} + +/* hier1 + * + * The highest hierarchy level: it looks for pointer and array indices + * and function calls. + * Generates code to fetch a pointer value if it is indexed and code to + * add to the pointer value or the array address (the address is already + * read at primary()). It also generates code to fetch a function address + * if that hasn't already been done at primary() (check lval[4]) and calls + * callfunction() to call the function. + */ +static int hier1(value *lval1) +{ + int lvalue,index,tok,symtok; + cell val,cidx; + value lval2={0}; + char *st; + char close; + symbol *sym; + symbol dummysymbol,*cursym; /* for changing the index tags in case of enumerated pseudo-arrays */ + + lvalue=primary(lval1); + symtok=tokeninfo(&val,&st); /* get token read by primary() */ + cursym=lval1->sym; +restart: + sym=cursym; + if (matchtoken('[') || matchtoken('{') || matchtoken('(')) { + tok=tokeninfo(&val,&st); /* get token read by matchtoken() */ + if (sym==NULL && symtok!=tSYMBOL) { + /* we do not have a valid symbol and we appear not to have read a valid + * symbol name (so it is unlikely that we would have read a name of an + * undefined symbol) */ + error(29); /* expression error, assumed 0 */ + lexpush(); /* analyse '(', '{' or '[' again later */ + return FALSE; + } /* if */ + if (tok=='[' || tok=='{') { /* subscript */ + close = (char)((tok=='[') ? ']' : '}'); + if (sym==NULL) { /* sym==NULL if lval is a constant or a literal */ + error(28,""); /* cannot subscript */ + needtoken(close); + return FALSE; + } else if (sym->ident!=iARRAY && sym->ident!=iREFARRAY){ + error(28,sym->name); /* cannot subscript, variable is not an array */ + needtoken(close); + return FALSE; + } else if (sym->dim.array.level>0 && close!=']') { + error(51); /* invalid subscript, must use [ ] */ + needtoken(close); + return FALSE; + } /* if */ + /* set the tag to match (enumeration fields as indices) */ + lval2.cmptag=sym->x.tags.index; + stgget(&index,&cidx); /* mark position in code generator */ + pushreg(sPRI); /* save base address of the array */ + if (hier14(&lval2)) /* create expression for the array index */ + rvalue(&lval2); + if (lval2.ident==iARRAY || lval2.ident==iREFARRAY) + error(33,lval2.sym->name); /* array must be indexed */ + needtoken(close); + if (!matchtag(sym->x.tags.index,lval2.tag,TRUE)) + error(213); + if (lval2.ident==iCONSTEXPR) { /* constant expression */ + stgdel(index,cidx); /* scratch generated code */ + if (lval1->arrayidx!=NULL) { /* keep constant index, for checking */ + assert(sym->dim.array.level>=0 && sym->dim.array.levelarrayidx[sym->dim.array.level]=lval2.constval; + } /* if */ + if (close==']') { + /* normal array index */ + if (lval2.constval<0 || sym->dim.array.length!=0 && sym->dim.array.length<=lval2.constval) + error(32,sym->name); /* array index out of bounds */ + if (lval2.constval!=0) { + /* don't add offsets for zero subscripts */ + #if PAWN_CELL_SIZE==16 + ldconst(lval2.constval<<1,sALT); + #elif PAWN_CELL_SIZE==32 + ldconst(lval2.constval<<2,sALT); + #elif PAWN_CELL_SIZE==64 + ldconst(lval2.constval<<3,sALT); + #else + #error Unsupported cell size + #endif + ob_add(); + } /* if */ + } else { + /* character index */ + if (lval2.constval<0 || sym->dim.array.length!=0 + && sym->dim.array.length*((8*sizeof(cell))/sCHARBITS)<=(ucell)lval2.constval) + error(32,sym->name); /* array index out of bounds */ + if (lval2.constval!=0) { + /* don't add offsets for zero subscripts */ + #if sCHARBITS==16 + ldconst(lval2.constval<<1,sALT);/* 16-bit character */ + #else + ldconst(lval2.constval,sALT); /* 8-bit character */ + #endif + ob_add(); + } /* if */ + charalign(); /* align character index into array */ + } /* if */ + /* if the array index is a field from an enumeration, get the tag name + * from the field and save the size of the field too. + */ + assert(lval2.sym==NULL || lval2.sym->dim.array.level==0); + if (lval2.sym!=NULL && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) { + lval1->tag=lval2.sym->x.tags.index; + lval1->constval=lval2.sym->dim.array.length; + } /* if */ + } else { + /* array index is not constant */ + lval1->arrayidx=NULL; /* reset, so won't be checked */ + if (close==']') { + if (sym->dim.array.length!=0) + ffbounds(sym->dim.array.length-1); /* run time check for array bounds */ + cell2addr(); /* normal array index */ + } else { + if (sym->dim.array.length!=0) + ffbounds(sym->dim.array.length*(32/sCHARBITS)-1); + char2addr(); /* character array index */ + } /* if */ + popreg(sALT); + ob_add(); /* base address was popped into secondary register */ + if (close!=']') + charalign(); /* align character index into array */ + } /* if */ + /* the indexed item may be another array (multi-dimensional arrays) */ + assert(cursym==sym && sym!=NULL); /* should still be set */ + if (sym->dim.array.level>0) { + assert(close==']'); /* checked earlier */ + assert(cursym==lval1->sym); + /* read the offset to the subarray and add it to the current address */ + lval1->ident=iARRAYCELL; + pushreg(sPRI); /* the optimizer makes this to a MOVE.alt */ + rvalue(lval1); + popreg(sALT); + ob_add(); + /* adjust the "value" structure and find the referenced array */ + lval1->ident=iREFARRAY; + lval1->sym=finddepend(sym); + assert(lval1->sym!=NULL); + assert(lval1->sym->dim.array.level==sym->dim.array.level-1); + cursym=lval1->sym; + /* try to parse subsequent array indices */ + lvalue=FALSE; /* for now, a iREFARRAY is no lvalue */ + goto restart; + } /* if */ + assert(sym->dim.array.level==0); + /* set type to fetch... INDIRECTLY */ + lval1->ident= (char)((close==']') ? iARRAYCELL : iARRAYCHAR); + /* if the array index is a field from an enumeration, get the tag name + * from the field and save the size of the field too. Otherwise, the + * tag is the one from the array symbol. + */ + if (lval2.ident==iCONSTEXPR && lval2.sym!=NULL + && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) + { + lval1->tag=lval2.sym->x.tags.index; + lval1->constval=lval2.sym->dim.array.length; + if (lval2.tag==sym->x.tags.index && lval1->constval>1 && matchtoken('[')) { + /* an array indexed with an enumeration field may be considered a sub-array */ + lexpush(); + lvalue=FALSE; /* for now, a iREFARRAY is no lvalue */ + lval1->ident=iREFARRAY; + /* initialize a dummy symbol, which is a copy of the current symbol, + * but with an adjusted index tag + */ + assert(sym!=NULL); + dummysymbol=*sym; + /* get the tag of the root of the enumeration */ + assert(lval2.sym!=NULL); + dummysymbol.x.tags.index=lval2.sym->x.tags.field; + dummysymbol.dim.array.length=lval2.sym->dim.array.length; + cursym=&dummysymbol; + /* recurse */ + goto restart; + } /* if */ + } else { + assert(sym!=NULL); + if (cursym!=&dummysymbol) + lval1->tag=sym->tag; + lval1->constval=0; + } /* if */ + /* a cell in an array is an lvalue, a character in an array is not + * always a *valid* lvalue */ + return TRUE; + } else { /* tok=='(' -> function(...) */ + assert(tok=='('); + if (sym==NULL + || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) + { + if (sym==NULL && sc_status==statFIRST) { + /* could be a "use before declaration"; in that case, create a stub + * function so that the usage can be marked. + */ + sym=fetchfunc(lastsymbol,0); + if (sym==NULL) + error(103); /* insufficient memory */ + markusage(sym,uREAD); + } else { + return error(12); /* invalid function call */ + } /* if */ + } else if ((sym->usage & uMISSING)!=0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + error(4,symname); /* function not defined */ + } /* if */ + callfunction(sym,lval1,TRUE); + return FALSE; /* result of function call is no lvalue */ + } /* if */ + } /* if */ + if (sym!=NULL && lval1->ident==iFUNCTN) { + assert(sym->ident==iFUNCTN); + if (sc_allowproccall) { + callfunction(sym,lval1,FALSE); + } else { + lval1->sym=NULL; + lval1->ident=iEXPRESSION; + lval1->constval=0; + lval1->tag=0; + error(76); /* invalid function call, or syntax error */ + } /* if */ + return FALSE; + } /* if */ + return lvalue; +} + +/* primary + * + * Returns 1 if the operand is an lvalue (everything except arrays, functions + * constants and -of course- errors). + * Generates code to fetch the address of arrays. Code for constants is + * already generated by constant(). + * This routine first clears the entire lval array (all fields are set to 0). + * + * Global references: sc_intest (may be altered, but restored upon termination) + */ +static int primary(value *lval) +{ + char *st; + int lvalue,tok; + cell val; + symbol *sym; + + if (matchtoken('(')){ /* sub-expression - (expression,...) */ + PUSHSTK_I(sc_intest); + PUSHSTK_I(sc_allowtags); + + sc_intest=FALSE; /* no longer in "test" expression */ + sc_allowtags=TRUE; /* allow tagnames to be used in parenthesized expressions */ + sc_allowproccall=FALSE; + do + lvalue=hier14(lval); + while (matchtoken(',')); + needtoken(')'); + lexclr(FALSE); /* clear lex() push-back, it should have been + * cleared already by needtoken() */ + sc_allowtags=(short)POPSTK_I(); + sc_intest=(short)POPSTK_I(); + return lvalue; + } /* if */ + + clear_value(lval); /* clear lval */ + tok=lex(&val,&st); + if (tok==tSYMBOL) { + /* lastsymbol is char[sNAMEMAX+1], lex() should have truncated any symbol + * to sNAMEMAX significant characters */ + assert(strlen(st)ident==iLABEL) { + error(29); /* expression error, assumed 0 */ + ldconst(0,sPRI); /* load 0 */ + return FALSE; /* return 0 for labels (expression error) */ + } /* if */ + lval->sym=sym; + lval->ident=sym->ident; + lval->tag=sym->tag; + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + address(sym,sPRI); /* get starting address in primary register */ + return FALSE; /* return 0 for array (not lvalue) */ + } else { + return TRUE; /* return 1 if lvalue (not label or array) */ + } /* if */ + } /* if */ + /* now try a global variable */ + if ((sym=findglb(st,sSTATEVAR))!=0) { + if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { + /* if the function is only in the table because it was inserted as a + * stub in the first pass (i.e. it was "used" but never declared or + * implemented, issue an error + */ + if ((sym->usage & uPROTOTYPED)==0) + error(17,st); + } else { + if ((sym->usage & uDEFINE)==0) + error(17,st); + lval->sym=sym; + lval->ident=sym->ident; + lval->tag=sym->tag; + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + address(sym,sPRI); /* get starting address in primary register */ + return FALSE; /* return 0 for array (not lvalue) */ + } else { + return TRUE; /* return 1 if lvalue (not function or array) */ + } /* if */ + } /* if */ + } else { + if (!sc_allowproccall) + return error(17,st); /* undefined symbol */ + /* an unknown symbol, but used in a way compatible with the "procedure + * call" syntax. So assume that the symbol refers to a function. + */ + assert(sc_status==statFIRST); + sym=fetchfunc(st,0); + if (sym==NULL) + error(103); /* insufficient memory */ + } /* if */ + assert(sym!=NULL); + assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); + lval->sym=sym; + lval->ident=sym->ident; + lval->tag=sym->tag; + return FALSE; /* return 0 for function (not an lvalue) */ + } /* if */ + lexpush(); /* push the token, it is analyzed by constant() */ + if (constant(lval)==0) { + error(29); /* expression error, assumed 0 */ + ldconst(0,sPRI); /* load 0 */ + } /* if */ + return FALSE; /* return 0 for constants (or errors) */ +} + +static void clear_value(value *lval) +{ + lval->sym=NULL; + lval->constval=0L; + lval->tag=0; + lval->ident=0; + lval->boolresult=FALSE; + /* do not clear lval->arrayidx, it is preset in hier14() */ + /* do not clear lval->cmptag */ +} + +static void setdefarray(cell *string,cell size,cell array_sz,cell *dataaddr,int fconst) +{ + /* The routine must copy the default array data onto the heap, as to avoid + * that a function can change the default value. An optimization is that + * the default array data is "dumped" into the data segment only once (on the + * first use). + */ + assert(string!=NULL); + assert(size>0); + /* check whether to dump the default array */ + assert(dataaddr!=NULL); + if (sc_status==statWRITE && *dataaddr<0) { + int i; + *dataaddr=(litidx+glb_declared)*sizeof(cell); + for (i=0; i=size); + modheap((int)array_sz*sizeof(cell)); + /* ??? should perhaps fill with zeros first */ + memcopy(size*sizeof(cell)); + moveto1(); + } /* if */ +} + +static int findnamedarg(arginfo *arg,char *name) +{ + int i; + + for (i=0; arg[i].ident!=0 && arg[i].ident!=iVARARGS; i++) + if (strcmp(arg[i].name,name)==0) + return i; + return -1; +} + +static int checktag(int tags[],int numtags,int exprtag) +{ + int i; + + assert(tags!=0); + assert(numtags>0); + for (i=0; iident=iEXPRESSION; /* preset, may be changed later */ + lval_result->constval=0; + lval_result->tag=sym->tag; + /* check whether this is a function that returns an array */ + symret=finddepend(sym); + assert(symret==NULL || symret->ident==iREFARRAY); + if (symret!=NULL) { + int retsize; + /* allocate space on the heap for the array, and pass the pointer to the + * reserved memory block as a hidden parameter + */ + retsize=(int)array_totalsize(symret); + assert(retsize>0); + modheap(retsize*sizeof(cell));/* address is in ALT */ + pushreg(sALT); /* pass ALT as the last (hidden) parameter */ + decl_heap+=retsize; + /* also mark the ident of the result as "array" */ + lval_result->ident=iREFARRAY; + lval_result->sym=symret; + } /* if */ + locheap=decl_heap; + + nesting++; + assert(nest_stkusage>=0); + #if !defined NDEBUG + if (nesting==1) + assert(nest_stkusage==0); + #endif + sc_allowproccall=FALSE; /* parameters may not use procedure call syntax */ + + if ((sym->flags & flgDEPRICATED)!=0) { + char *ptr= (sym->documentation!=NULL) ? sym->documentation : ""; + error(234,sym->name,ptr); /* depricated (probably a native function) */ + } /* if */ + + /* run through the arguments */ + arg=sym->dim.arglist; + assert(arg!=NULL); + stgmark(sSTARTREORDER); + memset(arglist,ARG_UNHANDLED,sizeof arglist); + if (matchparanthesis) { + /* Opening brace was already parsed, if closing brace follows, this + * call passes no parameters. + */ + close=matchtoken(')'); + } else { + /* When we find an end of line here, it may be a function call passing + * no parameters, or it may be that the first parameter is on a line + * below. But as a parameter can be anything, this is difficult to check. + * The only simple check that we have is the use of "named parameters". + */ + close=matchtoken(tTERM); + if (close) { + close=!matchtoken('.'); + if (!close) + lexpush(); /* reset the '.' */ + } /* if */ + } /* if */ + if (!close) { + do { + if (matchtoken('.')) { + namedparams=TRUE; + if (needtoken(tSYMBOL)) + tokeninfo(&lexval,&lexstr); + else + lexstr=""; + argpos=findnamedarg(arg,lexstr); + if (argpos<0) { + error(17,lexstr); /* undefined symbol */ + break; /* exit loop, argpos is invalid */ + } /* if */ + needtoken('='); + argidx=argpos; + } else { + if (namedparams) + error(44); /* positional parameters must precede named parameters */ + argpos=nargs; + } /* if */ + /* the number of arguments this was already checked at the declaration + * of the function; check it again for functions with a variable + * argument list + */ + if (argpos>=sMAXARGS) + error(45); /* too many function arguments */ + stgmark((char)(sEXPRSTART+argpos));/* mark beginning of new expression in stage */ + if (arglist[argpos]!=ARG_UNHANDLED) + error(58); /* argument already set */ + if (matchtoken('_')) { + arglist[argpos]=ARG_IGNORED; /* flag argument as "present, but ignored" */ + if (arg[argidx].ident==0 || arg[argidx].ident==iVARARGS) { + error(202); /* argument count mismatch */ + } else if (!arg[argidx].hasdefault) { + error(34,nargs+1); /* argument has no default value */ + } /* if */ + if (arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS) + argidx++; + /* The rest of the code to handle default values is at the bottom + * of this routine where default values for unspecified parameters + * are (also) handled. Note that above, the argument is flagged as + * ARG_IGNORED. + */ + } else { + arglist[argpos]=ARG_DONE; /* flag argument as "present" */ + if (arg[argidx].numtags==1) /* set the expected tag, if any */ + lval.cmptag=arg[argidx].tags[0]; + lvalue=hier14(&lval); + assert(sc_status==statFIRST || arg[argidx].tags!=NULL); + switch (arg[argidx].ident) { + case 0: + error(202); /* argument count mismatch */ + break; + case iVARARGS: + /* always pass by reference */ + if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) { + assert(lval.sym!=NULL); + if ((lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) { + /* treat a "const" variable passed to a function with a non-const + * "variable argument list" as a constant here */ + if (!lvalue) { + error(22); /* need lvalue */ + } else { + rvalue(&lval); /* get value in PRI */ + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + } else if (lvalue) { + address(lval.sym,sPRI); + } else { + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + } else if (lval.ident==iCONSTEXPR || lval.ident==iEXPRESSION + || lval.ident==iARRAYCHAR) + { + /* fetch value if needed */ + if (lval.ident==iARRAYCHAR) + rvalue(&lval); + /* allocate a cell on the heap and store the + * value (already in PRI) there */ + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + /* ??? handle const array passed by reference */ + /* otherwise, the address is already in PRI */ + if (lval.sym!=NULL) + markusage(lval.sym,uWRITTEN); + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + break; + case iVARIABLE: + if (lval.ident==iLABEL || lval.ident==iFUNCTN || lval.ident==iREFFUNC + || lval.ident==iARRAY || lval.ident==iREFARRAY) + error(35,argidx+1); /* argument type mismatch */ + if (lvalue) + rvalue(&lval); /* get value (direct or indirect) */ + /* otherwise, the expression result is already in PRI */ + assert(arg[argidx].numtags>0); + check_userop(NULL,lval.tag,arg[argidx].tags[0],2,NULL,&lval.tag); + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + argidx++; /* argument done */ + break; + case iREFERENCE: + if (!lvalue || lval.ident==iARRAYCHAR) + error(35,argidx+1); /* argument type mismatch */ + if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) + error(35,argidx+1); /* argument type mismatch */ + if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) { + if (lvalue) { + assert(lval.sym!=NULL); + address(lval.sym,sPRI); + } else { + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + } /* if */ + /* otherwise, the address is already in PRI */ + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + argidx++; /* argument done */ + if (lval.sym!=NULL) + markusage(lval.sym,uWRITTEN); + break; + case iREFARRAY: + if (lval.ident!=iARRAY && lval.ident!=iREFARRAY + && lval.ident!=iARRAYCELL) + { + error(35,argidx+1); /* argument type mismatch */ + break; + } /* if */ + if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) + error(35,argidx+1); /* argument type mismatch */ + /* Verify that the dimensions match with those in arg[argidx]. + * A literal array always has a single dimension. + * An iARRAYCELL parameter is also assumed to have a single dimension. + */ + if (lval.sym==NULL || lval.ident==iARRAYCELL) { + if (arg[argidx].numdim!=1) { + error(48); /* array dimensions must match */ + } else if (arg[argidx].dim[0]!=0) { + assert(arg[argidx].dim[0]>0); + if (lval.ident==iARRAYCELL) { + error(47); /* array sizes must match */ + } else { + assert(lval.constval!=0); /* literal array must have a size */ + /* A literal array must have exactly the same size as the + * function argument; a literal string may be smaller than + * the function argument. + */ + if (lval.constval>0 && arg[argidx].dim[0]!=lval.constval + || lval.constval<0 && arg[argidx].dim[0] < -lval.constval) + error(47); /* array sizes must match */ + } /* if */ + } /* if */ + if (lval.ident!=iARRAYCELL) { + /* save array size, for default values with uSIZEOF flag */ + cell array_sz=lval.constval; + assert(array_sz!=0);/* literal array must have a size */ + if (array_sz<0) + array_sz= -array_sz; + append_constval(&arrayszlst,arg[argidx].name,array_sz,0); + } /* if */ + } else { + symbol *sym=lval.sym; + short level=0; + assert(sym!=NULL); + if (sym->dim.array.level+1!=arg[argidx].numdim) + error(48); /* array dimensions must match */ + /* the lengths for all dimensions must match, unless the dimension + * length was defined at zero (which means "undefined") + */ + while (sym->dim.array.level>0) { + assert(leveldim.array.length!=arg[argidx].dim[level]) + error(47); /* array sizes must match */ + else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) + error(229,sym->name); /* index tag mismatch */ + append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); + sym=finddepend(sym); + assert(sym!=NULL); + level++; + } /* if */ + /* the last dimension is checked too, again, unless it is zero */ + assert(leveldim.array.length!=arg[argidx].dim[level]) + error(47); /* array sizes must match */ + else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) + error(229,sym->name); /* index tag mismatch */ + append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); + } /* if */ + /* address already in PRI */ + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + // ??? set uWRITTEN? + argidx++; /* argument done */ + break; + } /* switch */ + pushreg(sPRI); /* store the function argument on the stack */ + markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ + nest_stkusage++; + } /* if */ + assert(arglist[argpos]!=ARG_UNHANDLED); + nargs++; + if (matchparanthesis) { + close=matchtoken(')'); + if (!close) /* if not paranthese... */ + if (!needtoken(',')) /* ...should be comma... */ + break; /* ...but abort loop if neither */ + } else { + close=!matchtoken(','); + if (close) { /* if not comma... */ + if (needtoken(tTERM)==1)/* ...must be end of statement */ + lexpush(); /* push again, because end of statement is analised later */ + } /* if */ + } /* if */ + } while (!close && freading && !matchtoken(tENDEXPR)); /* do */ + } /* if */ + /* check remaining function arguments (they may have default values) */ + for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) { + if (arglist[argidx]==ARG_DONE) + continue; /* already seen and handled this argument */ + /* in this first stage, we also skip the arguments with uSIZEOF and uTAGOF; + * these are handled last + */ + if ((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0) { + assert(arg[argidx].ident==iVARIABLE); + continue; + } /* if */ + stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */ + if (arg[argidx].hasdefault) { + if (arg[argidx].ident==iREFARRAY) { + short level; + setdefarray(arg[argidx].defvalue.array.data, + arg[argidx].defvalue.array.size, + arg[argidx].defvalue.array.arraysize, + &arg[argidx].defvalue.array.addr, + (arg[argidx].usage & uCONST)!=0); + if ((arg[argidx].usage & uCONST)==0) { + heapalloc+=arg[argidx].defvalue.array.arraysize; + nest_stkusage+=arg[argidx].defvalue.array.arraysize; + } /* if */ + /* keep the lengths of all dimensions of a multi-dimensional default array */ + assert(arg[argidx].numdim>0); + if (arg[argidx].numdim==1) { + append_constval(&arrayszlst,arg[argidx].name,arg[argidx].defvalue.array.arraysize,0); + } else { + for (level=0; level0); + check_userop(NULL,arg[argidx].defvalue_tag,arg[argidx].tags[0],2,NULL,&dummytag); + assert(dummytag==arg[argidx].tags[0]); + } /* if */ + pushreg(sPRI); /* store the function argument on the stack */ + markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ + nest_stkusage++; + } else { + error(202,argidx); /* argument count mismatch */ + } /* if */ + if (arglist[argidx]==ARG_UNHANDLED) + nargs++; + arglist[argidx]=ARG_DONE; + } /* for */ + /* now a second loop to catch the arguments with default values that are + * the "sizeof" or "tagof" of other arguments + */ + for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) { + constvalue *asz; + cell array_sz; + if (arglist[argidx]==ARG_DONE) + continue; /* already seen and handled this argument */ + stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */ + assert(arg[argidx].ident==iVARIABLE); /* if "sizeof", must be single cell */ + /* if unseen, must be "sizeof" or "tagof" */ + assert((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0); + if ((arg[argidx].hasdefault & uSIZEOF)!=0) { + /* find the argument; if it isn't found, the argument's default value + * was a "sizeof" of a non-array (a warning for this was already given + * when declaring the function) + */ + asz=find_constval(&arrayszlst,arg[argidx].defvalue.size.symname, + arg[argidx].defvalue.size.level); + if (asz!=NULL) { + array_sz=asz->value; + if (array_sz==0) + error(224,arg[argidx].name); /* indeterminate array size in "sizeof" expression */ + } else { + array_sz=1; + } /* if */ + } else { + asz=find_constval(&taglst,arg[argidx].defvalue.size.symname, + arg[argidx].defvalue.size.level); + if (asz != NULL) { + exporttag(asz->value); + array_sz=asz->value | PUBLICTAG; /* must be set, because it just was exported */ + } else { + array_sz=0; + } /* if */ + } /* if */ + ldconst(array_sz,sPRI); + pushreg(sPRI); /* store the function argument on the stack */ + markexpr(sPARM,NULL,0); + nest_stkusage++; + if (arglist[argidx]==ARG_UNHANDLED) + nargs++; + arglist[argidx]=ARG_DONE; + } /* for */ + stgmark(sENDREORDER); /* mark end of reversed evaluation */ + pushval((cell)nargs*sizeof(cell)); + nest_stkusage++; + ffcall(sym,NULL,nargs); + if (sc_status!=statSKIP) + markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ + if ((sym->usage & uNATIVE)!=0 &&sym->x.lib!=NULL) + sym->x.lib->value += 1; /* increment "usage count" of the library */ + modheap(-heapalloc*sizeof(cell)); + if (symret!=NULL) + popreg(sPRI); /* pop hidden parameter as function result */ + sideeffect=TRUE; /* assume functions carry out a side-effect */ + delete_consttable(&arrayszlst); /* clear list of array sizes */ + delete_consttable(&taglst); /* clear list of parameter tags */ + + /* maintain max. amount of memory used */ + { + long totalsize; + totalsize=declared+decl_heap+1; /* local variables & return value size, + * +1 for PROC opcode */ + if (lval_result->ident==iREFARRAY) + totalsize++; /* add hidden parameter (on the stack) */ + if ((sym->usage & uNATIVE)==0) + totalsize++; /* add "call" opcode */ + totalsize+=nest_stkusage; + assert(curfunc!=NULL); + if (curfunc->x.stacksizex.stacksize=totalsize; + nest_stkusage-=nargs+heapalloc+1; /* stack/heap space, +1 for argcount param */ + /* if there is a syntax error in the script, the stack calculation is + * probably incorrect; but we may not allow it to drop below zero + */ + if (nest_stkusage<0) + nest_stkusage=0; + } + + /* scrap any arrays left on the heap, with the exception of the array that + * this function has as a result (in other words, scrap all arrays on the + * heap that caused by expressions in the function arguments) + */ + assert(decl_heap>=locheap); + modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ + decl_heap=locheap; + nesting--; +} + +/* dbltest + * + * Returns a non-zero value if lval1 an array and lval2 is not an array and + * the operation is addition or subtraction. + * + * Returns the "shift" count (1 for 16-bit, 2 for 32-bit) to align a cell + * to an array offset. + */ +static int dbltest(void (*oper)(),value *lval1,value *lval2) +{ + if ((oper!=ob_add) && (oper!=ob_sub)) + return 0; + if (lval1->ident!=iARRAY) + return 0; + if (lval2->ident==iARRAY) + return 0; + return sizeof(cell)/2; /* 1 for 16-bit, 2 for 32-bit */ +} + +/* commutative + * + * Test whether an operator is commutative, i.e. x oper y == y oper x. + * Commutative operators are: + (addition) + * * (multiplication) + * == (equality) + * != (inequality) + * & (bitwise and) + * ^ (bitwise xor) + * | (bitwise or) + * + * If in an expression, code for the left operand has been generated and + * the right operand is a constant and the operator is commutative, the + * precautionary "push" of the primary register is scrapped and the constant + * is read into the secondary register immediately. + */ +static int commutative(void (*oper)()) +{ + return oper==ob_add || oper==os_mult + || oper==ob_eq || oper==ob_ne + || oper==ob_and || oper==ob_xor || oper==ob_or; +} + +/* constant + * + * Generates code to fetch a number, a literal character (which is returned + * by lex() as a number as well) or a literal string (lex() stores the + * strings in the literal queue). If the operand was a number, it is stored + * in lval->constval. + * + * The function returns 1 if the token was a constant or a string, 0 + * otherwise. + */ +static int constant(value *lval) +{ + int tok,index,ident; + cell val,item,cidx; + char *st; + symbol *sym; + int cmptag=lval->cmptag; + + tok=lex(&val,&st); + if (tok==tSYMBOL && (sym=findconst(st,&cmptag))!=0) { + if (cmptag>1) + error(91,sym->name); /* ambiguity: multiple matching constants (different tags) */ + lval->constval=sym->addr; + ldconst(lval->constval,sPRI); + lval->ident=iCONSTEXPR; + lval->tag=sym->tag; + lval->sym=sym; + markusage(sym,uREAD); + } else if (tok==tNUMBER) { + lval->constval=val; + ldconst(lval->constval,sPRI); + lval->ident=iCONSTEXPR; + } else if (tok==tRATIONAL) { + lval->constval=val; + ldconst(lval->constval,sPRI); + lval->ident=iCONSTEXPR; + lval->tag=sc_rationaltag; + } else if (tok==tSTRING) { + /* lex() stores starting index of string in the literal table in 'val' */ + ldconst((val+glb_declared)*sizeof(cell),sPRI); + lval->ident=iARRAY; /* pretend this is a global array */ + lval->constval=val-litidx; /* constval == the negative value of the + * size of the literal array; using a negative + * value distinguishes between literal arrays + * and literal strings (this was done for + * array assignment). */ + } else if (tok=='{') { + int tag,lasttag=-1; + val=litidx; + do { + /* cannot call constexpr() here, because "staging" is already turned + * on at this point */ + assert(staging); + stgget(&index,&cidx); /* mark position in code generator */ + ident=expression(&item,&tag,NULL,FALSE); + stgdel(index,cidx); /* scratch generated code */ + if (ident!=iCONSTEXPR) + error(8); /* must be constant expression */ + if (lasttag<0) + lasttag=tag; + else if (!matchtag(lasttag,tag,FALSE)) + error(213); /* tagname mismatch */ + litadd(item); /* store expression result in literal table */ + } while (matchtoken(',')); + if (!needtoken('}')) + lexclr(FALSE); + ldconst((val+glb_declared)*sizeof(cell),sPRI); + lval->ident=iARRAY; /* pretend this is a global array */ + lval->constval=litidx-val; /* constval == the size of the literal array */ + } else { + return FALSE; /* no, it cannot be interpreted as a constant */ + } /* if */ + return TRUE; /* yes, it was a constant value */ +} + diff --git a/compiler-init/sc4.c b/compiler-init/sc4.c new file mode 100644 index 00000000..70e39dde --- /dev/null +++ b/compiler-init/sc4.c @@ -0,0 +1,1331 @@ +/* Pawn compiler - code generation (unoptimized "assembler" code) + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc4.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include +#include /* for _MAX_PATH */ +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +static int fcurseg; /* the file number (fcurrent) for the active segment */ + + +/* When a subroutine returns to address 0, the AMX must halt. In earlier + * releases, the RET and RETN opcodes checked for the special case 0 address. + * Today, the compiler simply generates a HALT instruction at address 0. So + * a subroutine can savely return to 0, and then encounter a HALT. + */ +SC_FUNC void writeleader(symbol *root) +{ + int lbl_nostate,lbl_table; + int statecount; + symbol *sym; + constvalue *fsa, *state, *stlist; + int fsa_id,listid; + char lbl_default[sNAMEMAX+1]; + + assert(code_idx==0); + + begcseg(); + stgwrite(";program exit point\n"); + stgwrite("\thalt 0\n\n"); + code_idx+=opcodes(1)+opargs(1); /* calculate code length */ + + /* check whether there are any functions that have states */ + for (sym=root->next; sym!=NULL; sym=sym->next) + if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) + break; + if (sym==NULL) + return; /* no function has states, nothing to do next */ + + /* generate an error function that is called for an undefined state */ + stgwrite("\n;exit point for functions called from the wrong state\n"); + lbl_nostate=getlabel(); + setlabel(lbl_nostate); + stgwrite("\thalt "); + outval(AMX_ERR_INVSTATE,TRUE); + code_idx+=opcodes(1)+opargs(1); /* calculate code length */ + + /* write the "state-selectors" table with all automatons (update the + * automatons structure too, as we are now assigning the address to + * each automaton state-selector variable) + */ + assert(glb_declared==0); + begdseg(); + for (fsa=sc_automaton_tab.next; fsa!=NULL; fsa=fsa->next) { + defstorage(); + stgwrite("0\t; automaton "); + if (strlen(fsa->name)==0) + stgwrite("(anonymous)"); + else + stgwrite(fsa->name); + stgwrite("\n"); + fsa->value=glb_declared*sizeof(cell); + glb_declared++; + } /* for */ + + /* write stubs and jump tables for all state functions */ + begcseg(); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) { + stlist=sym->states->next; + assert(stlist!=NULL); /* there should be at least one state item */ + listid=stlist->index; + assert(listid==-1 || listid>0); + if (listid==-1 && stlist->next!=NULL) { + /* first index is the "fallback", take the next one (if available) */ + stlist=stlist->next; + listid=stlist->index; + } /* if */ + if (listid==-1) { + /* first index is the fallback, there is no second... */ + strcpy(stlist->name,"0"); /* insert dummy label number */ + /* this is an error, but we postpone adding the error message until the + * function definition + */ + continue; + } /* if */ + /* generate label numbers for all statelist ids */ + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + assert(strlen(stlist->name)==0); + strcpy(stlist->name,itoh(getlabel())); + } /* for */ + if (strcmp(sym->name,uENTRYFUNC)==0) + continue; /* do not generate stubs for this special function */ + sym->addr=code_idx; /* fix the function address now */ + /* get automaton id for this function */ + assert(listid>0); + fsa_id=state_getfsa(listid); + assert(fsa_id>=0); /* automaton 0 exists */ + fsa=automaton_findid(fsa_id); + /* count the number of states actually used; at the sane time, check + * whether there is a default state function + */ + statecount=0; + strcpy(lbl_default,itoh(lbl_nostate)); + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + if (stlist->index==-1) { + assert(strlen(stlist->name)name); + } else { + statecount+=state_count(stlist->index); + } /* if */ + } /* for */ + /* generate a stub entry for the functions */ + stgwrite("\tload.pri "); + outval(fsa->value,FALSE); + stgwrite("\t; "); + stgwrite(sym->name); + stgwrite("\n"); + code_idx+=opcodes(1)+opargs(1); /* calculate code length */ + lbl_table=getlabel(); + ffswitch(lbl_table); + /* generate the jump table */ + setlabel(lbl_table); + ffcase(statecount,lbl_default,TRUE); + for (state=sc_state_tab.next; state!=NULL; state=state->next) { + if (state->index==fsa_id) { + /* find the label for this list id */ + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + if (stlist->index!=-1 && state_inlist(stlist->index,(int)state->value)) { + ffcase(state->value,stlist->name,FALSE); + break; + } /* if */ + } /* for */ + if (stlist==NULL && strtol(lbl_default,NULL,16)==lbl_nostate) + error(230,state->name,sym->name); /* unimplemented state, no fallback */ + } /* if (state belongs to automaton of function) */ + } /* for (state) */ + stgwrite("\n"); + } /* if (is function, used & having states) */ + } /* for (sym) */ +} + +/* writetrailer + * Not much left of this once important function. + * + * Global references: pc_stksize (referred to only) + * sc_dataalign (referred to only) + * code_idx (altered) + * glb_declared (altered) + */ +SC_FUNC void writetrailer(void) +{ + assert(sc_dataalign % opcodes(1) == 0); /* alignment must be a multiple of + * the opcode size */ + assert(sc_dataalign!=0); + + /* pad code to align data segment */ + if ((code_idx % sc_dataalign)!=0) { + begcseg(); + while ((code_idx % sc_dataalign)!=0) + nooperation(); + } /* if */ + + /* pad data segment to align the stack and the heap */ + assert(litidx==0); /* literal queue should have been emptied */ + assert(sc_dataalign % sizeof(cell) == 0); + if (((glb_declared*sizeof(cell)) % sc_dataalign)!=0) { + begdseg(); + defstorage(); + while (((glb_declared*sizeof(cell)) % sc_dataalign)!=0) { + stgwrite("0 "); + glb_declared++; + } /* while */ + } /* if */ + + stgwrite("\nSTKSIZE "); /* write stack size (align stack top) */ + outval(pc_stksize - (pc_stksize % sc_dataalign), TRUE); +} + +/* + * Start (or restart) the CODE segment. + * + * In fact, the code and data segment specifiers are purely informational; + * the "DUMP" instruction itself already specifies that the following values + * should go to the data segment. All other instructions go to the code + * segment. + * + * Global references: curseg + * fcurrent + */ +SC_FUNC void begcseg(void) +{ + if (curseg!=sIN_CSEG || fcurrent!=fcurseg) { + stgwrite("\n"); + stgwrite("CODE "); + outval(fcurrent,FALSE); + stgwrite("\t; "); + outval(code_idx,TRUE); + curseg=sIN_CSEG; + fcurseg=fcurrent; + } /* endif */ +} + +/* + * Start (or restart) the DATA segment. + * + * Global references: curseg + */ +SC_FUNC void begdseg(void) +{ + if (curseg!=sIN_DSEG || fcurrent!=fcurseg) { + stgwrite("\n"); + stgwrite("DATA "); + outval(fcurrent,FALSE); + stgwrite("\t; "); + outval((glb_declared-litidx)*sizeof(cell),TRUE); + curseg=sIN_DSEG; + fcurseg=fcurrent; + } /* if */ +} + +SC_FUNC void setline(int chkbounds) +{ + if ((sc_debug & sSYMBOLIC)!=0 || chkbounds && (sc_debug & sCHKBOUNDS)!=0) { + /* generate a "break" (start statement) opcode rather than a "line" opcode + * because earlier versions of Small/Pawn have an incompatible version of the + * line opcode + */ + stgwrite("\tbreak\t; "); + outval(code_idx,TRUE); + code_idx+=opcodes(1); + } /* if */ +} + +SC_FUNC void setfiledirect(char *name) +{ + if (sc_status==statFIRST && sc_listing) { + assert(name!=NULL); + pc_writeasm(outf,"#file "); + pc_writeasm(outf,name); + pc_writeasm(outf,"\n"); + } /* if */ +} + +SC_FUNC void setlinedirect(int line) +{ + if (sc_status==statFIRST && sc_listing) { + char string[40]; + sprintf(string,"#line %d\n",line); + pc_writeasm(outf,string); + } /* if */ +} + +/* setlabel + * + * Post a code label (specified as a number), on a new line. + */ +SC_FUNC void setlabel(int number) +{ + assert(number>=0); + stgwrite("l."); + stgwrite((char *)itoh(number)); + /* To assist verification of the assembled code, put the address of the + * label as a comment. However, labels that occur inside an expression + * may move (through optimization or through re-ordering). So write the + * address only if it is known to accurate. + */ + if (!staging) { + stgwrite("\t\t; "); + outval(code_idx,FALSE); + } /* if */ + stgwrite("\n"); +} + +/* Write a token that signifies the start or end of an expression or special + * statement. This allows several simple optimizations by the peephole + * optimizer. + */ +SC_FUNC void markexpr(optmark type,const char *name,cell offset) +{ + switch (type) { + case sEXPR: + stgwrite("\t;$exp\n"); + break; + case sPARM: + stgwrite("\t;$par\n"); + break; + case sLDECL: + assert(name!=NULL); + stgwrite("\t;$lcl "); + stgwrite(name); + stgwrite(" "); + outval(offset,TRUE); + break; + default: + assert(0); + } /* switch */ +} + +/* startfunc - declare a CODE entry point (function start) + * + * Global references: funcstatus (referred to only) + */ +SC_FUNC void startfunc(char *fname) +{ + stgwrite("\tproc"); + if (sc_asmfile) { + char symname[2*sNAMEMAX+16]; + funcdisplayname(symname,fname); + stgwrite("\t; "); + stgwrite(symname); + } /* if */ + stgwrite("\n"); + code_idx+=opcodes(1); +} + +/* endfunc + * + * Declare a CODE ending point (function end) + */ +SC_FUNC void endfunc(void) +{ + stgwrite("\n"); /* skip a line */ +} + +/* alignframe + * + * Aligns the frame (and the stack) of the current function to a multiple + * of the specified byte count. Two caveats: the alignment ("numbytes") should + * be a power of 2, and this alignment must be done right after the frame + * is set up (before the first variable is declared) + */ +SC_FUNC void alignframe(int numbytes) +{ + #if !defined NDEBUG + /* "numbytes" should be a power of 2 for this code to work */ + int i,count=0; + for (i=0; isym; + if (lval->ident==iARRAYCELL) { + /* indirect fetch, address already in PRI */ + stgwrite("\tload.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* indirect fetch of a character from a pack, address already in PRI */ + stgwrite("\tlodb.i "); + outval(sCHARBITS/8,TRUE); /* read one or two bytes */ + code_idx+=opcodes(1)+opargs(1); + } else if (lval->ident==iREFERENCE) { + /* indirect fetch, but address not yet in PRI */ + assert(sym!=NULL); + assert(sym->vclass==sLOCAL);/* global references don't exist in Pawn */ + if (sym->vclass==sLOCAL) + stgwrite("\tlref.s.pri "); + else + stgwrite("\tlref.pri "); + outval(sym->addr,TRUE); + markusage(sym,uREAD); + code_idx+=opcodes(1)+opargs(1); + } else { + /* direct or stack relative fetch */ + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tload.s.pri "); + else + stgwrite("\tload.pri "); + outval(sym->addr,TRUE); + markusage(sym,uREAD); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* Get the address of a symbol into the primary or alternate register (used + * for arrays, and for passing arguments by reference). + */ +SC_FUNC void address(symbol *sym,regid reg) +{ + assert(sym!=NULL); + assert(reg==sPRI || reg==sALT); + /* the symbol can be a local array, a global array, or an array + * that is passed by reference. + */ + if (sym->ident==iREFARRAY || sym->ident==iREFERENCE) { + /* reference to a variable or to an array; currently this is + * always a local variable */ + switch (reg) { + case sPRI: + stgwrite("\tload.s.pri "); + break; + case sALT: + stgwrite("\tload.s.alt "); + break; + } /* switch */ + } else { + /* a local array or local variable */ + switch (reg) { + case sPRI: + if (sym->vclass==sLOCAL) + stgwrite("\taddr.pri "); + else + stgwrite("\tconst.pri "); + break; + case sALT: + if (sym->vclass==sLOCAL) + stgwrite("\taddr.alt "); + else + stgwrite("\tconst.alt "); + break; + } /* switch */ + } /* if */ + outval(sym->addr,TRUE); + markusage(sym,uREAD); + code_idx+=opcodes(1)+opargs(1); +} + +/* store + * + * Saves the contents of "primary" into a memory cell, either directly + * or indirectly (at the address given in the alternate register). + */ +SC_FUNC void store(value *lval) +{ + symbol *sym; + + sym=lval->sym; + if (lval->ident==iARRAYCELL) { + /* store at address in ALT */ + stgwrite("\tstor.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* store at address in ALT */ + stgwrite("\tstrb.i "); + outval(sCHARBITS/8,TRUE); /* write one or two bytes */ + code_idx+=opcodes(1)+opargs(1); + } else if (lval->ident==iREFERENCE) { + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tsref.s.pri "); + else + stgwrite("\tsref.pri "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } else { + assert(sym!=NULL); + markusage(sym,uWRITTEN); + if (sym->vclass==sLOCAL) + stgwrite("\tstor.s.pri "); + else + stgwrite("\tstor.pri "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* Get a cell from a fixed address in memory */ +SC_FUNC void loadreg(cell address,regid reg) +{ + assert(reg==sPRI || reg==sALT); + if (reg==sPRI) + stgwrite("\tload.pri "); + else + stgwrite("\tload.alt "); + outval(address,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* Store a cell into a fixed address in memory */ +SC_FUNC void storereg(cell address,regid reg) +{ + assert(reg==sPRI || reg==sALT); + if (reg==sPRI) + stgwrite("\tstor.pri "); + else + stgwrite("\tstor.alt "); + outval(address,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* source must in PRI, destination address in ALT. The "size" + * parameter is in bytes, not cells. + */ +SC_FUNC void memcopy(cell size) +{ + stgwrite("\tmovs "); + outval(size,TRUE); + + code_idx+=opcodes(1)+opargs(1); +} + +/* Address of the source must already have been loaded in PRI + * "size" is the size in bytes (not cells). + */ +SC_FUNC void copyarray(symbol *sym,cell size) +{ + assert(sym!=NULL); + /* the symbol can be a local array, a global array, or an array + * that is passed by reference. + */ + if (sym->ident==iREFARRAY) { + /* reference to an array; currently this is always a local variable */ + assert(sym->vclass==sLOCAL); /* symbol must be stack relative */ + stgwrite("\tload.s.alt "); + } else { + /* a local or global array */ + if (sym->vclass==sLOCAL) + stgwrite("\taddr.alt "); + else + stgwrite("\tconst.alt "); + } /* if */ + outval(sym->addr,TRUE); + markusage(sym,uWRITTEN); + + code_idx+=opcodes(1)+opargs(1); + memcopy(size); +} + +SC_FUNC void fillarray(symbol *sym,cell size,cell value) +{ + ldconst(value,sPRI); /* load value in PRI */ + + assert(sym!=NULL); + /* the symbol can be a local array, a global array, or an array + * that is passed by reference. + */ + if (sym->ident==iREFARRAY) { + /* reference to an array; currently this is always a local variable */ + assert(sym->vclass==sLOCAL); /* symbol must be stack relative */ + stgwrite("\tload.s.alt "); + } else { + /* a local or global array */ + if (sym->vclass==sLOCAL) + stgwrite("\taddr.alt "); + else + stgwrite("\tconst.alt "); + } /* if */ + outval(sym->addr,TRUE); + markusage(sym,uWRITTEN); + + assert(size>0); + stgwrite("\tfill "); + outval(size,TRUE); + + code_idx+=opcodes(2)+opargs(2); +} + +/* Instruction to get an immediate value into the primary or the alternate + * register + */ +SC_FUNC void ldconst(cell val,regid reg) +{ + assert(reg==sPRI || reg==sALT); + switch (reg) { + case sPRI: + if (val==0) { + stgwrite("\tzero.pri\n"); + code_idx+=opcodes(1); + } else { + stgwrite("\tconst.pri "); + outval(val, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ + break; + case sALT: + if (val==0) { + stgwrite("\tzero.alt\n"); + code_idx+=opcodes(1); + } else { + stgwrite("\tconst.alt "); + outval(val, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ + break; + } /* switch */ +} + +/* Copy value in alternate register to the primary register */ +SC_FUNC void moveto1(void) +{ + stgwrite("\tmove.pri\n"); + code_idx+=opcodes(1)+opargs(0); +} + +/* Push primary or the alternate register onto the stack + */ +SC_FUNC void pushreg(regid reg) +{ + assert(reg==sPRI || reg==sALT); + switch (reg) { + case sPRI: + stgwrite("\tpush.pri\n"); + break; + case sALT: + stgwrite("\tpush.alt\n"); + break; + } /* switch */ + code_idx+=opcodes(1); +} + +/* + * Push a constant value onto the stack + */ +SC_FUNC void pushval(cell val) +{ + stgwrite("\tpush.c "); + outval(val, TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* Pop stack into the primary or the alternate register + */ +SC_FUNC void popreg(regid reg) +{ + assert(reg==sPRI || reg==sALT); + switch (reg) { + case sPRI: + stgwrite("\tpop.pri\n"); + break; + case sALT: + stgwrite("\tpop.alt\n"); + break; + } /* switch */ + code_idx+=opcodes(1); +} + +/* + * swap the top-of-stack with the value in primary register + */ +SC_FUNC void swap1(void) +{ + stgwrite("\tswap.pri\n"); + code_idx+=opcodes(1); +} + +/* Switch statements + * The "switch" statement generates a "case" table using the "CASE" opcode. + * The case table contains a list of records, each record holds a comparison + * value and a label to branch to on a match. The very first record is an + * exception: it holds the size of the table (excluding the first record) and + * the label to branch to when none of the values in the case table match. + * The case table is sorted on the comparison value. This allows more advanced + * abstract machines to sift the case table with a binary search. + */ +SC_FUNC void ffswitch(int label) +{ + stgwrite("\tswitch "); + outval(label,TRUE); /* the label is the address of the case table */ + code_idx+=opcodes(1)+opargs(1); +} + +SC_FUNC void ffcase(cell value,char *labelname,int newtable) +{ + if (newtable) { + stgwrite("\tcasetbl\n"); + code_idx+=opcodes(1); + } /* if */ + stgwrite("\tcase "); + outval(value,FALSE); + stgwrite(" "); + stgwrite(labelname); + stgwrite("\n"); + code_idx+=opcodes(0)+opargs(2); +} + +/* + * Call specified function + */ +SC_FUNC void ffcall(symbol *sym,const char *label,int numargs) +{ + char symname[2*sNAMEMAX+16]; + + assert(sym!=NULL); + assert(sym->ident==iFUNCTN); + if (sc_asmfile) + funcdisplayname(symname,sym->name); + if ((sym->usage & uNATIVE)!=0) { + /* reserve a SYSREQ id if called for the first time */ + assert(label==NULL); + if (sc_status==statWRITE && (sym->usage & uREAD)==0 && sym->addr>=0) + sym->addr=ntv_funcid++; + stgwrite("\tsysreq.c "); + outval(sym->addr,FALSE); + if (sc_asmfile) { + stgwrite("\t; "); + stgwrite(symname); + } /* if */ + stgwrite("\n"); /* write on a separate line, to mark a sequence point for the peephole optimizer */ + stgwrite("\tstack "); + outval((numargs+1)*sizeof(cell), TRUE); + code_idx+=opcodes(2)+opargs(2); + } else { + /* normal function */ + stgwrite("\tcall "); + if (label!=NULL) { + stgwrite("l."); + stgwrite(label); + } else { + stgwrite(sym->name); + } /* if */ + if (sc_asmfile + && (label!=NULL || !isalpha(sym->name[0]) && sym->name[0]!='_' && sym->name[0]!=sc_ctrlchar)) + { + stgwrite("\t; "); + stgwrite(symname); + } /* if */ + stgwrite("\n"); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* Return from function + * + * Global references: funcstatus (referred to only) + */ +SC_FUNC void ffret(int remparams) +{ + if (remparams) + stgwrite("\tretn\n"); + else + stgwrite("\tret\n"); + code_idx+=opcodes(1); +} + +SC_FUNC void ffabort(int reason) +{ + stgwrite("\thalt "); + outval(reason,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +SC_FUNC void ffbounds(cell size) +{ + if ((sc_debug & sCHKBOUNDS)!=0) { + stgwrite("\tbounds "); + outval(size,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* + * Jump to local label number (the number is converted to a name) + */ +SC_FUNC void jumplabel(int number) +{ + stgwrite("\tjump "); + outval(number,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Define storage (global and static variables) + */ +SC_FUNC void defstorage(void) +{ + stgwrite("dump "); +} + +/* + * Inclrement/decrement stack pointer. Note that this routine does + * nothing if the delta is zero. + */ +SC_FUNC void modstk(int delta) +{ + if (delta) { + stgwrite("\tstack "); + outval(delta, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* set the stack to a hard offset from the frame */ +SC_FUNC void setstk(cell value) +{ + stgwrite("\tlctrl 5\n"); /* get FRM in PRI */ + assert(value<=0); /* STK should always become <= FRM */ + if (value<0) { + stgwrite("\tadd.c "); + outval(value, TRUE); /* add (negative) offset */ + code_idx+=opcodes(1)+opargs(1); + // ??? write zeros in the space between STK and the value in PRI (the new stk) + // get value of STK in ALT + // zero PRI + // need new FILL opcode that takes a variable size + } /* if */ + stgwrite("\tsctrl 4\n"); /* store in STK */ + code_idx+=opcodes(2)+opargs(2); +} + +SC_FUNC void modheap(int delta) +{ + if (delta) { + stgwrite("\theap "); + outval(delta, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +SC_FUNC void setheap_pri(void) +{ + stgwrite("\theap "); /* ALT = HEA++ */ + outval(sizeof(cell), TRUE); + stgwrite("\tstor.i\n"); /* store PRI (default value) at address ALT */ + stgwrite("\tmove.pri\n"); /* move ALT to PRI: PRI contains the address */ + code_idx+=opcodes(3)+opargs(1); +} + +SC_FUNC void setheap(cell value) +{ + stgwrite("\tconst.pri "); /* load default value in PRI */ + outval(value, TRUE); + code_idx+=opcodes(1)+opargs(1); + setheap_pri(); +} + +/* + * Convert a cell number to a "byte" address; i.e. double or quadruple + * the primary register. + */ +SC_FUNC void cell2addr(void) +{ + #if PAWN_CELL_SIZE==16 + stgwrite("\tshl.c.pri 1\n"); + #elif PAWN_CELL_SIZE==32 + stgwrite("\tshl.c.pri 2\n"); + #elif PAWN_CELL_SIZE==64 + stgwrite("\tshl.c.pri 3\n"); + #else + #error Unsupported cell size + #endif + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Double or quadruple the alternate register. + */ +SC_FUNC void cell2addr_alt(void) +{ + #if PAWN_CELL_SIZE==16 + stgwrite("\tshl.c.alt 1\n"); + #elif PAWN_CELL_SIZE==32 + stgwrite("\tshl.c.alt 2\n"); + #elif PAWN_CELL_SIZE==64 + stgwrite("\tshl.c.alt 3\n"); + #else + #error Unsupported cell size + #endif + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Convert "distance of addresses" to "number of cells" in between. + * Or convert a number of packed characters to the number of cells (with + * truncation). + */ +SC_FUNC void addr2cell(void) +{ + #if PAWN_CELL_SIZE==16 + stgwrite("\tshr.c.pri 1\n"); + #elif PAWN_CELL_SIZE==32 + stgwrite("\tshr.c.pri 2\n"); + #elif PAWN_CELL_SIZE==64 + stgwrite("\tshr.c.pri 3\n"); + #else + #error Unsupported cell size + #endif + code_idx+=opcodes(1)+opargs(1); +} + +/* Convert from character index to byte address. This routine does + * nothing if a character has the size of a byte. + */ +SC_FUNC void char2addr(void) +{ + #if sCHARBITS==16 + stgwrite("\tshl.c.pri 1\n"); + code_idx+=opcodes(1)+opargs(1); + #endif +} + +/* Align PRI (which should hold a character index) to an address. + * The first character in a "pack" occupies the highest bits of + * the cell. This is at the lower memory address on Big Endian + * computers and on the higher address on Little Endian computers. + * The ALIGN.pri/alt instructions must solve this machine dependence; + * that is, on Big Endian computers, ALIGN.pri/alt shuold do nothing + * and on Little Endian computers they should toggle the address. + */ +SC_FUNC void charalign(void) +{ + stgwrite("\talign.pri "); + outval(sCHARBITS/8,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Add a constant to the primary register. + */ +SC_FUNC void addconst(cell value) +{ + if (value!=0) { + stgwrite("\tadd.c "); + outval(value,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* + * signed multiply of primary and secundairy registers (result in primary) + */ +SC_FUNC void os_mult(void) +{ + stgwrite("\tsmul\n"); + code_idx+=opcodes(1); +} + +/* + * signed divide of alternate register by primary register (quotient in + * primary; remainder in alternate) + */ +SC_FUNC void os_div(void) +{ + stgwrite("\tsdiv.alt\n"); + code_idx+=opcodes(1); +} + +/* + * modulus of (alternate % primary), result in primary (signed) + */ +SC_FUNC void os_mod(void) +{ + stgwrite("\tsdiv.alt\n"); + stgwrite("\tmove.pri\n"); /* move ALT to PRI */ + code_idx+=opcodes(2); +} + +/* + * Add primary and alternate registers (result in primary). + */ +SC_FUNC void ob_add(void) +{ + stgwrite("\tadd\n"); + code_idx+=opcodes(1); +} + +/* + * subtract primary register from alternate register (result in primary) + */ +SC_FUNC void ob_sub(void) +{ + stgwrite("\tsub.alt\n"); + code_idx+=opcodes(1); +} + +/* + * arithmic shift left alternate register the number of bits + * given in the primary register (result in primary). + * There is no need for a "logical shift left" routine, since + * logical shift left is identical to arithmic shift left. + */ +SC_FUNC void ob_sal(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tshl\n"); + code_idx+=opcodes(2); +} + +/* + * arithmic shift right alternate register the number of bits + * given in the primary register (result in primary). + */ +SC_FUNC void os_sar(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tsshr\n"); + code_idx+=opcodes(2); +} + +/* + * logical (unsigned) shift right of the alternate register by the + * number of bits given in the primary register (result in primary). + */ +SC_FUNC void ou_sar(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tshr\n"); + code_idx+=opcodes(2); +} + +/* + * inclusive "or" of primary and alternate registers (result in primary) + */ +SC_FUNC void ob_or(void) +{ + stgwrite("\tor\n"); + code_idx+=opcodes(1); +} + +/* + * "exclusive or" of primary and alternate registers (result in primary) + */ +SC_FUNC void ob_xor(void) +{ + stgwrite("\txor\n"); + code_idx+=opcodes(1); +} + +/* + * "and" of primary and secundairy registers (result in primary) + */ +SC_FUNC void ob_and(void) +{ + stgwrite("\tand\n"); + code_idx+=opcodes(1); +} + +/* + * test ALT==PRI; result in primary register (1 or 0). + */ +SC_FUNC void ob_eq(void) +{ + stgwrite("\teq\n"); + code_idx+=opcodes(1); +} + +/* + * test ALT!=PRI + */ +SC_FUNC void ob_ne(void) +{ + stgwrite("\tneq\n"); + code_idx+=opcodes(1); +} + +/* The abstract machine defines the relational instructions so that PRI is + * on the left side and ALT on the right side of the operator. For example, + * SLESS sets PRI to either 1 or 0 depending on whether the expression + * "PRI < ALT" is true. + * + * The compiler generates comparisons with ALT on the left side of the + * relational operator and PRI on the right side. The XCHG instruction + * prefixing the relational operators resets this. We leave it to the + * peephole optimizer to choose more compact instructions where possible. + */ + +/* Relational operator prefix for chained relational expressions. The + * "suffix" code restores the stack. + * For chained relational operators, the goal is to keep the comparison + * result "so far" in PRI and the value of the most recent operand in + * ALT, ready for a next comparison. + * The "prefix" instruction pushed the comparison result (PRI) onto the + * stack and moves the value of ALT into PRI. If there is a next comparison, + * PRI can now serve as the "left" operand of the relational operator. + */ +SC_FUNC void relop_prefix(void) +{ + stgwrite("\tpush.pri\n"); + stgwrite("\tmove.pri\n"); + code_idx+=opcodes(2); +} + +SC_FUNC void relop_suffix(void) +{ + stgwrite("\tswap.alt\n"); + stgwrite("\tand\n"); + stgwrite("\tpop.alt\n"); + code_idx+=opcodes(3); +} + +/* + * test ALTPRI (signed) + */ +SC_FUNC void os_gt(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tsgrtr\n"); + code_idx+=opcodes(2); +} + +/* + * test ALT>=PRI (signed) + */ +SC_FUNC void os_ge(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tsgeq\n"); + code_idx+=opcodes(2); +} + +/* + * logical negation of primary register + */ +SC_FUNC void lneg(void) +{ + stgwrite("\tnot\n"); + code_idx+=opcodes(1); +} + +/* + * two's complement primary register + */ +SC_FUNC void neg(void) +{ + stgwrite("\tneg\n"); + code_idx+=opcodes(1); +} + +/* + * one's complement of primary register + */ +SC_FUNC void invert(void) +{ + stgwrite("\tinvert\n"); + code_idx+=opcodes(1); +} + +/* + * nop + */ +SC_FUNC void nooperation(void) +{ + stgwrite("\tnop\n"); + code_idx+=opcodes(1); +} + + +/* increment symbol + */ +SC_FUNC void inc(value *lval) +{ + symbol *sym; + + sym=lval->sym; + if (lval->ident==iARRAYCELL) { + /* indirect increment, address already in PRI */ + stgwrite("\tinc.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* indirect increment of single character, address already in PRI */ + stgwrite("\tpush.pri\n"); + stgwrite("\tpush.alt\n"); + stgwrite("\tmove.alt\n"); /* copy address */ + stgwrite("\tlodb.i "); /* read from PRI into PRI */ + outval(sCHARBITS/8,TRUE); /* read one or two bytes */ + stgwrite("\tinc.pri\n"); + stgwrite("\tstrb.i "); /* write PRI to ALT */ + outval(sCHARBITS/8,TRUE); /* write one or two bytes */ + stgwrite("\tpop.alt\n"); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(8)+opargs(2); + } else if (lval->ident==iREFERENCE) { + assert(sym!=NULL); + stgwrite("\tpush.pri\n"); + /* load dereferenced value */ + assert(sym->vclass==sLOCAL); /* global references don't exist in Pawn */ + if (sym->vclass==sLOCAL) + stgwrite("\tlref.s.pri "); + else + stgwrite("\tlref.pri "); + outval(sym->addr,TRUE); + /* increment */ + stgwrite("\tinc.pri\n"); + /* store dereferenced value */ + if (sym->vclass==sLOCAL) + stgwrite("\tsref.s.pri "); + else + stgwrite("\tsref.pri "); + outval(sym->addr,TRUE); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(5)+opargs(2); + } else { + /* local or global variable */ + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tinc.s "); + else + stgwrite("\tinc "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* decrement symbol + * + * in case of an integer pointer, the symbol must be incremented by 2. + */ +SC_FUNC void dec(value *lval) +{ + symbol *sym; + + sym=lval->sym; + if (lval->ident==iARRAYCELL) { + /* indirect decrement, address already in PRI */ + stgwrite("\tdec.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* indirect decrement of single character, address already in PRI */ + stgwrite("\tpush.pri\n"); + stgwrite("\tpush.alt\n"); + stgwrite("\tmove.alt\n"); /* copy address */ + stgwrite("\tlodb.i "); /* read from PRI into PRI */ + outval(sCHARBITS/8,TRUE); /* read one or two bytes */ + stgwrite("\tdec.pri\n"); + stgwrite("\tstrb.i "); /* write PRI to ALT */ + outval(sCHARBITS/8,TRUE); /* write one or two bytes */ + stgwrite("\tpop.alt\n"); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(8)+opargs(2); + } else if (lval->ident==iREFERENCE) { + assert(sym!=NULL); + stgwrite("\tpush.pri\n"); + /* load dereferenced value */ + assert(sym->vclass==sLOCAL); /* global references don't exist in Pawn */ + if (sym->vclass==sLOCAL) + stgwrite("\tlref.s.pri "); + else + stgwrite("\tlref.pri "); + outval(sym->addr,TRUE); + /* decrement */ + stgwrite("\tdec.pri\n"); + /* store dereferenced value */ + if (sym->vclass==sLOCAL) + stgwrite("\tsref.s.pri "); + else + stgwrite("\tsref.pri "); + outval(sym->addr,TRUE); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(5)+opargs(2); + } else { + /* local or global variable */ + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tdec.s "); + else + stgwrite("\tdec "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* + * Jumps to "label" if PRI != 0 + */ +SC_FUNC void jmp_ne0(int number) +{ + stgwrite("\tjnz "); + outval(number,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Jumps to "label" if PRI == 0 + */ +SC_FUNC void jmp_eq0(int number) +{ + stgwrite("\tjzer "); + outval(number,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* write a value in hexadecimal; optionally adds a newline */ +SC_FUNC void outval(cell val,int newline) +{ + stgwrite(itoh(val)); + if (newline) + stgwrite("\n"); +} diff --git a/compiler-init/sc5.c b/compiler-init/sc5.c new file mode 100644 index 00000000..d9d0298a --- /dev/null +++ b/compiler-init/sc5.c @@ -0,0 +1,228 @@ +/* Pawn compiler - Error message system + * In fact a very simple system, using only 'panic mode'. + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc5.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ + #include +#endif +#if defined LINUX || defined __GNUC__ + #include +#endif +#include +#include +#include /* ANSI standardized variable argument list functions */ +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +#if defined _MSC_VER + #pragma warning(push) + #pragma warning(disable:4125) /* decimal digit terminates octal escape sequence */ +#endif + +#include "sc5.scp" + +#if defined _MSC_VER + #pragma warning(pop) +#endif + +#define NUM_WARNINGS (sizeof warnmsg / sizeof warnmsg[0]) +static unsigned char warndisable[(NUM_WARNINGS + 7) / 8]; /* 8 flags in a char */ + +static int errflag; +static int errstart; /* line number at which the instruction started */ +static int errline; /* forced line number for the error message */ + +/* error + * + * Outputs an error message (note: msg is passed optionally). + * If an error is found, the variable "errflag" is set and subsequent + * errors are ignored until lex() finds a semicolumn or a keyword + * (lex() resets "errflag" in that case). + * + * Global references: inpfname (reffered to only) + * fline (reffered to only) + * fcurrent (reffered to only) + * errflag (altered) + */ +SC_FUNC int error(int number,...) +{ +static char *prefix[3]={ "error", "fatal error", "warning" }; +static int lastline,errorcount; +static short lastfile; + char *msg,*pre; + va_list argptr; + char string[128]; + + /* errflag is reset on each semicolon. + * In a two-pass compiler, an error should not be reported twice. Therefore + * the error reporting is enabled only in the second pass (and only when + * actually producing output). Fatal errors may never be ignored. + */ + if ((errflag || sc_status!=statWRITE) && (number<100 || number>=200)) + return 0; + + /* also check for disabled warnings */ + if (number>=200) { + int index=(number-200)/8; + int mask=1 << ((number-200)%8); + if ((warndisable[index] & mask)!=0) + return 0; + } /* if */ + + if (number<100){ + msg=errmsg[number-1]; + pre=prefix[0]; + errflag=TRUE; /* set errflag (skip rest of erroneous expression) */ + errnum++; + } else if (number<200){ + msg=fatalmsg[number-100]; + pre=prefix[1]; + errnum++; /* a fatal error also counts as an error */ + } else { + msg=warnmsg[number-200]; + pre=prefix[2]; + warnnum++; + } /* if */ + + strexpand(string,(unsigned char *)msg,sizeof string,SCPACK_TABLE); + + assert(errstart<=fline); + if (errline>0) + errstart=errline; + else + errline=fline; + assert(errstart<=errline); + va_start(argptr,number); + if (strlen(errfname)==0) { + int start= (errstart==errline) ? -1 : errstart; + if (pc_error(number,string,inpfname,start,errline,argptr)) { + if (outf!=NULL) { + pc_closeasm(outf,TRUE); + outf=NULL; + } /* if */ + longjmp(errbuf,3); /* user abort */ + } /* if */ + } else { + FILE *fp=fopen(errfname,"a"); + if (fp!=NULL) { + if (errstart>=0 && errstart!=errline) + fprintf(fp,"%s(%d -- %d) : %s %03d: ",inpfname,errstart,errline,pre,number); + else + fprintf(fp,"%s(%d) : %s %03d: ",inpfname,errline,pre,number); + vfprintf(fp,string,argptr); + fclose(fp); + } /* if */ + } /* if */ + va_end(argptr); + + if (number>=100 && number<200 || errnum>25){ + if (strlen(errfname)==0) { + va_start(argptr,number); + pc_error(0,"\nCompilation aborted.",NULL,0,0,argptr); + va_end(argptr); + } /* if */ + if (outf!=NULL) { + pc_closeasm(outf,TRUE); + outf=NULL; + } /* if */ + longjmp(errbuf,2); /* fatal error, quit */ + } /* if */ + + errline=-1; + /* check whether we are seeing many errors on the same line */ + if ((errstart<0 && lastline!=fline) || lastlinefline || fcurrent!=lastfile) + errorcount=0; + lastline=fline; + lastfile=fcurrent; + if (number<200) + errorcount++; + if (errorcount>=3) + error(107); /* too many error/warning messages on one line */ + + return 0; +} + +SC_FUNC void errorset(int code,int line) +{ + switch (code) { + case sRESET: + errflag=FALSE; /* start reporting errors */ + break; + case sFORCESET: + errflag=TRUE; /* stop reporting errors */ + break; + case sEXPRMARK: + errstart=fline; /* save start line number */ + break; + case sEXPRRELEASE: + errstart=-1; /* forget start line number */ + errline=-1; + break; + case sSETPOS: + errline=line; + break; + } /* switch */ +} + +/* sc_enablewarning() + * Enables or disables a warning (errors cannot be disabled). + * Initially all warnings are enabled. The compiler does this by setting bits + * for the *disabled* warnings and relying on the array to be zero-initialized. + * + * Parameter enable can be: + * o 0 for disable + * o 1 for enable + * o 2 for toggle + */ +int pc_enablewarning(int number,int enable) +{ + int index; + unsigned char mask; + + if (number<200) + return FALSE; /* errors and fatal errors cannot be disabled */ + number -= 200; + if (number>=NUM_WARNINGS) + return FALSE; + + index=number/8; + mask=(unsigned char)(1 << (number%8)); + switch (enable) { + case 0: + warndisable[index] |= mask; + break; + case 1: + warndisable[index] &= (unsigned char)~mask; + break; + case 2: + warndisable[index] ^= mask; + break; + } /* switch */ + + return TRUE; +} + +#undef SCPACK_TABLE diff --git a/compiler-init/sc5.scp b/compiler-init/sc5.scp new file mode 100644 index 00000000..a08315c2 --- /dev/null +++ b/compiler-init/sc5.scp @@ -0,0 +1,342 @@ +/* Pawn compiler - Error message strings (plain and compressed formats) + * + * Copyright (c) ITB CompuPhase, 2000-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc5.sch 3590 2006-06-24 14:16:39Z thiadmer $ + */ + +SC_FUNC int strexpand(char *dest, unsigned char *source, int maxlen, unsigned char pairtable[128][2]); + +#define SCPACK_TABLE errstr_table +/*-*SCPACK start of pair table, do not change or remove this line */ +unsigned char errstr_table[][2] = { + {101,32}, {111,110}, {116,32}, {105,110}, {97,114}, {116,105}, {100,32}, {115,32}, {101,114}, {97,108}, {101,110}, {37,115}, {133,129}, {34,139}, {141,34}, {117,110}, + {110,111}, {114,101}, {115,105}, {121,32}, {97,116}, {111,114}, {97,110}, {32,142}, {109,98}, {115,116}, {41,10}, {100,101}, {109,138}, {101,134}, {98,108}, {140,32}, + {111,108}, {114,97}, {144,130}, {118,137}, {143,99}, {102,164}, {115,121}, {166,152}, {167,160}, {117,115}, {97,32}, {115,146}, {97,158}, {149,32}, {132,161}, {105,134}, + {103,32}, {163,175}, {103,117}, {178,156}, {136,32}, {132,179}, {131,177}, {111,102}, {116,104}, {101,120}, {105,135}, {165,159}, {101,100}, {99,104}, {118,132}, {168,151}, + {105,172}, {190,192}, {155,102}, {174,147}, {183,32}, {109,97}, {116,111}, {99,129}, {101,135}, {181,130}, {98,128}, {115,10}, {112,145}, {153,148}, {44,32}, {40,191}, + {169,130}, {151,10}, {101,10}, {207,154}, {109,208}, {116,97}, {105,99}, {194,131}, {193,128}, {34,32}, {129,32}, {132,97}, {100,105}, {146,122}, {110,32}, {137,32}, + {104,97}, {101,108}, {117,108}, {99,111}, {108,111}, {109,148}, {199,153}, {58,209}, {111,112}, {97,115}, {108,128}, {232,136}, {230,150}, {150,32}, {204,171}, {131,176}, + {212,202}, {102,105}, {119,105}, {185,238}, {109,112}, {116,136}, {165,140}, {197,147}, {102,149}, {111,32}, {131,32}, {213,176}, {110,117}, {115,117}, {118,128} +}; +/*-*SCPACK end of pair table, do not change or remove this line */ + +static char *errmsg[] = { +#ifdef SCPACK +/*001*/ "expected token: \"%s\", but found \"%s\"\n", +/*002*/ "only a single statement (or expression) can follow each \"case\"\n", +/*003*/ "declaration of a local variable must appear in a compound block\n", +/*004*/ "function \"%s\" is not implemented\n", +/*005*/ "function may not have arguments\n", +/*006*/ "must be assigned to an array\n", +/*007*/ "operator cannot be redefined\n", +/*008*/ "must be a constant expression; assumed zero\n", +/*009*/ "invalid array size (negative, zero or out of bounds)\n", +/*010*/ "invalid function or declaration\n", +/*011*/ "invalid outside functions\n", +/*012*/ "invalid function call, not a valid address\n", +/*013*/ "no entry point (no public functions)\n", +/*014*/ "invalid statement; not in switch\n", +/*015*/ "\"default\" case must be the last case in switch statement\n", +/*016*/ "multiple defaults in \"switch\"\n", +/*017*/ "undefined symbol \"%s\"\n", +/*018*/ "initialization data exceeds declared size\n", +/*019*/ "not a label: \"%s\"\n", +/*020*/ "invalid symbol name \"%s\"\n", +/*021*/ "symbol already defined: \"%s\"\n", +/*022*/ "must be lvalue (non-constant)\n", +/*023*/ "array assignment must be simple assignment\n", +/*024*/ "\"break\" or \"continue\" is out of context\n", +/*025*/ "function heading differs from prototype\n", +/*026*/ "no matching \"#if...\"\n", +/*027*/ "invalid character constant\n", +/*028*/ "invalid subscript (not an array or too many subscripts): \"%s\"\n", +/*029*/ "invalid expression, assumed zero\n", +/*030*/ "compound statement not closed at the end of file (started at line %d)\n", +/*031*/ "unknown directive\n", +/*032*/ "array index out of bounds (variable \"%s\")\n", +/*033*/ "array must be indexed (variable \"%s\")\n", +/*034*/ "argument does not have a default value (argument %d)\n", +/*035*/ "argument type mismatch (argument %d)\n", +/*036*/ "empty statement\n", +/*037*/ "invalid string (possibly non-terminated string)\n", +/*038*/ "extra characters on line\n", +/*039*/ "constant symbol has no size\n", +/*040*/ "duplicate \"case\" label (value %d)\n", +/*041*/ "invalid ellipsis, array size is not known\n", +/*042*/ "invalid combination of class specifiers\n", +/*043*/ "character constant exceeds range for packed string\n", +/*044*/ "positional parameters must precede all named parameters\n", +/*045*/ "too many function arguments\n", +/*046*/ "unknown array size (variable \"%s\")\n", +/*047*/ "array sizes do not match, or destination array is too small\n", +/*048*/ "array dimensions do not match\n", +/*049*/ "invalid line continuation\n", +/*050*/ "invalid range\n", +/*051*/ "invalid subscript, use \"[ ]\" operators on major dimensions\n", +/*052*/ "multi-dimensional arrays must be fully initialized\n", +/*053*/ "exceeding maximum number of dimensions\n", +/*054*/ "unmatched closing brace (\"}\")\n", +/*055*/ "start of function body without function header\n", +/*056*/ "arrays, local variables and function arguments cannot be public (variable \"%s\")\n", +/*057*/ "unfinished expression before compiler directive\n", +/*058*/ "duplicate argument; same argument is passed twice\n", +/*059*/ "function argument may not have a default value (variable \"%s\")\n", +/*060*/ "multiple \"#else\" directives between \"#if ... #endif\"\n", +/*061*/ "\"#elseif\" directive follows an \"#else\" directive\n", +/*062*/ "number of operands does not fit the operator\n", +/*063*/ "function result tag of operator \"%s\" must be \"%s\"\n", +/*064*/ "cannot change predefined operators\n", +/*065*/ "function argument may only have a single tag (argument %d)\n", +/*066*/ "function argument may not be a reference argument or an array (argument \"%s\")\n", +/*067*/ "variable cannot be both a reference and an array (variable \"%s\")\n", +/*068*/ "invalid rational number precision in #pragma\n", +/*069*/ "rational number format already defined\n", +/*070*/ "rational number support was not enabled\n", +/*071*/ "user-defined operator must be declared before use (function \"%s\")\n", +/*072*/ "\"sizeof\" operator is invalid on \"function\" symbols\n", +/*073*/ "function argument must be an array (argument \"%s\")\n", +/*074*/ "#define pattern must start with an alphabetic character\n", +/*075*/ "input line too long (after substitutions)\n", +/*076*/ "syntax error in the expression, or invalid function call\n", +/*077*/ "malformed UTF-8 encoding, or corrupted file: %s\n", +/*078*/ "function uses both \"return\" and \"return \"\n", +/*079*/ "inconsistent return types (array & non-array)\n", +/*080*/ "unknown symbol, or not a constant symbol (symbol \"%s\")\n", +/*081*/ "cannot take a tag as a default value for an indexed array parameter (symbol \"%s\")\n", +/*082*/ "user-defined operators and native functions may not have states\n", +/*083*/ "a function or variable may only belong to a single automaton (symbol \"%s\")\n", +/*084*/ "state conflict: one of the states is already assigned to another implementation (symbol \"%s\")\n", +/*085*/ "no states are defined for symbol \"%s\"\n", +/*086*/ "unknown automaton \"%s\"\n", +/*087*/ "unknown state \"%s\" for automaton \"%s\"\n", +/*088*/ "public variables and local variables may not have states (symbol \"%s\")\n", +/*089*/ "state variables may not be initialized (symbol \"%s\")\n", +/*090*/ "public functions may not return arrays (symbol \"%s\")\n", +/*091*/ "ambiguous constant; tag override is required (symbol \"%s\")\n" +#else + "\271pect\235\306k\212:\227\316bu\202fo\217\206\216\012", + "\201l\223\252s\203g\352\315e\234\202(\255\363\201) c\355f\240\344w ea\275 \042c\351e\042\012", + "\233cl\333\237\304\252\344c\337\330\324appe\204 \372\252\343\364o\217\206\236ock\012", + "\366\227 \272\242i\364le\234t\274\012", + "\273\367\242\340\376\265t\313", + "\360a\253gn\235\306 \355\256y\012", + "\353\224\255c\226\242\312\221\327\274\012", + "\360\252\354\202\363\201; \351\375m\235z\210o\012", + "\266\303\335\200(nega\205ve\316z\210\371\255ou\202\304bo\217ds\232", + "\266\273\255\233cl\333\214\012", + "\266out\222d\200\366\313", + "\266\273c\211l\316\242\252\261add\221s\313", + "\220 \212tr\223po\203\202(\220 pu\236\326 \366s\232", + "\266\315e\234t; \242\372s\362t\275\012", + "\042\302a\342t\331c\351\200\360\270\200l\351\202c\351\200\372s\362t\275 \315e\234t\012", + "m\342\205p\352\302a\342t\207\372\042s\362t\275\042\012", + "\217\327\235\277\012", + "\203i\205\211iza\237d\224\252\271ce\274\207\233cl\204\235\335\322", + "\242\252lab\341\347", + "\266\250 nam\200\216\012", + "\250 \211\221ad\223\327\274\347", + "\360l\243u\200(n\201-\354t\232", + "\303a\253gn\234\202\360\222\364\352a\253gn\234t\012", + "\042b\221ak\331\255\042\307t\203ue\331\272ou\202\304\307t\271t\012", + "\273head\357\334ff\210\207from pro\306typ\322", + "\220 \345\275\357\042#if...\042\012", + "\266\275\333ct\264\354t\012", + "\266\375bscrip\202(\242\355\303\255\306\371m\226\223\375bscripts)\347", + "\266\363\201\316\351\375m\235z\210o\012", + "\343\364o\217\206\315e\234\202\242c\344s\235a\202\270\200\212\206\304\361\352(\231\204t\235a\202l\203\200%d\232", + "\217k\220w\336\334\221c\205v\322", + "\303\203\233x ou\202\304bo\217d\207(\330\216\232", + "\303\360\203\233x\235(\330\216\232", + "\311do\310\242\340\376\252\302a\342\202\243u\200(\311%d\232", + "\311typ\200mis\345\275 (\311%d\232", + "e\364t\223\315e\234t\012", + "\266\231r\357(po\253\236\223n\201-\365m\203\224\235\231r\203g\232", + "\271t\241 \275\333c\365\207\332l\203\322", + "\354\202\250 \340\207\220 \335\322", + "dupl\326\224\200\042c\351e\331lab\341 (\243u\200%d\232", + "\266\341lip\222s\316\303\335\200\272\242k\220wn\012", + "\266\343\230\203a\237\304cl\351\207speci\361\210\313", + "\275\333ct\264\354\202\271ce\274\207r\226g\200f\255pack\235\231r\203g\012", + "po\222\214\337p\333me\365\207\324\314c\274\200\211l nam\235p\333me\365\313", + "\306\371m\226\223\273\265t\313", + "\217k\220w\336\303\335\200(\330\216\232", + "\303\335\310d\371\242\345\275\316\255\233\231\203a\237\303\272\306\371sm\211l\012", + "\303\334\234\222\201\207d\371\242\345\275\012", + "\266l\203\200\307t\203ua\214\012", + "\266r\226g\322", + "\266\375bscript\316\251\200\042[ ]\331\353\224\225\207\332\305j\255\334\234\222\201\313", + "m\342\205-\334\234\222\201\337\256y\207\360f\342l\223\203i\205\211iz\274\012", + "\271ce\274\357\305ximum \374\230\264\304\334\234\222\201\313", + "\217\345\275\235c\344s\357b\241c\200(\042}\042\232", + "\231\204\202\304\273bod\223\362\270ou\202\273head\210\012", + "\256ys\316\344c\337\301\310\226\206\273\265t\207c\226\242\312pu\236\326 (\330\216\232", + "\217f\203ish\235\363\332be\370\200\343\364il\264\334\221c\205v\322", + "dupl\326\224\200\265t; sam\200\311\272p\351s\235tw\326\322", + "\273\311\367\242\340\376\252\302a\342\202\243u\200(\330\216\232", + "m\342\205p\352\042#\341se\331\334\221c\205v\310betwe\212 \042#if ... #\212\334f\042\012", + "\042#\341seif\331\334\221c\205\376f\240\344w\207\355\042#\341se\331\334\221c\205v\322", + "\374\230\264\304\353\226d\207do\310\242\361\202\270\200\353\224\225\012", + "\273\221s\342\202\373\304\353\224\225\227 \360\216\012", + "c\226\242\275\226g\200\314\327\235\353\224\225\313", + "\273\311\367\201l\223\340\376\252s\203g\352\373(\311%d\232", + "\273\311\367\242\312\252\221f\210\212c\200\311\255\355\303(\311\216\232", + "\330c\226\242\312bo\270 \252\221f\210\212c\200\226\206\355\303(\330\216\232", + "\266\241\214\337\374\230\264\314ci\222\332\372#p\241g\305\012", + "\241\214\337\374\230\264\370\305\202\211\221ad\223\327\274\012", + "\241\214\337\374\230\264\375pp\225\202wa\207\242\212\254\274\012", + "\251\210-\327\235\353\224\255\360\233cl\204\235be\370\200\251\200(\366\227\232", + "\042\335e\267\331\353\224\255\272\266\332\042\366\331\250\313", + "\273\311\360\355\303(\311\216\232", + "#\327\200p\224\365\336\324\231\204\202\362\270 \355\211p\340be\205c \275\333c\365\012", + "\203pu\202l\203\200\306\371l\201\260(aft\264\375bs\205tu\214s\232", + "\246n\325x \210r\255\372\270\200\363\201\316\255\266\273c\211l\012", + "m\211\370m\235UTF-8 \212\343d\203g\316\255c\225rupt\235\361le: \213\012", + "\273\251\310bo\270 \042\221turn\331\226\206\042\221tur\336<\243ue>\042\012", + "\203\307\222\231\212\202\221tur\336typ\310(\303& n\201-\256y\232", + "\217k\220w\336\250\316\255\242\252\354\202\250 \323", + "c\226\242\325k\200\252\373a\207\252\302a\342\202\243u\200f\255\355\203\233x\235\303p\333met\264\323", + "\251\210-\327\235\353\224\225\207\226\206na\205\376\366\207\367\242\340\376\315e\313", + "\252\273\255\330\367\201l\223b\341\201\260\306 \252s\203g\352au\306\345\332\323", + "\315\200\307fl\326t: \201\200\304\270\200\315\310\272\211\221ad\223a\253gn\235\306 a\220\270\264i\364le\234\325\237\323", + "\220 \315\310\204\200\327\235f\255\277\012", + "\217k\220w\336au\306\345\201\321", + "\217k\220w\336\315\200\216 f\255au\306\345\201\321", + "pu\236\326 \301\310\226\206\344c\337\301\310\367\242\340\376\315\310\323", + "\315\200\301\310\367\242\312\203i\205\211iz\235\323", + "pu\236\326 \366\207\367\242\221tur\336\256y\207\323", + "a\230i\262ou\207\354t; \373ov\210rid\200\272\221qui\221\206\323" +#endif + }; + +static char *fatalmsg[] = { +#ifdef SCPACK +/*100*/ "cannot read from file: \"%s\"\n", +/*101*/ "cannot write to file: \"%s\"\n", +/*102*/ "table overflow: \"%s\"\n", + /* table can be: loop table + * literal table + * staging buffer + * option table (response file) + * peephole optimizer table + */ +/*103*/ "insufficient memory\n", +/*104*/ "invalid assembler instruction \"%s\"\n", +/*105*/ "numeric overflow, exceeding capacity\n", +/*106*/ "compiled script exceeds the maximum memory size (%ld bytes)\n", +/*107*/ "too many error messages on one line\n", +/*108*/ "codepage mapping file not found\n", +/*109*/ "invalid path: \"%s\"\n", +/*110*/ "assertion failed: %s\n", +/*111*/ "user error: %s\n", +#else + "c\226\242\221a\206from \361le\347", + "c\226\242writ\200\306 \361le\347", + "t\254\200ov\210f\344w\347", + "\203\375ff\326i\212\202mem\225y\012", + "\266\351se\230l\264\203\231ruc\214\321", + "\374m\210\326 ov\210f\344w\316\271ce\274\357capacity\012", + "\343\364il\235scrip\202\271ce\274\207\270\200\305ximum mem\225\223\335\200(%l\206bytes\232", + "\306\371m\226\223\210r\255messag\310\332\201\200l\203\322", + "\343\233pag\200\305pp\357\361\352\242fo\217d\012", + "\266p\224h\347", + "\351s\210\237fail\274: \213\012", + "\251\264\210r\225: \213\012" +#endif + }; + +static char *warnmsg[] = { +#ifdef SCPACK +/*200*/ "symbol \"%s\" is truncated to %d characters\n", +/*201*/ "redefinition of constant/macro (symbol \"%s\")\n", +/*202*/ "number of arguments does not match definition\n", +/*203*/ "symbol is never used: \"%s\"\n", +/*204*/ "symbol is assigned a value that is never used: \"%s\"\n", +/*205*/ "redundant code: constant expression is zero\n", +/*206*/ "redundant test: constant expression is non-zero\n", +/*207*/ "unknown #pragma\n", +/*208*/ "function with tag result used before definition, forcing reparse\n", +/*209*/ "function \"%s\" should return a value\n", +/*210*/ "possible use of symbol before initialization: \"%s\"\n", +/*211*/ "possibly unintended assignment\n", +/*212*/ "possibly unintended bitwise operation\n", +/*213*/ "tag mismatch\n", +/*214*/ "possibly a \"const\" array argument was intended: \"%s\"\n", +/*215*/ "expression has no effect\n", +/*216*/ "nested comment\n", +/*217*/ "loose indentation\n", +/*218*/ "old style prototypes used with optional semicolumns\n", +/*219*/ "local variable \"%s\" shadows a variable at a preceding level\n", +/*220*/ "expression with tag override must appear between parentheses\n", +/*221*/ "label name \"%s\" shadows tag name\n", +/*222*/ "number of digits exceeds rational number precision\n", +/*223*/ "redundant \"sizeof\": argument size is always 1 (symbol \"%s\")\n", +/*224*/ "indeterminate array size in \"sizeof\" expression (symbol \"%s\")\n", +/*225*/ "unreachable code\n", +/*226*/ "a variable is assigned to itself (symbol \"%s\")\n", +/*227*/ "more initiallers than enum fields\n", +/*228*/ "length of initialler exceeds size of the enum field\n", +/*229*/ "index tag mismatch (symbol \"%s\")\n", +/*230*/ "no implementation for state \"%s\" in function \"%s\", no fall-back\n", +/*231*/ "state specification on forward declaration is ignored\n", +/*232*/ "output file is written, but with compact encoding disabled\n", +/*233*/ "state variable \"%s\" shadows a global variable\n", +/*234*/ "function is depricated (symbol \"%s\") %s\n", +/*235*/ "public function lacks forward declaration (symbol \"%s\")\n", +/*236*/ "unknown parameter in substitution (incorrect #define pattern)\n" +#else + "\277 \272tr\244\224\235\306 %\206\275\333c\365\313", + "\221\327i\237\304\354t/\305cr\371\323", + "\374\230\264\304\265t\207do\310\242\345\275 \327i\214\012", + "\250 \272nev\264\251\274\347", + "\250 \272a\253gn\235\252\243u\200\270a\202\272nev\264\251\274\347", + "\221d\217d\226\202\343\233: \354\202\363\332\272z\210o\012", + "\221d\217d\226\202te\231: \354\202\363\332\272n\201-z\210o\012", + "\217k\220w\336#p\241g\305\012", + "\273\362\270 \373\221s\342\202\251\235be\370\200\327i\214\316\370c\357\221p\204s\322", + "\366\227 sho\342\206\221tur\336\252\243u\322", + "po\253\236\200\251\200\304\250 be\370\200\203i\205\211iza\214\347", + "po\253\236\223\217\203t\212\233\206a\253gn\234t\012", + "po\253\236\223\217\203t\212\233\206bit\362s\200\353a\214\012", + "\373mis\345\275\012", + "po\253\236\223\252\042\346\331\303\311wa\207\203t\212\233d\347", + "\363\332\340\207\220 effect\012", + "ne\231\235\343m\234t\012", + "\344os\200\203d\212\325\214\012", + "\240\206\231y\352pro\306typ\310\251\235\362\270 \350\214\337sem\326\240umn\313", + "\344c\337\330\216 s\340dow\207\252\330a\202\252\314c\274\357lev\341\012", + "\363\332\362\270 \373ov\210rid\200\324appe\204 betwe\212 p\204\212\270ese\313", + "lab\341 nam\200\216 s\340dow\207\373nam\322", + "\374\230\264\304\334git\207\271ce\274\207\241\214\337\374\230\264\314ci\222\201\012", + "\221d\217d\226\202\042\335e\267\042: \311\335\200\272\211way\2071 \323", + "\203\233\365m\203\224\200\303\335\200\372\042\335e\267\331\363\332\323", + "\217\221a\275\254\200\343\233\012", + "\252\330\272a\253gn\235\306 its\341f \323", + "m\225\200\203i\205\211l\210\207\270\355\212um \361\341d\313", + "l\212g\270 \304\203i\205\211l\264\271ce\274\207\335\200\304\270\200\212um \361\341d\012", + "\203\233x \373mis\345\275 \323", + "\220 i\364le\234\325\237f\255\315\200\216 \372\366\227\316\220 f\211l-back\012", + "\315\200specif\326a\237\332\370w\204\206\233cl\333\237\272ig\220\221d\012", + "outpu\202\361\352\272writt\212\316bu\202\362\270 \343\364ac\202\212\343d\357\334s\254\274\012", + "\315\200\330\216 s\340dow\207\252g\344b\337\301\322", + "\273\272\233pr\326\224\235\317) \213\012", + "pu\236\326 \273lack\207\370w\204\206\233cl\333\237\323", + "\217k\220w\336p\333met\264\372\375bs\205tu\237(\203c\225\221c\202#\327\200p\224\365n\232" +#endif + }; diff --git a/compiler-init/sc6.c b/compiler-init/sc6.c new file mode 100644 index 00000000..9dd5a169 --- /dev/null +++ b/compiler-init/sc6.c @@ -0,0 +1,1298 @@ +/* Pawn compiler - Binary code generation (the "assembler") + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc6.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include /* for macro max() */ +#include /* for macro offsetof() */ +#include +#include +#if defined FORTIFY + #include +#endif +#include "lstring.h" +#include "sc.h" +#include "amxdbg.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + + +static void append_dbginfo(FILE *fout); + + +typedef cell (*OPCODE_PROC)(FILE *fbin,char *params,cell opcode); + +typedef struct { + cell opcode; + char *name; + int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */ + OPCODE_PROC func; +} OPCODE; + +static cell codeindex; /* similar to "code_idx" */ +static cell *lbltab; /* label table */ +static int writeerror; +static int bytes_in, bytes_out; +static jmp_buf compact_err; + +/* apparently, strtol() does not work correctly on very large (unsigned) + * hexadecimal values */ +static ucell hex2long(const char *s,char **n) +{ + ucell result=0L; + int negate=FALSE; + int digit; + + /* ignore leading whitespace */ + while (*s==' ' || *s=='\t') + s++; + + /* allow a negation sign to create the two's complement of numbers */ + if (*s=='-') { + negate=TRUE; + s++; + } /* if */ + + assert((*s>='0' && *s<='9') || (*s>='a' && *s<='f') || (*s>='a' && *s<='f')); + for ( ;; ) { + if (*s>='0' && *s<='9') + digit=*s-'0'; + else if (*s>='a' && *s<='f') + digit=*s-'a' + 10; + else if (*s>='A' && *s<='F') + digit=*s-'A' + 10; + else + break; /* probably whitespace */ + result=(result<<4) | digit; + s++; + } /* for */ + if (n!=NULL) + *n=(char*)s; + if (negate) + result=(~result)+1; /* take two's complement of the result */ + return (ucell)result; +} + +static ucell getparam(const char *s,char **n) +{ + ucell result=0; + for ( ;; ) { + result+=hex2long(s,(char**)&s); + if (*s!='+') + break; + s++; + } /* for */ + if (n!=NULL) + *n=(char*)s; + return result; +} + +#if BYTE_ORDER==BIG_ENDIAN +static uint16_t *align16(uint16_t *v) +{ + unsigned char *s = (unsigned char *)v; + unsigned char t; + + /* swap two bytes */ + t=s[0]; + s[0]=s[1]; + s[1]=t; + return v; +} + +static uint32_t *align32(uint32_t *v) +{ + unsigned char *s = (unsigned char *)v; + unsigned char t; + + /* swap outer two bytes */ + t=s[0]; + s[0]=s[3]; + s[3]=t; + /* swap inner two bytes */ + t=s[1]; + s[1]=s[2]; + s[2]=t; + return v; +} + +#if PAWN_CELL_SIZE>=64 +static uint64_t *align64(uint64_t *v) +{ + unsigned char *s = (unsigned char *)v; + unsigned char t; + + t=s[0]; + s[0]=s[7]; + s[7]=t; + + t=s[1]; + s[1]=s[6]; + s[6]=t; + + t=s[2]; + s[2]=s[5]; + s[5]=t; + + t=s[3]; + s[3]=s[4]; + s[4]=t; + + return v; +} +#endif + + #if PAWN_CELL_SIZE==16 + #define aligncell(v) align16(v) + #elif PAWN_CELL_SIZE==32 + #define aligncell(v) align32(v) + #elif PAWN_CELL_SIZE==64 + #define aligncell(v) align64(v) + #endif +#else + #define align16(v) (v) + #define align32(v) (v) + #define aligncell(v) (v) +#endif + +static char *skipwhitespace(char *str) +{ + while (isspace(*str)) + str++; + return str; +} + +static char *stripcomment(char *str) +{ + char *ptr=strchr(str,';'); + if (ptr!=NULL) { + *ptr++='\n'; /* terminate the line, but leave the '\n' */ + *ptr='\0'; + } /* if */ + return str; +} + +static void write_encoded(FILE *fbin,ucell *c,int num) +{ + #if PAWN_CELL_SIZE == 16 + #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ + #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ + #elif PAWN_CELL_SIZE == 32 + #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ + #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ + #elif PAWN_CELL_SIZE == 64 + #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ + #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ + #endif + + assert(fbin!=NULL); + while (num-->0) { + if (sc_compress) { + ucell p=(ucell)*c; + unsigned char t[ENC_MAX]; + unsigned char code; + int index; + for (index=0; index>=7; + } /* for */ + /* skip leading zeros */ + while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) + index--; + /* skip leading -1s */ + if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) + index--; + while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) + index--; + /* write high byte first, write continuation bits */ + assert(index>0); + while (index-->0) { + code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); + writeerror |= !pc_writebin(fbin,&code,1); + bytes_out++; + } /* while */ + bytes_in+=sizeof *c; + assert(AMX_COMPACTMARGIN>2); + if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) + longjmp(compact_err,1); + } else { + assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); + writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); + } /* if */ + c++; + } /* while */ +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell noop(FILE *fbin,char *params,cell opcode) +{ + return 0; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell set_currentfile(FILE *fbin,char *params,cell opcode) +{ + fcurrent=(short)getparam(params,NULL); + return 0; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell parm0(FILE *fbin,char *params,cell opcode) +{ + if (fbin!=NULL) + write_encoded(fbin,(ucell*)&opcode,1); + return opcodes(1); +} + +static cell parm1(FILE *fbin,char *params,cell opcode) +{ + ucell p=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p,1); + } /* if */ + return opcodes(1)+opargs(1); +} + +static cell parm2(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + } /* if */ + return opcodes(1)+opargs(2); +} + +static cell parm3(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,¶ms); + ucell p3=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + write_encoded(fbin,&p3,1); + } /* if */ + return opcodes(1)+opargs(3); +} + +static cell parm4(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,¶ms); + ucell p3=getparam(params,¶ms); + ucell p4=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + write_encoded(fbin,&p3,1); + write_encoded(fbin,&p4,1); + } /* if */ + return opcodes(1)+opargs(4); +} + +static cell parm5(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,¶ms); + ucell p3=getparam(params,¶ms); + ucell p4=getparam(params,¶ms); + ucell p5=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + write_encoded(fbin,&p3,1); + write_encoded(fbin,&p4,1); + write_encoded(fbin,&p5,1); + } /* if */ + return opcodes(1)+opargs(5); +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell do_dump(FILE *fbin,char *params,cell opcode) +{ + ucell p; + int num = 0; + + while (*params!='\0') { + p=getparam(params,¶ms); + if (fbin!=NULL) + write_encoded(fbin,&p,1); + num++; + while (isspace(*params)) + params++; + } /* while */ + return num*sizeof(cell); +} + +static cell do_call(FILE *fbin,char *params,cell opcode) +{ + char name[sNAMEMAX+1]; + int i; + symbol *sym; + ucell p; + + for (i=0; !isspace(*params); i++,params++) { + assert(*params!='\0'); + assert(i=0 && iident==iFUNCTN || sym->ident==iREFFUNC); + assert(sym->vclass==sGLOBAL); + p=sym->addr; + } /* if */ + + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p,1); + } /* if */ + return opcodes(1)+opargs(1); +} + +static cell do_jump(FILE *fbin,char *params,cell opcode) +{ + int i; + ucell p; + + i=(int)hex2long(params,NULL); + assert(i>=0 && i=0 && i=0 && i=MAX_INSTR_LEN) + return 0; + strlcpy(str,instr,maxlen+1); + /* look up the instruction with a binary search + * the assembler is case insensitive to instructions (but case sensitive + * to symbols) + */ + low=1; /* entry 0 is reserved (for "not found") */ + high=(sizeof opcodelist / sizeof opcodelist[0])-1; + while (low0) + low=mid+1; + else + high=mid; + } /* while */ + + assert(low==high); + if (stricmp(str,opcodelist[low].name)==0) + return low; /* found */ + return 0; /* not found, return special index */ +} + +SC_FUNC int assemble(FILE *fout,FILE *fin) +{ + AMX_HEADER hdr; + AMX_FUNCSTUBNT func; + int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; + long nametablesize,nameofs; + #if PAWN_CELL_SIZE > 32 + char line[512]; + #else + char line[256]; + #endif + char *instr,*params; + int i,pass,size; + int16_t count; + symbol *sym, **nativelist; + constvalue *constptr; + cell mainaddr; + char nullchar = 0; + + /* if compression failed, restart the assembly with compaction switched off */ + if (setjmp(compact_err)!=0) { + assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ + sc_compress=FALSE; + pc_resetbin(fout,0); + error(232); /* disabled compact encoding */ + } /* if */ + + #if !defined NDEBUG + /* verify that the opcode list is sorted (skip entry 1; it is reserved + * for a non-existant opcode) + */ + assert(opcodelist[1].name!=NULL); + for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { + assert(opcodelist[i].name!=NULL); + assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); + } /* for */ + #endif + + writeerror=FALSE; + nametablesize=sizeof(int16_t); + numpublics=0; + numnatives=0; + numpubvars=0; + mainaddr=-1; + /* count number of public and native functions and public variables */ + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + int match=0; + if (sym->ident==iFUNCTN) { + if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) + match=++numnatives; + if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) + match=++numpublics; + if (strcmp(sym->name,uMAINFUNC)==0) { + assert(sym->vclass==sGLOBAL); + mainaddr=sym->addr; + } /* if */ + } else if (sym->ident==iVARIABLE) { + if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) + match=++numpubvars; + } /* if */ + if (match) { + char alias[sNAMEMAX+1]; + assert(sym!=NULL); + if ((sym->usage & uNATIVE)==0 || !lookup_alias(alias,sym->name)) { + assert(strlen(sym->name)<=sNAMEMAX); + strcpy(alias,sym->name); + } /* if */ + nametablesize+=strlen(alias)+1; + } /* if */ + } /* for */ + assert(numnatives==ntv_funcid); + + /* count number of libraries */ + numlibraries=0; + if (pc_addlibtable) { + for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { + if (constptr->value>0) { + assert(strlen(constptr->name)>0); + numlibraries++; + nametablesize+=strlen(constptr->name)+1; + } /* if */ + } /* for */ + } /* if */ + + /* count number of public tags */ + numtags=0; + for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + if ((constptr->value & PUBLICTAG)!=0) { + assert(strlen(constptr->name)>0); + numtags++; + nametablesize+=strlen(constptr->name)+1; + } /* if */ + } /* for */ + + /* pad the header to sc_dataalign + * => thereby the code segment is aligned + * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned + * => and thereby the stack top is aligned too + */ + assert(sc_dataalign!=0); + padding= (int)(sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign); + if (padding==sc_dataalign) + padding=0; + + /* write the abstract machine header */ + memset(&hdr, 0, sizeof hdr); + hdr.magic=(unsigned short)AMX_MAGIC; + hdr.file_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MAX_FILE_VER_JIT : CUR_FILE_VERSION); + hdr.amx_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MIN_AMX_VER_JIT : MIN_AMX_VERSION); + hdr.flags=(short)(sc_debug & sSYMBOLIC); + if (sc_compress) + hdr.flags|=AMX_FLAG_COMPACT; + if (sc_debug==0) + hdr.flags|=AMX_FLAG_NOCHECKS; + if (pc_memflags & suSLEEP_INSTR) + hdr.flags|=AMX_FLAG_SLEEP; + hdr.defsize=sizeof(AMX_FUNCSTUBNT); + hdr.publics=sizeof hdr; /* public table starts right after the header */ + hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); + hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); + hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); + hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); + hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); + hdr.cod=hdr.nametable + nametablesize + padding; + hdr.dat=hdr.cod + code_idx; + hdr.hea=hdr.dat + glb_declared*sizeof(cell); + hdr.stp=hdr.hea + pc_stksize*sizeof(cell); + hdr.cip=mainaddr; + hdr.size=hdr.hea; /* preset, this is incorrect in case of compressed output */ + pc_writebin(fout,&hdr,sizeof hdr); + + /* dump zeros up to the rest of the header, so that we can easily "seek" */ + for (nameofs=sizeof hdr; nameofsnext) { + if (sym->ident==iFUNCTN + && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) + { + assert(sym->vclass==sGLOBAL); + func.address=sym->addr; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,sym->name,strlen(sym->name)+1); + nameofs+=strlen(sym->name)+1; + count++; + } /* if */ + } /* for */ + + /* write the natives table */ + /* The native functions must be written in sorted order. (They are + * sorted on their "id", not on their name). A nested loop to find + * each successive function would be an O(n^2) operation. But we + * do not really need to sort, because the native function id's + * are sequential and there are no duplicates. So we first walk + * through the complete symbol list and store a pointer to every + * native function of interest in a temporary table, where its id + * serves as the index in the table. Now we can walk the table and + * have all native functions in sorted order. + */ + if (numnatives>0) { + nativelist=(symbol **)malloc(numnatives*sizeof(symbol *)); + if (nativelist==NULL) + error(103); /* insufficient memory */ + #if !defined NDEBUG + memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ + #endif + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN && (sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { + assert(sym->addr < numnatives); + nativelist[(int)sym->addr]=sym; + } /* if */ + } /* for */ + count=0; + for (i=0; iname)) { + assert(strlen(sym->name)<=sNAMEMAX); + strcpy(alias,sym->name); + } /* if */ + assert(sym->vclass==sGLOBAL); + func.address=0; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,alias,strlen(alias)+1); + nameofs+=strlen(alias)+1; + count++; + } /* for */ + free(nativelist); + } /* if */ + + /* write the libraries table */ + if (pc_addlibtable) { + count=0; + for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { + if (constptr->value>0) { + assert(strlen(constptr->name)>0); + func.address=0; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + nameofs+=strlen(constptr->name)+1; + count++; + } /* if */ + } /* for */ + } /* if */ + + /* write the public variables table */ + count=0; + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iVARIABLE && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { + assert((sym->usage & uDEFINE)!=0); + assert(sym->vclass==sGLOBAL); + func.address=sym->addr; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,sym->name,strlen(sym->name)+1); + nameofs+=strlen(sym->name)+1; + count++; + } /* if */ + } /* for */ + + /* write the public tagnames table */ + count=0; + for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + if ((constptr->value & PUBLICTAG)!=0) { + assert(strlen(constptr->name)>0); + func.address=constptr->value & TAGMASK; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + nameofs+=strlen(constptr->name)+1; + count++; + } /* if */ + } /* for */ + + /* write the "maximum name length" field in the name table */ + assert(nameofs==hdr.nametable+nametablesize); + pc_resetbin(fout,hdr.nametable); + count=sNAMEMAX; + #if BYTE_ORDER==BIG_ENDIAN + align16(&count); + #endif + pc_writebin(fout,&count,sizeof count); + pc_resetbin(fout,hdr.cod); + + /* First pass: relocate all labels */ + /* This pass is necessary because the code addresses of labels is only known + * after the peephole optimization flag. Labels can occur inside expressions + * (e.g. the conditional operator), which are optimized. + */ + lbltab=NULL; + if (sc_labnum>0) { + /* only very short programs have zero labels; no first pass is needed + * if there are no labels */ + lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); + if (lbltab==NULL) + error(103); /* insufficient memory */ + codeindex=0; + pc_resetasm(fin); + while (pc_readasm(fin,line,sizeof line)!=NULL) { + stripcomment(line); + instr=skipwhitespace(line); + /* ignore empty lines */ + if (*instr=='\0') + continue; + if (tolower(*instr)=='l' && *(instr+1)=='.') { + int lindex=(int)hex2long(instr+2,NULL); + assert(lindex>=0 && lindexinstr); + i=findopcode(instr,(int)(params-instr)); + if (opcodelist[i].name==NULL) { + *params='\0'; + error(104,instr); /* invalid assembler instruction */ + } /* if */ + if (opcodelist[i].segment==sIN_CSEG) + codeindex+=opcodelist[i].func(NULL,skipwhitespace(params),opcodelist[i].opcode); + } /* if */ + } /* while */ + } /* if */ + + /* Second pass (actually 2 more passes, one for all code and one for all data) */ + bytes_in=0; + bytes_out=0; + for (pass=sIN_CSEG; pass<=sIN_DSEG; pass++) { + pc_resetasm(fin); + while (pc_readasm(fin,line,sizeof line)!=NULL) { + stripcomment(line); + instr=skipwhitespace(line); + /* ignore empty lines and labels (labels have a special syntax, so these + * must be parsed separately) */ + if (*instr=='\0' || tolower(*instr)=='l' && *(instr+1)=='.') + continue; + /* get to the end of the instruction (make use of the '\n' that fgets() + * added at the end of the line; this way we will *always* drop on a + * whitespace character) */ + for (params=instr; *params!='\0' && !isspace(*params); params++) + /* nothing */; + assert(params>instr); + i=findopcode(instr,(int)(params-instr)); + assert(opcodelist[i].name!=NULL); + if (opcodelist[i].segment==pass) + opcodelist[i].func(fout,skipwhitespace(params),opcodelist[i].opcode); + } /* while */ + } /* for */ + if (bytes_out-bytes_in>0) + error(106); /* compression buffer overflow */ + + if (lbltab!=NULL) { + free(lbltab); + #if !defined NDEBUG + lbltab=NULL; + #endif + } /* if */ + + if (sc_compress) + hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ + if (!writeerror && (sc_debug & sSYMBOLIC)!=0) + append_dbginfo(fout); /* optionally append debug file */ + + if (writeerror) + error(101,"disk full"); + + /* adjust the header */ + size=(int)hdr.cod; /* save, the value in the header may be swapped */ + #if BYTE_ORDER==BIG_ENDIAN + align32(&hdr.size); + align16(&hdr.magic); + align16(&hdr.flags); + align16(&hdr.defsize); + align32(&hdr.publics); + align32(&hdr.natives); + align32(&hdr.libraries); + align32(&hdr.pubvars); + align32(&hdr.tags); + align32(&hdr.nametable); + align32(&hdr.cod); + align32(&hdr.dat); + align32(&hdr.hea); + align32(&hdr.stp); + align32(&hdr.cip); + #endif + pc_resetbin(fout,0); + pc_writebin(fout,&hdr,sizeof hdr); + + /* return the size of the header (including name tables, but excluding code + * or data sections) + */ + return size; +} + +static void append_dbginfo(FILE *fout) +{ + AMX_DBG_HDR dbghdr; + AMX_DBG_LINE dbgline; + AMX_DBG_SYMBOL dbgsym; + AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; + int index,dim,dbgsymdim; + char *str,*prevstr,*name,*prevname; + ucell codeidx,previdx; + constvalue *constptr; + char symname[2*sNAMEMAX+16]; + int16_t id1,id2; + ucell address; + + /* header with general information */ + memset(&dbghdr, 0, sizeof dbghdr); + dbghdr.size=sizeof dbghdr; + dbghdr.magic=AMX_DBG_MAGIC; + dbghdr.file_version=CUR_FILE_VERSION; + dbghdr.amx_version=MIN_AMX_VERSION; + + /* first pass: collect the number of items in various tables */ + + /* file table */ + previdx=0; + prevstr=NULL; + prevname=NULL; + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='F') { + codeidx=hex2long(str+2,&name); + if (codeidx!=previdx) { + if (prevstr!=NULL) { + assert(prevname!=NULL); + dbghdr.files++; + dbghdr.size+=sizeof(cell)+strlen(prevname)+1; + } /* if */ + previdx=codeidx; + } /* if */ + prevstr=str; + prevname=skipwhitespace(name); + } /* if */ + } /* for */ + if (prevstr!=NULL) { + assert(prevname!=NULL); + dbghdr.files++; + dbghdr.size+=sizeof(cell)+strlen(prevname)+1; + } /* if */ + + /* line number table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='L') { + dbghdr.lines++; + dbghdr.size+=sizeof(AMX_DBG_LINE); + } /* if */ + } /* for */ + + /* symbol table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='S') { + dbghdr.symbols++; + name=strchr(str+2,':'); + assert(name!=NULL); + dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); + if ((prevstr=strchr(name,'['))!=NULL) + while ((prevstr=strchr(prevstr+1,':'))!=NULL) + dbghdr.size+=sizeof(AMX_DBG_SYMDIM); + } /* if */ + } /* for */ + + /* tag table */ + for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(strlen(constptr->name)>0); + dbghdr.tags++; + dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); + } /* for */ + + /* automaton table */ + for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); + dbghdr.automatons++; + dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); + } /* for */ + + /* state table */ + for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(strlen(constptr->name)>0); + dbghdr.states++; + dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); + } /* for */ + + + /* pass 2: generate the tables */ + #if BYTE_ORDER==BIG_ENDIAN + align32((uint32_t*)&dbghdr.size); + align16(&dbghdr.magic); + align16(&dbghdr.flags); + align16(&dbghdr.files); + align16(&dbghdr.lines); + align16(&dbghdr.symbols); + align16(&dbghdr.tags); + align16(&dbghdr.automatons); + align16(&dbghdr.states); + #endif + writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); + + /* file table */ + previdx=0; + prevstr=NULL; + prevname=NULL; + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='F') { + codeidx=hex2long(str+2,&name); + if (codeidx!=previdx) { + if (prevstr!=NULL) { + assert(prevname!=NULL); + #if BYTE_ORDER==BIG_ENDIAN + aligncell(&previdx); + #endif + writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); + writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); + } /* if */ + previdx=codeidx; + } /* if */ + prevstr=str; + prevname=skipwhitespace(name); + } /* if */ + } /* for */ + if (prevstr!=NULL) { + assert(prevname!=NULL); + #if BYTE_ORDER==BIG_ENDIAN + aligncell(&previdx); + #endif + writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); + writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); + } /* if */ + + /* line number table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='L') { + dbgline.address=hex2long(str+2,&str); + dbgline.line=(int32_t)hex2long(str,NULL); + #if BYTE_ORDER==BIG_ENDIAN + aligncell(&dbgline.address); + align32(&dbgline.line); + #endif + writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); + } /* if */ + } /* for */ + + /* symbol table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='S') { + dbgsym.address=hex2long(str+2,&str); + dbgsym.tag=(int16_t)hex2long(str,&str); + str=skipwhitespace(str); + assert(*str==':'); + name=skipwhitespace(str+1); + str=strchr(name,' '); + assert(str!=NULL); + assert((int)(str-name)next) { + assert(strlen(constptr->name)>0); + id1=(int16_t)(constptr->value & TAGMASK); + #if BYTE_ORDER==BIG_ENDIAN + align16(&id1); + #endif + writeerror |= !pc_writebin(fout,&id1,sizeof id1); + writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + } /* for */ + + /* automaton table */ + for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); + id1=(int16_t)constptr->index; + address=(ucell)constptr->value; + #if BYTE_ORDER==BIG_ENDIAN + align16(&id1); + aligncell(&address); + #endif + writeerror |= !pc_writebin(fout,&id1,sizeof id1); + writeerror |= !pc_writebin(fout,&address,sizeof address); + writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + } /* for */ + + /* state table */ + for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(strlen(constptr->name)>0); + id1=(int16_t)constptr->value; + id2=(int16_t)constptr->index; + address=(ucell)constptr->value; + #if BYTE_ORDER==BIG_ENDIAN + align16(&id1); + align16(&id2); + #endif + writeerror |= !pc_writebin(fout,&id1,sizeof id1); + writeerror |= !pc_writebin(fout,&id2,sizeof id2); + writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + } /* for */ + + delete_dbgstringtable(); +} diff --git a/compiler-init/sc7.c b/compiler-init/sc7.c new file mode 100644 index 00000000..f5a52d5a --- /dev/null +++ b/compiler-init/sc7.c @@ -0,0 +1,703 @@ +/* Pawn compiler - Staging buffer and optimizer + * + * The staging buffer + * ------------------ + * The staging buffer allows buffered output of generated code, deletion + * of redundant code, optimization by a tinkering process and reversing + * the ouput of evaluated expressions (which is used for the reversed + * evaluation of arguments in functions). + * Initially, stgwrite() writes to the file directly, but after a call to + * stgset(TRUE), output is redirected to the buffer. After a call to + * stgset(FALSE), stgwrite()'s output is directed to the file again. Thus + * only one routine is used for writing to the output, which can be + * buffered output or direct output. + * + * staging buffer variables: stgbuf - the buffer + * stgidx - current index in the staging buffer + * staging - if true, write to the staging buffer; + * if false, write to file directly. + * + * The peephole optimizer uses a dual "pipeline". The staging buffer (described + * above) gets optimized for each expression or sub-expression in a function + * call. The peephole optimizer is recursive, but it does not span multiple + * sub-expressions. However, the data gets written to a second buffer that + * behaves much like the staging buffer. This second buffer gathers all + * optimized strings from the staging buffer for a complete expression. The + * peephole optmizer then runs over this second buffer to find optimzations + * across function parameter boundaries. + * + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc7.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include /* for atoi() */ +#include +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +#if defined _MSC_VER + #pragma warning(push) + #pragma warning(disable:4125) /* decimal digit terminates octal escape sequence */ +#endif + +#include "sc7.scp" + +#if defined _MSC_VER + #pragma warning(pop) +#endif + +static int stgstring(char *start,char *end); +static void stgopt(char *start,char *end,int (*outputfunc)(char *str)); + + +#define sSTG_GROW 512 +#define sSTG_MAX 20480 + +static char *stgbuf=NULL; +static int stgmax=0; /* current size of the staging buffer */ + +static char *stgpipe=NULL; +static int pipemax=0; /* current size of the stage pipe, a second staging buffer */ +static int pipeidx=0; + +#define CHECK_STGBUFFER(index) if ((int)(index)>=stgmax) grow_stgbuffer(&stgbuf, stgmax, (index)+1) +#define CHECK_STGPIPE(index) if ((int)(index)>=pipemax) grow_stgbuffer(&stgpipe, pipemax, (index)+1) + +static void grow_stgbuffer(char **buffer, int curmax, int requiredsize) +{ + char *p; + int clear= (*buffer==NULL); /* if previously none, empty buffer explicitly */ + + assert(curmaxsSTG_MAX) + error(102,"staging buffer"); /* staging buffer overflow (fatal error) */ + curmax=requiredsize+sSTG_GROW; + if (*buffer!=NULL) + p=(char *)realloc(*buffer,curmax*sizeof(char)); + else + p=(char *)malloc(curmax*sizeof(char)); + if (p==NULL) + error(102,"staging buffer"); /* staging buffer overflow (fatal error) */ + *buffer=p; + if (clear) + **buffer='\0'; +} + +SC_FUNC void stgbuffer_cleanup(void) +{ + if (stgbuf!=NULL) { + free(stgbuf); + stgbuf=NULL; + stgmax=0; + } /* if */ + if (stgpipe!=NULL) { + free(stgpipe); + stgpipe=NULL; + pipemax=0; + pipeidx=0; + } /* if */ +} + +/* the variables "stgidx" and "staging" are declared in "scvars.c" */ + +/* stgmark + * + * Copies a mark into the staging buffer. At this moment there are three + * possible marks: + * sSTARTREORDER identifies the beginning of a series of expression + * strings that must be written to the output file in + * reordered order + * sENDREORDER identifies the end of 'reverse evaluation' + * sEXPRSTART + idx only valid within a block that is evaluated in + * reordered order, it identifies the start of an + * expression; the "idx" value is the argument position + * + * Global references: stgidx (altered) + * stgbuf (altered) + * staging (referred to only) + */ +SC_FUNC void stgmark(char mark) +{ + if (staging) { + CHECK_STGBUFFER(stgidx); + stgbuf[stgidx++]=mark; + } /* if */ +} + +static int rebuffer(char *str) +{ + if (sc_status==statWRITE) { + if (pipeidx>=2 && stgpipe[pipeidx-1]=='\0' && stgpipe[pipeidx-2]!='\n') + pipeidx-=1; /* overwrite last '\0' */ + while (*str!='\0') { /* copy to staging buffer */ + CHECK_STGPIPE(pipeidx); + stgpipe[pipeidx++]=*str++; + } /* while */ + CHECK_STGPIPE(pipeidx); + stgpipe[pipeidx++]='\0'; + } /* if */ + return TRUE; +} + +static int filewrite(char *str) +{ + if (sc_status==statWRITE) + return pc_writeasm(outf,str); + return TRUE; +} + +/* stgwrite + * + * Writes the string "st" to the staging buffer or to the output file. In the + * case of writing to the staging buffer, the terminating byte of zero is + * copied too, but... the optimizer can only work on complete lines (not on + * fractions of it. Therefore if the string is staged, if the last character + * written to the buffer is a '\0' and the previous-to-last is not a '\n', + * the string is concatenated to the last string in the buffer (the '\0' is + * overwritten). This also means an '\n' used in the middle of a string isn't + * recognized and could give wrong results with the optimizer. + * Even when writing to the output file directly, all strings are buffered + * until a whole line is complete. + * + * Global references: stgidx (altered) + * stgbuf (altered) + * staging (referred to only) + */ +SC_FUNC void stgwrite(const char *st) +{ + int len; + + if (staging) { + assert(stgidx==0 || stgbuf!=NULL); /* staging buffer must be valid if there is (apparently) something in it */ + if (stgidx>=2 && stgbuf[stgidx-1]=='\0' && stgbuf[stgidx-2]!='\n') + stgidx-=1; /* overwrite last '\0' */ + while (*st!='\0') { /* copy to staging buffer */ + CHECK_STGBUFFER(stgidx); + stgbuf[stgidx++]=*st++; + } /* while */ + CHECK_STGBUFFER(stgidx); + stgbuf[stgidx++]='\0'; + } else { + len=(stgbuf!=NULL) ? strlen(stgbuf) : 0; + CHECK_STGBUFFER(len+strlen(st)+1); + strcat(stgbuf,st); + len=strlen(stgbuf); + if (len>0 && stgbuf[len-1]=='\n') { + filewrite(stgbuf); + stgbuf[0]='\0'; + } /* if */ + } /* if */ +} + +/* stgout + * + * Writes the staging buffer to the output file via stgstring() (for + * reversing expressions in the buffer) and stgopt() (for optimizing). It + * resets "stgidx". + * + * Global references: stgidx (altered) + * stgbuf (referred to only) + * staging (referred to only) + */ +SC_FUNC void stgout(int index) +{ + int reordered=0; + int idx; + + if (!staging) + return; + assert(pipeidx==0); + + /* first pass: sub-expressions */ + if (sc_status==statWRITE) + reordered=stgstring(&stgbuf[index],&stgbuf[stgidx]); + stgidx=index; + + /* second pass: optimize the buffer created in the first pass */ + if (sc_status==statWRITE) { + if (reordered) { + stgopt(stgpipe,stgpipe+pipeidx,filewrite); + } else { + /* there is no sense in re-optimizing if the order of the sub-expressions + * did not change; so output directly + */ + for (idx=0; idx=0) + stack[arg].end=start-1; /* finish previous argument */ + arg=(unsigned char)*start - sEXPRSTART; + stack[arg].start=start+1; + if (arg>=argc) + argc=arg+1; + } /* if */ + start++; + } else { + start+=strlen(start)+1; + } /* if */ + } /* switch */ + } while (nest); /* enddo */ + if (arg>=0) + stack[arg].end=start-1; /* finish previous argument */ + while (argc>0) { + argc--; + stgstring(stack[argc].start,stack[argc].end); + } /* while */ + free(stack); + } else { + ptr=start; + while (ptr0) + filewrite(stgbuf); + } /* if */ + stgbuf[0]='\0'; +} + +/* phopt_init + * Initialize all sequence strings of the peehole optimizer. The strings + * are embedded in the .EXE file in compressed format, here we expand + * them (and allocate memory for the sequences). + */ +static SEQUENCE *sequences; + +SC_FUNC int phopt_init(void) +{ + int number, i, len; + char str[160]; + + /* count number of sequences */ + for (number=0; sequences_cmp[number].find!=NULL; number++) + /* nothing */; + number++; /* include an item for the NULL terminator */ + + if ((sequences=(SEQUENCE*)malloc(number * sizeof(SEQUENCE)))==NULL) + return FALSE; + + /* pre-initialize all to NULL (in case of failure) */ + for (i=0; i (PAWN_CELL_SIZE/4) * MAX_OPT_CAT + #define MAX_ALIAS sNAMEMAX +#else + #define MAX_ALIAS (PAWN_CELL_SIZE/4) * MAX_OPT_CAT +#endif + +static int matchsequence(char *start,char *end,char *pattern, + char symbols[MAX_OPT_VARS][MAX_ALIAS+1], + int *match_length) +{ + int var,i; + char str[MAX_ALIAS+1]; + char *start_org=start; + cell value; + char *ptr; + + *match_length=0; + for (var=0; var=end) + return FALSE; + switch (*pattern) { + case '%': /* new "symbol" */ + pattern++; + assert(isdigit(*pattern)); + var=atoi(pattern) - 1; + assert(var>=0 && var=0 && var=0 && var0) { /* delete a section */ + memmove(dest,dest+offset,dest_length-offset); + memset(dest+dest_length-offset,0xcc,offset); /* not needed, but for cleanlyness */ + } else if (offset<0) { /* insert a section */ + memmove(dest-offset, dest, dest_length); + } /* if */ + memcpy(dest, replace, repl_length); +} + +/* stgopt + * + * Optimizes the staging buffer by checking for series of instructions that + * can be coded more compact. The routine expects the lines in the staging + * buffer to be separated with '\n' and '\0' characters. + * + * The longest sequences should probably be checked first. + */ + +static void stgopt(char *start,char *end,int (*outputfunc)(char *str)) +{ + char symbols[MAX_OPT_VARS][MAX_ALIAS+1]; + int seq,match_length,repl_length; + int matches; + char *debut=start; /* save original start of the buffer */ + + assert(sequences!=NULL); + /* do not match anything if debug-level is maximum */ + if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) { + do { + matches=0; + start=debut; + while (start=0); + if (*sequences[seq].find=='\0') { + if (pc_optimize==sOPTIMIZE_NOMACRO) { + break; /* don't look further */ + } else { + seq++; /* continue with next string */ + continue; + } /* if */ + } /* if */ + if (matchsequence(start,end,sequences[seq].find,symbols,&match_length)) { + char *replace=replacesequence(sequences[seq].replace,symbols,&repl_length); + /* If the replacement is bigger than the original section, we may need + * to "grow" the staging buffer. This is quite complex, due to the + * re-ordering of expressions that can also happen in the staging + * buffer. In addition, it should not happen: the peephole optimizer + * must replace sequences with *shorter* sequences, not longer ones. + * So, I simply forbid sequences that are longer than the ones they + * are meant to replace. + */ + assert(match_length>=repl_length); + if (match_length>=repl_length) { + strreplace(start,replace,match_length,repl_length,(int)(end-start)); + end-=match_length-repl_length; + free(replace); + code_idx-=sequences[seq].savesize; + seq=0; /* restart search for matches */ + matches++; + } else { + /* actually, we should never get here (match_length0); + } /* if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) */ + + for (start=debut; start ;$lcl + * stack -4 push.c + * const.pri ;$exp + * stor.s.pri - + * ;$exp - + */ + { + #ifdef SCPACK + ";$lcl %1 %2!stack -4!const.pri %3!stor.s.pri %2!;$exp!", + ";$lcl %1 %2!push.c %3!;$exp!", + #else + "\224lcl\332\227ack -4!\233\206\256\227or\222\230\252", + "\224lcl\332\331\256\252", + #endif + seqsize(3,3) - seqsize(1,1) + }, + { + #ifdef SCPACK + ";$lcl %1 %2!stack -4!zero.pri!stor.s.pri %2!;$exp!", + ";$lcl %1 %2!push.c 0!;$exp!", + #else + "\224lcl\332\227ack -4!\373\227or\222\230\252", + "\224lcl\332\331 0!\252", + #endif + seqsize(3,2) - seqsize(1,1) + }, + /* During a calculation, the intermediate result must sometimes + * be moved from PRI to ALT, like in: + * push.pri move.alt + * load.s.pri n1 load.s.pri n1 + * pop.alt - + * + * The above also accurs for "load.pri" and for "const.pri", + * so add another two cases. + */ + { + #ifdef SCPACK + "push.pri!load.s.pri %1!pop.alt!", + "move.alt!load.s.pri %1!", + #else + "\244\333\241", + "\306\333", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "push.pri!load.pri %1!pop.alt!", + "move.alt!load.pri %1!", + #else + "\244\311\241", + "\306\311", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "push.pri!const.pri %1!pop.alt!", + "move.alt!const.pri %1!", + #else + "\244\330\241", + "\306\330", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "push.pri!zero.pri!pop.alt!", + "move.alt!zero.pri!", + #else + "\244\373\241", + "\306\373", + #endif + seqsize(3,0) - seqsize(2,0) + }, + /* saving PRI and then loading from its address + * occurs when indexing a multi-dimensional array + */ + { + #ifdef SCPACK + "push.pri!load.i!pop.alt!", + "move.alt!load.i!", + #else + "\244\342i\266", + "\306\342\355", + #endif + seqsize(3,0) - seqsize(2,0) + }, + /* An even simpler PUSH/POP optimization (occurs in + * switch statements): + * push.pri move.alt + * pop.alt - + */ + { + #ifdef SCPACK + "push.pri!pop.alt!", + "move.alt!", + #else + "\244\241", + "\306", + #endif + seqsize(2,0) - seqsize(1,0) + }, + /* Some simple arithmetic sequences + */ + { + #ifdef SCPACK + "move.alt!load.s.pri %1!add!", + "load.s.alt %1!add!", + #else + "\306\333\271", + "\375\271", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!load.pri %1!add!", + "load.alt %1!add!", + #else + "\306\311\271", + "\374\271", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!const.pri %1!add!", + "const.alt %1!add!", + #else + "\306\330\271", + "\356\271", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!load.s.pri %1!sub.alt!", + "load.s.alt %1!sub!", + #else + "\306\333sub\223", + "\375sub!", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!load.pri %1!sub.alt!", + "load.alt %1!sub!", + #else + "\306\311sub\223", + "\374sub!", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!const.pri %1!sub.alt!", + "const.alt %1!sub!", + #else + "\306\330sub\223", + "\356sub!", + #endif + seqsize(3,1) - seqsize(2,1) + }, + /* User-defined operators first load the operands into registers and + * then have them pushed onto the stack. This can give rise to sequences + * like: + * const.pri n1 push.c n1 + * const.alt n2 push.c n2 + * push.pri - + * push.alt - + * A similar sequence occurs with the two PUSH.pri/alt instructions inverted. + * The first, second, or both CONST.pri/alt instructions can also be + * LOAD.pri/alt. + * This gives 2 x 4 cases. + */ + { + #ifdef SCPACK + "const.pri %1!const.alt %2!push.pri!push.alt!", + "push.c %1!push.c %2!", + #else + "\330\233\304\244\354", + "\331\202\331\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!const.alt %2!push.alt!push.pri!", + "push.c %2!push.c %1!", + #else + "\330\233\304\354\244", + "\331\221\331\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!load.alt %2!push.pri!push.alt!", + "push.c %1!push %2!", + #else + "\330\362\244\354", + "\331\202\216\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!load.alt %2!push.alt!push.pri!", + "push %2!push.c %1!", + #else + "\330\362\354\244", + "\216\221\331\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!const.alt %2!push.pri!push.alt!", + "push %1!push.c %2!", + #else + "\311\233\304\244\354", + "\216\202\331\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!const.alt %2!push.alt!push.pri!", + "push.c %2!push %1!", + #else + "\311\233\304\354\244", + "\331\221\216\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!load.alt %2!push.pri!push.alt!", + "push %1!push %2!", + #else + "\311\362\244\354", + "\216\202\216\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!load.alt %2!push.alt!push.pri!", + "push %2!push %1!", + #else + "\311\362\354\244", + "\216\221\216\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + /* Function calls (parameters are passed on the stack) + * load.s.pri n1 push.s n1 + * push.pri - + * -------------------------------------- + * load.pri n1 push n1 + * push.pri - + * -------------------------------------- + * const.pri n1 push.c n1 + * push.pri - + * -------------------------------------- + * zero.pri push.c 0 + * push.pri - + * -------------------------------------- + * addr.pri n1 push.adr n1 + * push.pri - + * + * However, PRI must not be needed after this instruction + * if this shortcut is used. Check for the ;$par comment. + */ + { + #ifdef SCPACK + "load.s.pri %1!push.pri!;$par!", + "push.s %1!;$par!", + #else + "\226\264\255", + "\216\222\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.pri %1!push.pri!;$par!", + "push %1!;$par!", + #else + "\217\264\255", + "\216\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.pri %1!push.pri!;$par!", + "push.c %1!;$par!", + #else + "\233\264\255", + "\331\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.pri!push.pri!;$par!", + "push.c 0!;$par!", + #else + "\373\244\255", + "\331 0!\255", + #endif + seqsize(2,0) - seqsize(1,1) + }, + { + #ifdef SCPACK + "addr.pri %1!push.pri!;$par!", + "push.adr %1!;$par!", + #else + "\265\264\255", + "\216\326\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* References with a default value generate new cells on the heap + * dynamically. That code often ends with: + * move.pri push.alt + * push.pri - + */ + { + #ifdef SCPACK + "move.pri!push.pri!", + "push.alt!", + #else + "\300\236\244", + "\354", + #endif + seqsize(2,0) - seqsize(1,0) + }, + /* Simple arithmetic operations on constants. Noteworthy is the + * subtraction of a constant, since it is converted to the addition + * of the inverse value. + * const.alt n1 add.c n1 + * add - + * -------------------------------------- + * const.alt n1 add.c -n1 + * sub - + * -------------------------------------- + * const.alt n1 smul.c n1 + * smul - + * -------------------------------------- + * const.alt n1 eq.c.pri n1 + * eq - + */ + { + #ifdef SCPACK + "const.alt %1!add!", + "add.c %1!", + #else + "\356\271", + "\235\242\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.alt %1!sub!", + "add.c -%1!", + #else + "\356sub!", + "\235\242 -\201", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.alt %1!smul!", + "smul.c %1!", + #else + "\356smul!", + "smu\307\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.alt %1!eq!", + "eq.c.pri %1!", + #else + "\356\325", + "\262\242\225", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* Some operations use the alternative subtraction operation --these + * can also be optimized. + * const.pri n1 load.s.pri n2 + * load.s.alt n2 add.c -n1 + * sub.alt - + * -------------------------------------- + * const.pri n1 load.pri n2 + * load.alt n2 add.c -n1 + * sub.alt - + */ + { + #ifdef SCPACK + "const.pri %1!load.s.alt %2!sub.alt!", + "load.s.pri %2!add.c -%1!", + #else + "\330\226\304sub\223", + "\246\235\242 -\201", + #endif + seqsize(3,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!load.alt %2!sub.alt!", + "load.pri %2!add.c -%1!", + #else + "\330\362sub\223", + "\334\235\242 -\201", + #endif + seqsize(3,2) - seqsize(2,2) + }, + /* With arrays indexed with constants that come from enumerations, it happens + * multiple add.c opcodes follow in sequence. + * add.c n1 add.c n1+n2 + * add.c n2 - + */ + { + #ifdef SCPACK + "add.c %1!add.c %2!", + "add.c %1+%2!", + #else + "\235\242\202\235\242\221", + "\235\242\267+%2!", + #endif + seqsize(2,2) - seqsize(1,1) + }, + /* Compare and jump + * eq jneq n1 + * jzer n1 - + * -------------------------------------- + * eq jeq n1 + * jnz n1 - + * -------------------------------------- + * neq jeq n1 + * jzer n1 - + * -------------------------------------- + * neq jneq n1 + * jnz n1 - + * Compares followed by jzer occur much more + * often than compares followed with jnz. So we + * take the easy route here. + * less jgeq n1 + * jzer n1 - + * -------------------------------------- + * leq jgrtr n1 + * jzer n1 - + * -------------------------------------- + * grtr jleq n1 + * jzer n1 - + * -------------------------------------- + * geq jless n1 + * jzer n1 - + * -------------------------------------- + * sless jsgeq n1 + * jzer n1 - + * -------------------------------------- + * sleq jsgrtr n1 + * jzer n1 - + * -------------------------------------- + * sgrtr jsleq n1 + * jzer n1 - + * -------------------------------------- + * sgeq jsless n1 + * jzer n1 - + */ + { + #ifdef SCPACK + "eq!jzer %1!", + "jneq %1!", + #else + "\325\323", + "jn\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "eq!jnz %1!", + "jeq %1!", + #else + "\325jnz\202", + "j\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "neq!jzer %1!", + "jeq %1!", + #else + "n\325\323", + "j\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "neq!jnz %1!", + "jneq %1!", + #else + "n\325jnz\202", + "jn\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "less!jzer %1!", + "jgeq %1!", + #else + "l\352!\323", + "jg\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "leq!jzer %1!", + "jgrtr %1!", + #else + "l\325\323", + "jg\353r\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "grtr!jzer %1!", + "jleq %1!", + #else + "g\353\237\323", + "jl\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "geq!jzer %1!", + "jless %1!", + #else + "g\325\323", + "jl\352\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sless!jzer %1!", + "jsgeq %1!", + #else + "\372!\323", + "j\320\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sleq!jzer %1!", + "jsgrtr %1!", + #else + "\321\325\323", + "j\371r\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sgrtr!jzer %1!", + "jsleq %1!", + #else + "\371\237\323", + "j\321\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sgeq!jzer %1!", + "jsless %1!", + #else + "\320\325\323", + "j\372\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* Test for zero (common case, especially for strings) + * E.g. the test expression of: "for (i=0; str{i}!=0; ++i)" + * + * zero.alt jzer n1 + * jeq n1 - + * -------------------------------------- + * zero.alt jnz n1 + * jneq n1 - + */ + { + #ifdef SCPACK + "zero.alt!jeq %1!", + "jzer %1!", + #else + "\340\223j\357", + "\323", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.alt!jneq %1!", + "jnz %1!", + #else + "\340\223jn\357", + "jnz\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* Incrementing and decrementing leaves a value in + * in PRI which may not be used (for example, as the + * third expression in a "for" loop). + * inc n1 inc n1 ; ++n + * load.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * load.pri n1 inc n1 ; n++, e.g. "for (n=0; n<10; n++)" + * inc n1 ;$exp + * ;$exp - + * Plus the varieties for stack relative increments + * and decrements. + */ + { + #ifdef SCPACK + "inc %1!load.pri %1!;$exp!", + "inc %1!;$exp!", + #else + "inc\202\311\252", + "inc\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.pri %1!inc %1!;$exp!", + "inc %1!;$exp!", + #else + "\311inc\301", + "inc\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "inc.s %1!load.s.pri %1!;$exp!", + "inc.s %1!;$exp!", + #else + "inc\222\202\333\252", + "inc\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.s.pri %1!inc.s %1!;$exp!", + "inc.s %1!;$exp!", + #else + "\333inc\222\301", + "inc\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "dec %1!load.pri %1!;$exp!", + "dec %1!;$exp!", + #else + "dec\202\311\252", + "dec\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.pri %1!dec %1!;$exp!", + "dec %1!;$exp!", + #else + "\311dec\301", + "dec\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "dec.s %1!load.s.pri %1!;$exp!", + "dec.s %1!;$exp!", + #else + "dec\222\202\333\252", + "dec\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.s.pri %1!dec.s %1!;$exp!", + "dec.s %1!;$exp!", + #else + "\333dec\222\301", + "dec\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + /* ??? the same (increments and decrements) for references */ + /* Loading the constant zero has a special opcode. + * When storing zero in memory, the value of PRI must not be later on. + * const.pri 0 zero n1 + * stor.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * const.pri 0 zero.s n1 + * stor.s.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * zero.pri zero n1 + * stor.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * zero.pri zero.s n1 + * stor.s.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * const.pri 0 zero.pri + * -------------------------------------- + * const.alt 0 zero.alt + * The last two alternatives save more memory than they save + * time, but anyway... + */ + { + #ifdef SCPACK + "const.pri 0!stor.pri %1!;$exp!", + "zero %1!;$exp!", + #else + "\233\206 0!\227or\225\252", + "\340\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.pri 0!stor.s.pri %1!;$exp!", + "zero.s %1!;$exp!", + #else + "\233\206 0!\227or\222\225\252", + "\340\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.pri!stor.pri %1!;$exp!", + "zero %1!;$exp!", + #else + "\373\227or\225\252", + "\340\301", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.pri!stor.s.pri %1!;$exp!", + "zero.s %1!;$exp!", + #else + "\373\227or\222\225\252", + "\340\222\301", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.pri 0!", + "zero.pri!", + #else + "\233\206 0!", + "\373", + #endif + seqsize(1,1) - seqsize(1,0) + }, + { + #ifdef SCPACK + "const.alt 0!", + "zero.alt!", + #else + "\233\212 0!", + "\340\223", + #endif + seqsize(1,1) - seqsize(1,0) + }, + + /* ------------------ */ + /* Macro instructions */ + /* ------------------ */ + + { "", "", 0 }, /* separator, so optimizer can stop before generating macro opcodes */ + + /* optimizing the calling of native functions (which always have a parameter + * count pushed before, and the stack pointer restored afterwards + */ + { + #ifdef SCPACK + "push.c %1!sysreq.c %2!stack %3!", //note: %3 == %1 + 4 + "sysreq.n %2 %1!", + #else + "\331\202sysr\262\242\221\227ack\256", + "sysr\262.n\220\202", + #endif + seqsize(3,3) - seqsize(1,2) + }, + /* ----- */ + /* Functions with many parameters with the same "type" have sequences like: + * push.c n1 push3.c n1 n2 n3 + * ;$par ;$par + * push.c n2 - + * ;$par - + * push.c n3 - + * ;$par - + * etc. etc. + * + * Similar sequences occur with PUSH, PUSH.s and PUSHADDR + */ + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!;$par!push.c %3!;$par!push.c %4!;$par!push.c %5!", + "push5.c %1 %2 %3 %4 %5!", + #else + "\331\336\242\345\242\256\257\242\335\257\242\2035!", + "\2165\242\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!;$par!push.c %3!;$par!push.c %4!", + "push4.c %1 %2 %3 %4!", + #else + "\331\336\242\345\242\256\257\242\335", + "\2164\242\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!;$par!push.c %3!", + "push3.c %1 %2 %3!", + #else + "\331\336\242\345\242\256", + "\2163\242\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!", + "push2.c %1 %2!", + #else + "\331\336\242\221", + "\2162\242\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { + #ifdef SCPACK + "push %1!;$par!push %2!;$par!push %3!;$par!push %4!;$par!push %5!", + "push5 %1 %2 %3 %4 %5!", + #else + "\216\336\345\256\257\335\257\2035!", + "\2165\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push %1!;$par!push %2!;$par!push %3!;$par!push %4!", + "push4 %1 %2 %3 %4!", + #else + "\216\336\345\256\257\335", + "\2164\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push %1!;$par!push %2!;$par!push %3!", + "push3 %1 %2 %3!", + #else + "\216\336\345\256", + "\2163\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push %1!;$par!push %2!", + "push2 %1 %2!", + #else + "\216\336\221", + "\2162\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!;$par!push.s %3!;$par!push.s %4!;$par!push.s %5!", + "push5.s %1 %2 %3 %4 %5!", + #else + "\216\222\336\222\345\222\256\257\222\335\257\222\2035!", + "\2165\222\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!;$par!push.s %3!;$par!push.s %4!", + "push4.s %1 %2 %3 %4!", + #else + "\216\222\336\222\345\222\256\257\222\335", + "\2164\222\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!;$par!push.s %3!", + "push3.s %1 %2 %3!", + #else + "\216\222\336\222\345\222\256", + "\2163\222\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!", + "push2.s %1 %2!", + #else + "\216\222\336\222\221", + "\2162\222\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!;$par!push.adr %4!;$par!push.adr %5!", + "push5.adr %1 %2 %3 %4 %5!", + #else + "\216\326\336\326\345\326\256\257\326\335\257\326\2035!", + "\2165\326\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!;$par!push.adr %4!", + "push4.adr %1 %2 %3 %4!", + #else + "\216\326\336\326\345\326\256\257\326\335", + "\2164\326\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!", + "push3.adr %1 %2 %3!", + #else + "\216\326\336\326\345\326\256", + "\2163\326\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!", + "push2.adr %1 %2!", + #else + "\216\326\336\326\221", + "\2162\326\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* Loading two registers at a time + * load.pri n1 load.both n1 n2 + * load.alt n2 - + * -------------------------------------- + * load.alt n2 load.both n1 n2 + * load.pri n1 - + * -------------------------------------- + * load.s.pri n1 load.s.both n1 n2 + * load.s.alt n2 - + * -------------------------------------- + * load.s.alt n2 load.s.both n1 n2 + * load.s.pri n1 - + */ + { + #ifdef SCPACK + "load.pri %1!load.alt %2!", + "load.both %1 %2!", + #else + "\311\362", + "\342\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.alt %2!load.pri %1!", + "load.both %1 %2!", + #else + "\362\311", + "\342\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.s.pri %1!load.s.alt %2!", + "load.s.both %1 %2!", + #else + "\333\226\304", + "\226.\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.s.alt %2!load.s.pri %1!", + "load.s.both %1 %2!", + #else + "\226\304\333", + "\226.\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* Loading two registers and then pushing them occurs with user operators + * load.both n1 n2 push2 n1 n2 + * push.pri - + * push.alt - + * -------------------------------------- + * load.s.both n1 n2 push2.s n1 n2 + * push.pri - + * push.alt - + */ + { + #ifdef SCPACK + "load.both %1 %2!push.pri!push.alt!", + "push2 %1 %2!", + #else + "\342\275th\332\244\354", + "\2162\332", + #endif + seqsize(3,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.s.both %1 %2!push.pri!push.alt!", + "push2.s %1 %2!", + #else + "\226.\275th\332\244\354", + "\2162\222\332", + #endif + seqsize(3,2) - seqsize(1,2) + }, + /* Load a constant in a variable + * const.pri n1 const n2 n1 + * stor.pri n2 - + * -------------------------------------- + * const.pri n1 const.s n2 n1 + * stor.s.pri n2 - + */ + { + #ifdef SCPACK + "const.pri %1!stor.pri %2!", + "const %2 %1!", + #else + "\330\227or\230", + "\233\220\202", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "const.pri %1!stor.s.pri %2!", + "const.s %2 %1!", + #else + "\330\227or\222\230", + "\233\222\220\202", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { NULL, NULL, 0 } +}; diff --git a/compiler-init/scexpand.c b/compiler-init/scexpand.c new file mode 100644 index 00000000..977c0121 --- /dev/null +++ b/compiler-init/scexpand.c @@ -0,0 +1,68 @@ +/* expand.c -- Byte Pair Encoding decompression */ +/* Copyright 1996 Philip Gage */ + +/* Byte Pair Compression appeared in the September 1997 + * issue of C/C++ Users Journal. The original source code + * may still be found at the web site of the magazine + * (www.cuj.com). + * + * The decompressor has been modified by me (Thiadmer + * Riemersma) to accept a string as input, instead of a + * complete file. + */ +#include +#include +#include "sc.h" + +#define STACKSIZE 16 + +SC_FUNC int strexpand(char *dest, unsigned char *source, int maxlen, unsigned char pairtable[128][2]) +{ + unsigned char stack[STACKSIZE]; + short c, top = 0; + int len; + + assert(maxlen > 0); + len = 1; /* already 1 byte for '\0' */ + for (;;) { + + /* Pop byte from stack or read byte from the input string */ + if (top) + c = stack[--top]; + else if ((c = *(unsigned char *)source++) == '\0') + break; + + /* Push pair on stack or output byte to the output string */ + if (c > 127) { + assert(top+2 <= STACKSIZE); + stack[top++] = pairtable[c-128][1]; + stack[top++] = pairtable[c-128][0]; + } + else { + len++; + if (maxlen > 1) { /* reserve one byte for the '\0' */ + *dest++ = (char)c; + maxlen--; + } + } + } + *dest = '\0'; + return len; /* return number of bytes decoded */ +} + +#if 0 /*for testing*/ +#include "sc5.scp" + +int main (int argc, char **argv) +{ + int i; + char str[128]; + + for (i=0; i<58; i++) { + strexpand(str, errmsg[i], sizeof str, SCPACK_TABLE); + printf("%s", str); + } /* for */ + return 0; +} +#endif + diff --git a/compiler-init/sci18n.c b/compiler-init/sci18n.c new file mode 100644 index 00000000..7b58e1f4 --- /dev/null +++ b/compiler-init/sci18n.c @@ -0,0 +1,428 @@ +/* Codepage translation to Unicode, and UTF-8 support + * + * The translation is based on codepage mapping files that are distributed + * by the Unicode consortium, see ftp://ftp.unicode.org/Public/MAPPINGS/. + * + * Character sets with a maximum of 256 codes are translated via a lookup + * table (these are Single-Byte Character Sets). Character sets like Shift-JIS + * with single-byte characters and multi-byte characters (introduced by a + * leader byte) are split into two tables: the 256-entry lookup table for + * the single-byte characters and an extended table for the multi-byte + * characters. The extended table is allocated dynamically; the lookup table + * is allocated statically, so loading SBCS tables cannot fail (if the tables + * themselves are valid, of course). + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: CODEPAGE.C,v 1.0 2004-02-18 12:13:04+01 thiadmer Exp thiadmer $ + */ +#include +#include +#include +#include +#include +#include "sc.h" + +#if !defined TRUE + #define FALSE 0 + #define TRUE 1 +#endif +#if !defined _MAX_PATH + #define _MAX_PATH 250 +#endif +#if !defined DIRSEP_CHAR + #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #define DIRSEP_CHAR '/' + #elif defined macintosh + #define DIRSEP_CHAR ':' + #else + #define DIRSEP_CHAR '\\' + #endif +#endif + +#if !defined ELEMENTS + #define ELEMENTS(array) (sizeof(array) / sizeof(array[0])) +#endif + +#if !defined NO_CODEPAGE + +#if !defined MAXCODEPAGE + #define MAXCODEPAGE 12 /* typically "cp" + 4 digits + ".txt" */ +#endif +#define INVALID 0xffffu /* 0xffff and 0xfffe are invalid Unicode characters */ +#define LEADBYTE 0xfffeu + +struct wordpair { + unsigned short index; + wchar_t code; +}; +static char cprootpath[_MAX_PATH] = { DIRSEP_CHAR, '\0' }; +static wchar_t bytetable[256]; +static struct wordpair *wordtable = NULL; +static unsigned wordtablesize = 0; +static unsigned wordtabletop = 0; + + +/* read in a line delimited by '\r' or '\n'; do NOT store the '\r' or '\n' into + * the string and ignore empty lines + * returns 1 for success and 0 for failure + */ +static int cp_readline(FILE *fp,char *string,size_t size) +{ + size_t count=0; + int c; + assert(size>1); + while ((c=fgetc(fp))!=EOF && count0) /* '\r' or '\n' ends a string */ + break; + /* if count==0, the line started with a '\r' or '\n', or perhaps line + * ends in the file are '\r\n' and we read and stopped on the '\r' of + * the preceding line + */ + } else { + string[count++]=(char)c; + } /* if */ + } /* while */ + string[count]='\0'; + return count>0; +} + +/* cp_path() sets the directory where all codepage files must be found (if + * the parameter to cp_set() specifies a full path, that is used instead). + * The path is specified into two parts: root and directory; the full path + * for the codepage direcory is just the concatenation of the two, with a + * directory separator in between. The directory is given in two parts, + * because often a program already retrieves its "home" directory and the + * codepages are most conveniently stored in a subdirectory of this home + * directory. + */ +SC_FUNC int cp_path(const char *root, const char *directory) +{ + size_t len1,len2; + int add_slash1,add_slash2; + + len1= (root!=NULL) ? strlen(root) : 0; + add_slash1= (len1==0 || root[len1-1]!=DIRSEP_CHAR); + len2= (directory!=NULL) ? strlen(directory) : 0; + add_slash2= (len2>0 && root[len2-1]!=DIRSEP_CHAR); + if (len1+add_slash1+len2+add_slash2>=(_MAX_PATH-MAXCODEPAGE)) + return FALSE; /* full filename may not fit */ + if (root!=NULL) + strcpy(cprootpath,root); + if (add_slash1) { + assert(len1==0 || cprootpath[len1]=='\0'); + cprootpath[len1]=DIRSEP_CHAR; + cprootpath[len1+1]='\0'; + } /* if */ + if (directory!=NULL) + strcat(cprootpath,directory); + if (add_slash2) { + assert(cprootpath[len1+add_slash1+len2]=='\0'); + cprootpath[len1+add_slash1+len2]=DIRSEP_CHAR; + cprootpath[len1+add_slash1+len2+1]='\0'; + } /* if */ + cp_set(NULL); /* start with a "linear" table (no translation) */ + return TRUE; +} + +/* cp_set() loads a codepage from a file. The name parameter may be a + * filename (including a full path) or it may be a partial codepage name. + * If the name parameter is NULL, the codepage is cleared to be a "linear" + * table (no translation). + * The following files are attempted to open (where specifies the + * value of the parameter): + * + * / + * /.txt + * /cp + * /cp.txt + */ +SC_FUNC int cp_set(const char *name) +{ + char filename[_MAX_PATH]; + FILE *fp=NULL; + unsigned index; + + /* for name==NULL, set up an identity table */ + if (name==NULL || *name=='\0') { + if (wordtable!=NULL) { + free(wordtable); + wordtable=NULL; + wordtablesize=0; + wordtabletop=0; + } /* if */ + for (index=0; indexMAXCODEPAGE) + return 0; + assert(strlen(name)+strlen(cprootpath)<_MAX_PATH); + strcpy(filename,cprootpath); + strcat(filename,name); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) { + /* try opening the file in the "root path" for codepages, with a ".txt" extension */ + if (strlen(name)+4>=MAXCODEPAGE) + return 0; + assert(strlen(filename)+4<_MAX_PATH); + strcat(filename,".txt"); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) { + /* try opening the file in the "root path" for codepages, with "cp" prefixed before the name */ + if (strlen(name)+2>MAXCODEPAGE) + return 0; + assert(2+strlen(name)+strlen(cprootpath)<_MAX_PATH); + strcpy(filename,cprootpath); + strcat(filename,"cp"); + strcat(filename,name); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) { + /* try opening the file in the "root path" for codepages, with "cp" prefixed an ".txt" appended */ + if (strlen(name)+2+4>MAXCODEPAGE) + return 0; + assert(strlen(filename)+4<_MAX_PATH); + strcat(filename,".txt"); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) + return FALSE; /* all failed */ + + /* clear the tables */ + for (index=0; index0 && wordtable!=NULL); + if (wordtable!=NULL) { + free(wordtable); + wordtable=NULL; + wordtablesize=0; + wordtabletop=0; + } /* if */ + + /* read in the table */ + while (cp_readline(fp,filename,sizeof filename)) { + char *ptr; + if ((ptr=strchr(filename,'#'))!=NULL) + *ptr='\0'; /* strip of comment */ + for (ptr=filename; *ptr>0 && *ptr<' '; ptr++) + /* nothing */; /* skip leading whitespace */ + if (*ptr!='\0') { + /* content on line */ + unsigned code=LEADBYTE; + int num=sscanf(ptr,"%i %i",&index,&code); + /* if sscanf() returns 1 and the index is in range 0..255, then the + * code is a DBCS lead byte; if sscanf() returns 2 and index>=256, this + * is a double byte pair (lead byte + follower) + */ + if (num>=1 && index<256) { + bytetable[index]=(wchar_t)code; + } else if (num==2 && index>=256 && index=wordtablesize) { + /* grow the list */ + int newsize; + struct wordpair *newblock; + newsize= (wordtablesize==0) ? 128 : 2*wordtablesize; + newblock=(struct wordpair *)malloc(newsize*sizeof(*wordtable)); + if (newblock!=NULL) { + memcpy(newblock,wordtable,wordtabletop*sizeof(*wordtable)); + free(wordtable); + wordtable=newblock; + wordtablesize=newsize; + } /* if */ + } /* if */ + if (wordtabletop0 && (unsigned)wordtable[pos-1].index>index) { + wordtable[pos]=wordtable[pos-1]; + pos--; + } /* while */ + wordtable[pos].index=(unsigned short)index; + wordtable[pos].code=(wchar_t)code; + } /* if */ + } /* if */ + } /* if */ + } /* while */ + + fclose(fp); + return TRUE; +} + +SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr) +{ + wchar_t result; + + result=bytetable[*string++]; + /* check whether this is a leader code */ + if ((unsigned)result==LEADBYTE && wordtable!=NULL) { + /* look up the code via binary search */ + int low,high,mid; + unsigned short index=(unsigned short)(((*(string-1)) << 8) | *string); + string++; + assert(wordtabletop>0); + low=0; + high=wordtabletop-1; + while (lowwordtable[mid].index) + low=mid+1; + else + high=mid; + } /* while */ + assert(low==high); + if (wordtable[low].index==index) + result=wordtable[low].code; + } /* if */ + + if (endptr!=NULL) + *endptr=string; + return (cell)result; +} + +#endif /* NO_CODEPAGE */ + +#if !defined NO_UTF8 +SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr) +{ + int follow=0; + long lowmark=0; + unsigned char ch; + cell result=0; + + if (endptr!=NULL) + *endptr=string; + + for ( ;; ) { + ch=*string++; + + if (follow>0 && (ch & 0xc0)==0x80) { + /* leader code is active, combine with earlier code */ + result=(result << 6) | (ch & 0x3f); + if (--follow==0) { + /* encoding a character in more bytes than is strictly needed, + * is not really valid UTF-8; we are strict here to increase + * the chance of heuristic dectection of non-UTF-8 text + * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) + */ + if (result=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) + return -1; + } /* if */ + break; + } else if (follow==0 && (ch & 0x80)==0x80) { + /* UTF-8 leader code */ + if ((ch & 0xe0)==0xc0) { + /* 110xxxxx 10xxxxxx */ + follow=1; + lowmark=0x80L; + result=ch & 0x1f; + } else if ((ch & 0xf0)==0xe0) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + follow=2; + lowmark=0x800L; + result=ch & 0x0f; + } else if ((ch & 0xf8)==0xf0) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=3; + lowmark=0x10000L; + result=ch & 0x07; + } else if ((ch & 0xfc)==0xf8) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=4; + lowmark=0x200000L; + result=ch & 0x03; + } else if ((ch & 0xfe)==0xfc) { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (32 bits) */ + follow=5; + lowmark=0x4000000L; + result=ch & 0x01; + } else { + /* this is invalid UTF-8 */ + return -1; + } /* if */ + } else if (follow==0 && (ch & 0x80)==0x00) { + /* 0xxxxxxx (US-ASCII) */ + result=ch; + break; + } else { + /* this is invalid UTF-8 */ + return -1; + } /* if */ + + } /* for */ + + if (endptr!=NULL) + *endptr=string; + return result; +} +#endif + +SC_FUNC int scan_utf8(FILE *fp,const char *filename) +{ + #if defined NO_UTF8 + return 0; + #else + void *resetpos=pc_getpossrc(fp); + int utf8=TRUE; + int firstchar=TRUE,bom_found=FALSE; + const unsigned char *ptr; + + while (utf8 && pc_readsrc(fp,pline,sLINEMAX)!=NULL) { + ptr=pline; + if (firstchar) { + /* check whether the very first character on the very first line + * starts with a BYTE order mark + */ + cell c=get_utf8_char(ptr,&ptr); + bom_found= (c==0xfeff); + utf8= (c>=0); + firstchar=FALSE; + } /* if */ + while (utf8 && *ptr!='\0') + utf8= (get_utf8_char(ptr,&ptr)>=0); + } /* while */ + pc_resetsrc(fp,resetpos); + if (bom_found) { + unsigned char bom[3]; + if (!utf8) + error(77,filename); /* malformed UTF-8 encoding */ + pc_readsrc(fp,bom,3); + assert(bom[0]==0xef && bom[1]==0xbb && bom[2]==0xbf); + } /* if */ + return utf8; + #endif /* NO_UTF8 */ +} diff --git a/compiler-init/sclist.c b/compiler-init/sclist.c new file mode 100644 index 00000000..18c961b0 --- /dev/null +++ b/compiler-init/sclist.c @@ -0,0 +1,486 @@ +/* Pawn compiler - maintenance of various lists + * + * o Name list (aliases) + * o Include path list + * o Macro defintions (text substitutions) + * + * Copyright (c) ITB CompuPhase, 2001-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sclist.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include +#include +#include "sc.h" + +#if defined FORTIFY + #include +#endif + +/* a "private" implementation of strdup(), so that porting + * to other memory allocators becomes easier. + * By Søren Hannibal. + */ +SC_FUNC char* duplicatestring(const char* sourcestring) +{ + char* result=(char*)malloc(strlen(sourcestring)+1); + strcpy(result,sourcestring); + return result; +} + + +static stringpair *insert_stringpair(stringpair *root,char *first,char *second,int matchlength) +{ + stringpair *cur,*pred; + + assert(root!=NULL); + assert(first!=NULL); + assert(second!=NULL); + /* create a new node, and check whether all is okay */ + if ((cur=(stringpair*)malloc(sizeof(stringpair)))==NULL) + return NULL; + cur->first=duplicatestring(first); + cur->second=duplicatestring(second); + cur->matchlength=matchlength; + if (cur->first==NULL || cur->second==NULL) { + if (cur->first!=NULL) + free(cur->first); + if (cur->second!=NULL) + free(cur->second); + free(cur); + return NULL; + } /* if */ + /* link the node to the tree, find the position */ + for (pred=root; pred->next!=NULL && strcmp(pred->next->first,first)<0; pred=pred->next) + /* nothing */; + cur->next=pred->next; + pred->next=cur; + return cur; +} + +static void delete_stringpairtable(stringpair *root) +{ + stringpair *cur, *next; + + assert(root!=NULL); + cur=root->next; + while (cur!=NULL) { + next=cur->next; + assert(cur->first!=NULL); + assert(cur->second!=NULL); + free(cur->first); + free(cur->second); + free(cur); + cur=next; + } /* while */ + memset(root,0,sizeof(stringpair)); +} + +static stringpair *find_stringpair(stringpair *cur,char *first,int matchlength) +{ + int result=0; + + assert(matchlength>0); /* the function cannot handle zero-length comparison */ + assert(first!=NULL); + while (cur!=NULL && result<=0) { + result=(int)*cur->first - (int)*first; + if (result==0 && matchlength==cur->matchlength) { + result=strncmp(cur->first,first,matchlength); + if (result==0) + return cur; + } /* if */ + cur=cur->next; + } /* while */ + return NULL; +} + +static int delete_stringpair(stringpair *root,stringpair *item) +{ + stringpair *cur; + + assert(root!=NULL); + cur=root; + while (cur->next!=NULL) { + if (cur->next==item) { + cur->next=item->next; /* unlink from list */ + assert(item->first!=NULL); + assert(item->second!=NULL); + free(item->first); + free(item->second); + free(item); + return TRUE; + } /* if */ + cur=cur->next; + } /* while */ + return FALSE; +} + +/* ----- string list functions ----------------------------------- */ +static stringlist *insert_string(stringlist *root,char *string) +{ + stringlist *cur; + + assert(string!=NULL); + if ((cur=(stringlist*)malloc(sizeof(stringlist)))==NULL) + error(103); /* insufficient memory (fatal error) */ + if ((cur->line=duplicatestring(string))==NULL) + error(103); /* insufficient memory (fatal error) */ + /* insert as "last" */ + assert(root!=NULL); + while (root->next!=NULL) + root=root->next; + cur->next=root->next; + root->next=cur; + return cur; +} + +static char *get_string(stringlist *root,int index) +{ + stringlist *cur; + + assert(root!=NULL); + cur=root->next; + while (cur!=NULL && index-->0) + cur=cur->next; + if (cur!=NULL) { + assert(cur->line!=NULL); + return cur->line; + } /* if */ + return NULL; +} + +static int delete_string(stringlist *root,int index) +{ + stringlist *cur,*item; + + assert(root!=NULL); + for (cur=root; cur->next!=NULL && index>0; cur=cur->next,index--) + /* nothing */; + if (cur->next!=NULL) { + item=cur->next; + cur->next=item->next; /* unlink from list */ + assert(item->line!=NULL); + free(item->line); + free(item); + return TRUE; + } /* if */ + return FALSE; +} + +SC_FUNC void delete_stringtable(stringlist *root) +{ + stringlist *cur,*next; + + assert(root!=NULL); + cur=root->next; + while (cur!=NULL) { + next=cur->next; + assert(cur->line!=NULL); + free(cur->line); + free(cur); + cur=next; + } /* while */ + memset(root,0,sizeof(stringlist)); +} + + +/* ----- alias table --------------------------------------------- */ +static stringpair alias_tab = {NULL, NULL, NULL}; /* alias table */ + +SC_FUNC stringpair *insert_alias(char *name,char *alias) +{ + stringpair *cur; + + assert(name!=NULL); + assert(strlen(name)<=sNAMEMAX); + assert(alias!=NULL); + assert(strlen(alias)<=sNAMEMAX); + if ((cur=insert_stringpair(&alias_tab,name,alias,strlen(name)))==NULL) + error(103); /* insufficient memory (fatal error) */ + return cur; +} + +SC_FUNC int lookup_alias(char *target,char *name) +{ + stringpair *cur=find_stringpair(alias_tab.next,name,strlen(name)); + if (cur!=NULL) { + assert(strlen(cur->second)<=sNAMEMAX); + strcpy(target,cur->second); + } /* if */ + return cur!=NULL; +} + +SC_FUNC void delete_aliastable(void) +{ + delete_stringpairtable(&alias_tab); +} + +/* ----- include paths list -------------------------------------- */ +static stringlist includepaths = {NULL, NULL}; /* directory list for include files */ + +SC_FUNC stringlist *insert_path(char *path) +{ + return insert_string(&includepaths,path); +} + +SC_FUNC char *get_path(int index) +{ + return get_string(&includepaths,index); +} + +SC_FUNC void delete_pathtable(void) +{ + delete_stringtable(&includepaths); + assert(includepaths.next==NULL); +} + + +/* ----- text substitution patterns ------------------------------ */ +#if !defined NO_DEFINE + +static stringpair substpair = { NULL, NULL, NULL}; /* list of substitution pairs */ + +static stringpair *substindex['z'-PUBLIC_CHAR+1]; /* quick index to first character */ +static void adjustindex(char c) +{ + stringpair *cur; + assert(c>='A' && c<='Z' || c>='a' && c<='z' || c=='_' || c==PUBLIC_CHAR); + assert(PUBLIC_CHAR<'A' && 'A'<'_' && '_'<'z'); + + for (cur=substpair.next; cur!=NULL && cur->first[0]!=c; cur=cur->next) + /* nothing */; + substindex[(int)c-PUBLIC_CHAR]=cur; +} + +SC_FUNC stringpair *insert_subst(char *pattern,char *substitution,int prefixlen) +{ + stringpair *cur; + + assert(pattern!=NULL); + assert(substitution!=NULL); + if ((cur=insert_stringpair(&substpair,pattern,substitution,prefixlen))==NULL) + error(103); /* insufficient memory (fatal error) */ + adjustindex(*pattern); + return cur; +} + +SC_FUNC stringpair *find_subst(char *name,int length) +{ + stringpair *item; + assert(name!=NULL); + assert(length>0); + assert(*name>='A' && *name<='Z' || *name>='a' && *name<='z' || *name=='_' || *name==PUBLIC_CHAR); + item=substindex[(int)*name-PUBLIC_CHAR]; + if (item!=NULL) + item=find_stringpair(item,name,length); + return item; +} + +SC_FUNC int delete_subst(char *name,int length) +{ + stringpair *item; + assert(name!=NULL); + assert(length>0); + assert(*name>='A' && *name<='Z' || *name>='a' && *name<='z' || *name=='_' || *name==PUBLIC_CHAR); + item=substindex[(int)*name-PUBLIC_CHAR]; + if (item!=NULL) + item=find_stringpair(item,name,length); + if (item==NULL) + return FALSE; + delete_stringpair(&substpair,item); + adjustindex(*name); + return TRUE; +} + +SC_FUNC void delete_substtable(void) +{ + int i; + delete_stringpairtable(&substpair); + for (i=0; i=199901L + #define __STDC_FORMAT_MACROS + #define __STDC_CONSTANT_MACROS + #include /* automatically includes stdint.h */ +#elif (defined _MSC_VER || defined __BORLANDC__) && (defined _I64_MAX || defined HAVE_I64) + #define PRId64 "I64d" + #define PRIx64 "I64x" +#else + #define PRId64 "lld" + #define PRIx64 "llx" +#endif +#if PAWN_CELL_SIZE==64 + #define PRIdC PRId64 + #define PRIxC PRIx64 +#elif PAWN_CELL_SIZE==32 + #define PRIdC "ld" + #define PRIxC "lx" +#else + #define PRIdC "d" + #define PRIxC "x" +#endif + +static stringlist dbgstrings = {NULL, NULL}; + +SC_FUNC stringlist *insert_dbgfile(const char *filename) +{ + + if (sc_status==statWRITE && (sc_debug & sSYMBOLIC)!=0) { + char string[_MAX_PATH+40]; + assert(filename!=NULL); + assert(strlen(filename)+400) + linenr--; /* line numbers are zero-based in the debug information */ + sprintf(string,"L:%" PRIxC " %x",code_idx,linenr); + return insert_string(&dbgstrings,string); + } /* if */ + return NULL; +} + +SC_FUNC stringlist *insert_dbgsymbol(symbol *sym) +{ + if (sc_status==statWRITE && (sc_debug & sSYMBOLIC)!=0) { + char string[2*sNAMEMAX+128]; + char symname[2*sNAMEMAX+16]; + + funcdisplayname(symname,sym->name); + /* address tag:name codestart codeend ident vclass [tag:dim ...] */ + if (sym->ident==iFUNCTN) { + sprintf(string,"S:%" PRIxC " %x:%s %" PRIxC " %" PRIxC " %x %x", + sym->addr,sym->tag,symname,sym->addr,sym->codeaddr,sym->ident,sym->vclass); + } else { + sprintf(string,"S:%" PRIxC " %x:%s %" PRIxC " %" PRIxC " %x %x", + sym->addr,sym->tag,symname,sym->codeaddr,code_idx,sym->ident,sym->vclass); + } /* if */ + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + #if !defined NDEBUG + int count=sym->dim.array.level; + #endif + symbol *sub; + strcat(string," [ "); + for (sub=sym; sub!=NULL; sub=finddepend(sub)) { + assert(sub->dim.array.level==count--); + sprintf(string+strlen(string),"%x:%x ",sub->x.tags.index,sub->dim.array.length); + } /* for */ + strcat(string,"]"); + } /* if */ + + return insert_string(&dbgstrings,string); + } /* if */ + return NULL; +} + +SC_FUNC char *get_dbgstring(int index) +{ + return get_string(&dbgstrings,index); +} + +SC_FUNC void delete_dbgstringtable(void) +{ + delete_stringtable(&dbgstrings); + assert(dbgstrings.next==NULL); +} diff --git a/compiler-init/scmemfil.c b/compiler-init/scmemfil.c new file mode 100644 index 00000000..73a76c63 --- /dev/null +++ b/compiler-init/scmemfil.c @@ -0,0 +1,179 @@ +/* Pawn compiler + * + * Routines to maintain a "text file" in memory. + * + * Copyright (c) ITB CompuPhase, 2003-2006 + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the + * use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: scmemfil.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ + +#include +#include +#include +#include +#include "memfile.h" + +#if defined FORTIFY + #include +#endif + + +#define BUFFERSIZE 512u + +/* For every block, except the first: + * buffer points to a block that is BUFFERSIZE long that holds the data + * bufpos is the "used size" of the block + * For the first block: + * buffer points to the "file name" + * bufpos is the current "file pointer" + */ +typedef memfile_t MEMFILE; +#define tMEMFILE 1 + +#include "sc.h" + + +MEMFILE *mfcreate(char *filename) +{ + return memfile_creat(filename, 4096); +} + +void mfclose(MEMFILE *mf) +{ + memfile_destroy(mf); +} + +int mfdump(MEMFILE *mf) +{ + FILE *fp; + int okay; + + assert(mf!=NULL); + /* create the file */ + fp=fopen(mf->name, "wb"); + if (fp==NULL) + return 0; + + okay=1; + okay = okay & (fwrite(mf->base, mf->usedoffs, 1, fp)==(size_t)mf->usedoffs); + + fclose(fp); + return okay; +} + +long mflength(MEMFILE *mf) +{ + return mf->usedoffs; +} + +long mfseek(MEMFILE *mf,long offset,int whence) +{ + long length; + + assert(mf!=NULL); + if (mf->usedoffs == 0) + return 0L; /* early exit: not a single byte in the file */ + + /* find the size of the memory file */ + length=mflength(mf); + + /* convert the offset to an absolute position */ + switch (whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset+=mf->offs; + break; + case SEEK_END: + assert(offset<=0); + offset+=length; + break; + } /* switch */ + + /* clamp to the file length limit */ + if (offset<0) + offset=0; + else if (offset>length) + offset=length; + + /* set new position and return it */ + memfile_seek(mf, offset); + + return offset; +} + +unsigned int mfwrite(MEMFILE *mf,unsigned char *buffer,unsigned int size) +{ + return (memfile_write(mf, buffer, size) ? size : 0); +} + +unsigned int mfread(MEMFILE *mf,unsigned char *buffer,unsigned int size) +{ + return memfile_read(mf, buffer, size); +} + +char *mfgets(MEMFILE *mf,char *string,unsigned int size) +{ + char *ptr; + unsigned int read; + long seek; + + assert(mf!=NULL); + + read=mfread(mf,(unsigned char *)string,size); + if (read==0) + return NULL; + seek=0L; + + /* make sure that the string is zero-terminated */ + assert(read<=size); + if (read +#include +#include +#include +#include +#include "sc.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + +#if defined FORTIFY + #include +#endif + +typedef struct s_statelist { + struct s_statelist *next; + int *states; /* list of states in this combination */ + int numstates; /* number of items in the above list */ + int fsa; /* automaton id */ + int listid; /* unique id for this combination list */ +} statelist; + +static statelist statelist_tab = { NULL, NULL, 0, 0, 0}; /* state combinations table */ + + +static constvalue *find_automaton(const char *name,int *last) +{ + constvalue *ptr; + + assert(last!=NULL); + *last=0; + ptr=sc_automaton_tab.next; + while (ptr!=NULL) { + if (strcmp(name,ptr->name)==0) + return ptr; + if (ptr->index>*last) + *last=ptr->index; + ptr=ptr->next; + } /* while */ + return NULL; +} + +SC_FUNC constvalue *automaton_add(const char *name) +{ + constvalue *ptr; + int last; + + assert(strlen(name)name)); + ptr=find_automaton(name,&last); + if (ptr==NULL) { + assert(last+1 <= SHRT_MAX); + ptr=append_constval(&sc_automaton_tab,name,(cell)0,(short)(last+1)); + } /* if */ + return ptr; +} + +SC_FUNC constvalue *automaton_find(const char *name) +{ + int last; + return find_automaton(name,&last); +} + +SC_FUNC constvalue *automaton_findid(int id) +{ + constvalue *ptr; + for (ptr=sc_automaton_tab.next; ptr!=NULL && ptr->index!=id; ptr=ptr->next) + /* nothing */; + return ptr; +} + + +static constvalue *find_state(const char *name,int fsa,int *last) +{ + constvalue *ptr; + + assert(last!=NULL); + *last=0; + ptr=sc_state_tab.next; + while (ptr!=NULL) { + if (ptr->index==fsa) { + if (strcmp(name,ptr->name)==0) + return ptr; + if ((int)ptr->value>*last) + *last=(int)ptr->value; + } /* if */ + ptr=ptr->next; + } /* while */ + return NULL; +} + +SC_FUNC constvalue *state_add(const char *name,int fsa) +{ + constvalue *ptr; + int last; + + assert(strlen(name)name)); + ptr=find_state(name,fsa,&last); + if (ptr==NULL) { + assert(fsa <= SHRT_MAX); + ptr=append_constval(&sc_state_tab,name,(cell)(last+1),(short)fsa); + } /* if */ + return ptr; +} + +SC_FUNC constvalue *state_find(const char *name,int fsa_id) +{ + int last; /* dummy */ + return find_state(name,fsa_id,&last); +} + +SC_FUNC constvalue *state_findid(int id) +{ + constvalue *ptr; + for (ptr=sc_state_tab.next; ptr!=NULL && ptr->value!=id; ptr=ptr->next) + /* nothing */; + return ptr; +} + +SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid) +{ + int idx; + + assert(list!=NULL); + assert(listsize!=NULL); + assert(*listsize>=0); + assert(count!=NULL); + assert(*count>=0); + assert(*count<=*listsize); + + if (*count==*listsize) { + /* To avoid constantly calling malloc(), the list is grown by 4 states at + * a time. + */ + *listsize+=4; + *list=(int*)realloc(*list,*listsize*sizeof(int)); + if (*list==NULL) + error(103); /* insufficient memory */ + } /* if */ + + /* find the insertion point (the list has to stay sorted) */ + for (idx=0; idx<*count && *list[idx]0); + assert(last!=NULL); + *last=0; + ptr=statelist_tab.next; + while (ptr!=NULL) { + if (ptr->listid>*last) + *last=ptr->listid; + if (ptr->fsa==fsa && ptr->numstates==count) { + /* compare all states */ + for (i=0; istates[i]==list[i]; i++) + /* nothing */; + if (i==count) + return ptr; + } /* if */ + ptr=ptr->next; + } /* while */ + return NULL; +} + +static statelist *state_getlist_ptr(int listid) +{ + statelist *ptr; + + assert(listid>0); + for (ptr=statelist_tab.next; ptr!=NULL && ptr->listid!=listid; ptr=ptr->next) + /* nothing */; + return ptr; +} + +SC_FUNC int state_addlist(int *list,int count,int fsa) +{ + statelist *ptr; + int last; + + assert(list!=NULL); + assert(count>0); + ptr=state_findlist(list,count,fsa,&last); + if (ptr==NULL) { + if ((ptr=(statelist*)malloc(sizeof(statelist)))==NULL) + error(103); /* insufficient memory */ + if ((ptr->states=(int*)malloc(count*sizeof(int)))==NULL) { + free(ptr); + error(103); /* insufficient memory */ + } /* if */ + memcpy(ptr->states,list,count*sizeof(int)); + ptr->numstates=count; + ptr->fsa=fsa; + ptr->listid=last+1; + ptr->next=statelist_tab.next; + statelist_tab.next=ptr; + } /* if */ + assert(ptr!=NULL); + return ptr->listid; +} + +SC_FUNC void state_deletetable(void) +{ + statelist *ptr; + + while (statelist_tab.next!=NULL) { + ptr=statelist_tab.next; + /* unlink first */ + statelist_tab.next=ptr->next; + /* then delete */ + assert(ptr->states!=NULL); + free(ptr->states); + free(ptr); + } /* while */ +} + +SC_FUNC int state_getfsa(int listid) +{ + statelist *ptr; + + assert(listid>=0); + if (listid==0) + return -1; + + ptr=state_getlist_ptr(listid); + return (ptr!=NULL) ? ptr->fsa : -1; /* fsa 0 exists */ +} + +SC_FUNC int state_count(int listid) +{ + statelist *ptr=state_getlist_ptr(listid); + if (ptr==NULL) + return 0; /* unknown list, no states in it */ + return ptr->numstates; +} + +SC_FUNC int state_inlist(int listid,int state) +{ + statelist *ptr; + int i; + + ptr=state_getlist_ptr(listid); + if (ptr==NULL) + return FALSE; /* unknown list, state not in it */ + for (i=0; inumstates; i++) + if (ptr->states[i]==state) + return TRUE; + return FALSE; +} + +SC_FUNC int state_listitem(int listid,int index) +{ + statelist *ptr; + + ptr=state_getlist_ptr(listid); + assert(ptr!=NULL); + assert(index>=0 && indexnumstates); + return ptr->states[index]; +} + +static int checkconflict(statelist *psrc,statelist *ptgt) +{ + int s,t; + + assert(psrc!=NULL); + assert(ptgt!=NULL); + for (s=0; snumstates; s++) + for (t=0; tnumstates; t++) + if (psrc->states[s]==ptgt->states[t]) + return 1; /* state conflict */ + return 0; +} + +/* This function searches whether one of the states in the list of statelist id's + * of a symbol exists in any other statelist id's of the same function; it also + * verifies that all definitions of the symbol are in the same automaton. + */ +SC_FUNC void state_conflict(symbol *root) +{ + statelist *psrc,*ptgt; + constvalue *srcptr,*tgtptr; + symbol *sym; + + assert(root!=NULL); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL || sym->ident!=iFUNCTN) + continue; /* hierarchical data type or no function */ + if (sym->states==NULL) + continue; /* this function has no states */ + for (srcptr=sym->states->next; srcptr!=NULL; srcptr=srcptr->next) { + if (srcptr->index==-1) + continue; /* state list id -1 is a special case */ + psrc=state_getlist_ptr(srcptr->index); + assert(psrc!=NULL); + for (tgtptr=srcptr->next; tgtptr!=NULL; tgtptr=tgtptr->next) { + if (tgtptr->index==-1) + continue; /* state list id -1 is a special case */ + ptgt=state_getlist_ptr(tgtptr->index); + assert(ptgt!=NULL); + if (psrc->fsa!=ptgt->fsa && strcmp(sym->name,uENTRYFUNC)!=0) + error(83,sym->name); /* this function is part of another machine */ + if (checkconflict(psrc,ptgt)) + error(84,sym->name); /* state conflict */ + } /* for (tgtptr) */ + } /* for (srcptr) */ + } /* for (sym) */ +} + +/* check whether the two state lists (whose ids are passed in) share any + * states + */ +SC_FUNC int state_conflict_id(int listid1,int listid2) +{ + statelist *psrc,*ptgt; + + psrc=state_getlist_ptr(listid1); + assert(psrc!=NULL); + ptgt=state_getlist_ptr(listid2); + assert(ptgt!=NULL); + return checkconflict(psrc,ptgt); +} diff --git a/compiler-init/scvars.c b/compiler-init/scvars.c new file mode 100644 index 00000000..6a1365f8 --- /dev/null +++ b/compiler-init/scvars.c @@ -0,0 +1,113 @@ +/* Pawn compiler + * + * Global (cross-module) variables. + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: scvars.c 3577 2006-06-02 16:22:52Z thiadmer $ + */ +#include +#include /* for _MAX_PATH */ +#include "sc.h" + +/* global variables + * + * All global variables that are shared amongst the compiler files are + * declared here. + */ +SC_VDEFINE symbol loctab; /* local symbol table */ +SC_VDEFINE symbol glbtab; /* global symbol table */ +SC_VDEFINE cell *litq; /* the literal queue */ +SC_VDEFINE unsigned char pline[sLINEMAX+1]; /* the line read from the input file */ +SC_VDEFINE const unsigned char *lptr; /* points to the current position in "pline" */ +SC_VDEFINE constvalue tagname_tab = { NULL, "", 0, 0}; /* tagname table */ +SC_VDEFINE constvalue libname_tab = { NULL, "", 0, 0}; /* library table (#pragma library "..." syntax) */ +SC_VDEFINE constvalue *curlibrary = NULL; /* current library */ +SC_VDEFINE int pc_addlibtable = TRUE; /* is the library table added to the AMX file? */ +SC_VDEFINE symbol *curfunc; /* pointer to current function */ +SC_VDEFINE char *inpfname; /* pointer to name of the file currently read from */ +SC_VDEFINE char outfname[_MAX_PATH]; /* intermediate (assembler) file name */ +SC_VDEFINE char binfname[_MAX_PATH]; /* binary file name */ +SC_VDEFINE char errfname[_MAX_PATH]; /* error file name */ +SC_VDEFINE char sc_ctrlchar = CTRL_CHAR; /* the control character (or escape character)*/ +SC_VDEFINE char sc_ctrlchar_org = CTRL_CHAR;/* the default control character */ +SC_VDEFINE int litidx = 0; /* index to literal table */ +SC_VDEFINE int litmax = sDEF_LITMAX; /* current size of the literal table */ +SC_VDEFINE int stgidx = 0; /* index to the staging buffer */ +SC_VDEFINE int sc_labnum = 0; /* number of (internal) labels */ +SC_VDEFINE int staging = 0; /* true if staging output */ +SC_VDEFINE cell declared = 0; /* number of local cells declared */ +SC_VDEFINE cell glb_declared=0; /* number of global cells declared */ +SC_VDEFINE cell code_idx = 0; /* number of bytes with generated code */ +SC_VDEFINE int ntv_funcid= 0; /* incremental number of native function */ +SC_VDEFINE int errnum = 0; /* number of errors */ +SC_VDEFINE int warnnum = 0; /* number of warnings */ +SC_VDEFINE int sc_debug = sCHKBOUNDS; /* by default: bounds checking+assertions */ +SC_VDEFINE int sc_packstr= FALSE; /* strings are packed by default? */ +SC_VDEFINE int sc_asmfile= FALSE; /* create .ASM file? */ +SC_VDEFINE int sc_listing= FALSE; /* create .LST file? */ +SC_VDEFINE int sc_compress=TRUE; /* compress bytecode? */ +SC_VDEFINE int sc_needsemicolon=TRUE;/* semicolon required to terminate expressions? */ +SC_VDEFINE int sc_dataalign=sizeof(cell);/* data alignment value */ +SC_VDEFINE int sc_alignnext=FALSE; /* must frame of the next function be aligned? */ +SC_VDEFINE int pc_docexpr=FALSE; /* must expression be attached to documentation comment? */ +SC_VDEFINE int curseg = 0; /* 1 if currently parsing CODE, 2 if parsing DATA */ +SC_VDEFINE cell pc_stksize=sDEF_AMXSTACK;/* default stack size */ +SC_VDEFINE cell pc_amxlimit=0; /* default abstract machine size limit = none */ +SC_VDEFINE cell pc_amxram=0; /* default abstract machine data size limit = none */ +SC_VDEFINE int freading = FALSE; /* Is there an input file ready for reading? */ +SC_VDEFINE int fline = 0; /* the line number in the current file */ +SC_VDEFINE short fnumber = 0; /* the file number in the file table (debugging) */ +SC_VDEFINE short fcurrent= 0; /* current file being processed (debugging) */ +SC_VDEFINE short sc_intest=FALSE; /* true if inside a test */ +SC_VDEFINE int sideeffect= 0; /* true if an expression causes a side-effect */ +SC_VDEFINE int stmtindent= 0; /* current indent of the statement */ +SC_VDEFINE int indent_nowarn=FALSE;/* skip warning "217 loose indentation" */ +SC_VDEFINE int sc_tabsize=8; /* number of spaces that a TAB represents */ +SC_VDEFINE short sc_allowtags=TRUE; /* allow/detect tagnames in lex() */ +SC_VDEFINE int sc_status; /* read/write status */ +SC_VDEFINE int sc_rationaltag=0; /* tag for rational numbers */ +SC_VDEFINE int rational_digits=0; /* number of fractional digits */ +SC_VDEFINE int sc_allowproccall=0; /* allow/detect tagnames in lex() */ +SC_VDEFINE short sc_is_utf8=FALSE; /* is this source file in UTF-8 encoding */ +SC_VDEFINE char *pc_depricate=NULL;/* if non-null, mark next declaration as depricated */ +SC_VDEFINE int sc_curstates=0; /* ID of the current state list */ +SC_VDEFINE int pc_optimize=sOPTIMIZE_NOMACRO; /* (peephole) optimization level */ +SC_VDEFINE int pc_memflags=0; /* special flags for the stack/heap usage */ + +SC_VDEFINE constvalue sc_automaton_tab = { NULL, "", 0, 0}; /* automaton table */ +SC_VDEFINE constvalue sc_state_tab = { NULL, "", 0, 0}; /* state table */ + +SC_VDEFINE FILE *inpf = NULL; /* file read from (source or include) */ +SC_VDEFINE FILE *inpf_org= NULL; /* main source file */ +SC_VDEFINE FILE *outf = NULL; /* (intermediate) text file written to */ + +SC_VDEFINE jmp_buf errbuf; + +#if !defined SC_LIGHT + SC_VDEFINE int sc_makereport=FALSE; /* generate a cross-reference report */ +#endif + +#if defined __WATCOMC__ && !defined NDEBUG + /* Watcom's CVPACK dislikes .OBJ files without functions */ + static int dummyfunc(void) + { + return 0; + } +#endif diff --git a/compiler-init/spcomp.sln b/compiler-init/spcomp.sln new file mode 100644 index 00000000..3c17272e --- /dev/null +++ b/compiler-init/spcomp.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spcomp", "spcomp.vcproj", "{B4C844FF-008D-4BD5-B82F-DC06E706C64B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Debug|Win32.ActiveCfg = Debug|Win32 + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Debug|Win32.Build.0 = Debug|Win32 + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Release|Win32.ActiveCfg = Release|Win32 + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/compiler-init/spcomp.vcproj b/compiler-init/spcomp.vcproj new file mode 100644 index 00000000..90da31f1 --- /dev/null +++ b/compiler-init/spcomp.vcproj @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler-init/svnrev.h b/compiler-init/svnrev.h new file mode 100644 index 00000000..b0d8a703 --- /dev/null +++ b/compiler-init/svnrev.h @@ -0,0 +1,9 @@ +#define SMC_VERSION 1 +#define SMC_REVISION 0 +#define SMC_BUILD 1 +#define SMC_VERSTRING "1.0.1.3599" + +#define SVN_REV 3599 +#define SVN_REVSTR "3599" +#define SVN_REVDATE "2006-07-05" +#define SVN_REVSTAMP 20060705L From 8616a284b75e74aa4a45da78ac1f2e4eae6f2d38 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 00:10:39 +0000 Subject: [PATCH 02/20] merged in some of the initial sourcemod changes --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%403 --- compiler-init/sc.h | 6 +++--- compiler-init/sc1.c | 29 ++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/compiler-init/sc.h b/compiler-init/sc.h index 8e435a12..d455c4e1 100644 --- a/compiler-init/sc.h +++ b/compiler-init/sc.h @@ -50,13 +50,13 @@ #define CTRL_CHAR '\\' /* default control character */ #define sCHARBITS 8 /* size of a packed character */ -#define sDIMEN_MAX 3 /* maximum number of array dimensions */ -#define sLINEMAX 511 /* input line length (in characters) */ +#define sDIMEN_MAX 4 /* maximum number of array dimensions */ +#define sLINEMAX 1024 /* input line length (in characters) */ #define sCOMP_STACK 32 /* maximum nesting of #if .. #endif sections */ #define sDEF_LITMAX 500 /* initial size of the literal pool, in "cells" */ #define sDEF_AMXSTACK 4096 /* default stack size for AMX files */ #define PREPROC_TERM '\x7f'/* termination character for preprocessor expressions (the "DEL" code) */ -#define sDEF_PREFIX "default.inc" /* default prefix filename */ +#define sDEF_PREFIX "sourcemod.inc" /* default prefix filename */ typedef union { void *pv; /* e.g. a name */ diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 03c7afba..2c5c1862 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -195,9 +195,9 @@ int pc_compile(int argc, char *argv[]) strcpy(binfname,outfname); ptr=get_extension(binfname); if (ptr!=NULL && stricmp(ptr,".asm")==0) - set_extension(binfname,".amx",TRUE); + set_extension(binfname,".smx",TRUE); else - set_extension(binfname,".amx",FALSE); + set_extension(binfname,".smx",FALSE); /* set output names that depend on the input name */ if (sc_listing) set_extension(outfname,".lst",TRUE); @@ -605,10 +605,10 @@ static void initglobals(void) litmax=sDEF_LITMAX; /* current size of the literal table */ errnum=0; /* number of errors */ warnnum=0; /* number of warnings */ - optproccall=TRUE; /* support "procedure call" */ + optproccall=FALSE; /* sourcemod: do not support "procedure call" */ verbosity=1; /* verbosity level, no copyright banner */ - sc_debug=sCHKBOUNDS; /* by default: bounds checking+assertions */ - pc_optimize=sOPTIMIZE_NOMACRO; + sc_debug=sSYMBOLIC; /* sourcemod: full debug stuff */ + pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */ sc_packstr=FALSE; /* strings are unpacked by default */ #if AMX_COMPACTMARGIN > 2 sc_compress=TRUE; /* compress output bytecodes */ @@ -758,6 +758,7 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam chdir(ptr); break; #endif +#if 0 /* not allowed to change for SourceMod */ case 'd': switch (*option_value(ptr)) { case '0': @@ -778,6 +779,7 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam about(); } /* switch */ break; +#endif case 'e': strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */ break; @@ -809,7 +811,7 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam break; case 'O': pc_optimize=*option_value(ptr) - '0'; - if (pc_optimize=sOPTIMIZE_NUMBER) + if (pc_optimize=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO) about(); break; case 'p': @@ -885,9 +887,11 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam case ';': sc_needsemicolon=toggle_option(ptr,sc_needsemicolon); break; +#if 0 /* not allowed to change in SourceMod */ case '(': optproccall=!toggle_option(ptr,!optproccall); break; +#endif default: /* wrong option */ about(); } /* switch */ @@ -905,8 +909,8 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam i=atoi(ptr+1); add_constant(str,i,sGLOBAL,0); } else { - strlcpy(str,argv[arg],sizeof(str)-2); /* -2 because default extension is ".p" */ - set_extension(str,".p",FALSE); + strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".psrc" */ + set_extension(str,".psrc",FALSE); insert_sourcefile(str); /* The output name is the first input name with a different extension, * but it is stored in a different directory @@ -1094,7 +1098,8 @@ static void setconfig(char *root) static void setcaption(void) { - pc_printf("Pawn compiler " VERSION_STR "\t \t \tCopyright (c) 1997-2006, ITB CompuPhase\n\n"); + pc_printf("SourcePawn Compiler " SMC_VERSTRING "\n"); + pc_printf("Copyright (c) 1997-2006, ITB CompuPhase, (C)2004-2006 AlliedModders, LLC\n\n"); } static void about(void) @@ -1112,11 +1117,13 @@ static void about(void) #if defined dos_setdrive pc_printf(" -Dpath active directory path\n"); #endif +#if 0 /* not used for SourceMod */ pc_printf(" -d debugging level (default=-d%d)\n",sc_debug); pc_printf(" 0 no symbolic information, no run-time checks\n"); pc_printf(" 1 run-time checks, no symbolic information\n"); pc_printf(" 2 full debug information and dynamic checking\n"); pc_printf(" 3 same as -d2, but implies -O0\n"); +#endif pc_printf(" -e set name of error file (quiet compile)\n"); #if defined __WIN32__ || defined _WIN32 || defined _Windows pc_printf(" -H window handle to send a notification message on finish\n"); @@ -1126,7 +1133,9 @@ static void about(void) pc_printf(" -o set base name of (P-code) output file\n"); pc_printf(" -O optimization level (default=-O%d)\n",pc_optimize); pc_printf(" 0 no optimization\n"); +#if 0 /* not used for SourceMod */ pc_printf(" 1 JIT-compatible optimizations only\n"); +#endif pc_printf(" 2 full optimizations\n"); pc_printf(" -p set name of \"prefix\" file\n"); #if !defined SC_LIGHT @@ -1142,7 +1151,9 @@ static void about(void) pc_printf(" -\\ use '\\' for escape characters\n"); pc_printf(" -^ use '^' for escape characters\n"); pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-'); +#if 0 /* not allowed in SourceMod */ pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+'); +#endif pc_printf(" sym=val define constant \"sym\" with value \"val\"\n"); pc_printf(" sym= define constant \"sym\" with value 0\n"); #if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__ From 5f41f7cb79e42b235cbc0e2661e26a13c48cdb47 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 01:55:21 +0000 Subject: [PATCH 03/20] fixed verbosity on no plugin --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%404 --- compiler-init/sc1.c | 4 +++- compiler-init/spcomp.vcproj | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 2c5c1862..64f44b92 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -131,6 +131,7 @@ static void addwhile(int *ptr); static void delwhile(void); static int *readwhile(void); +static int norun = 0; /* the compiler never ran */ static int lastst = 0; /* last executed statement type */ static int nestlevel = 0; /* number of active (open) compound statements */ static int rettype = 0; /* the type that a "return" expression should have */ @@ -435,7 +436,7 @@ cleanup: } /* if */ if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram) flag_exceed=1; - if ((sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) { + if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) { pc_printf("Header size: %8ld bytes\n", (long)hdrsize); pc_printf("Code size: %8ld bytes\n", (long)code_idx); pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell)); @@ -1164,6 +1165,7 @@ static void about(void) pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n"); pc_printf("and \"-d:0\" are all equivalent.\n"); } /* if */ + norun = 1; longjmp(errbuf,3); /* user abort */ } diff --git a/compiler-init/spcomp.vcproj b/compiler-init/spcomp.vcproj index 90da31f1..092d3a57 100644 --- a/compiler-init/spcomp.vcproj +++ b/compiler-init/spcomp.vcproj @@ -290,6 +290,14 @@ RelativePath=".\libpawnc.rc" > + + + + From 5ebf4942b4fd8bee6057147908f8c60e132f7273 Mon Sep 17 00:00:00 2001 From: Borja Ferrer Date: Fri, 14 Jul 2006 02:07:44 +0000 Subject: [PATCH 05/20] whoa svn is hard, sente will implode now --HG-- branch : faluco extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/faluco%407 --- compiler-init/amx.h | 462 +++ compiler-init/amxdbg.h | 172 ++ compiler-init/libpawnc.c | 257 ++ compiler-init/libpawnc.rc | 55 + compiler-init/lstring.c | 124 + compiler-init/lstring.h | 18 + compiler-init/memfile.c | 105 + compiler-init/memfile.h | 23 + compiler-init/osdefs.h | 108 + compiler-init/pawn.ico | Bin 0 -> 8478 bytes compiler-init/pawncc.c | 30 + compiler-init/sc.h | 813 +++++ compiler-init/sc1.c | 5605 +++++++++++++++++++++++++++++++++++ compiler-init/sc2.c | 2801 +++++++++++++++++ compiler-init/sc3.c | 2453 +++++++++++++++ compiler-init/sc4.c | 1331 +++++++++ compiler-init/sc5.c | 228 ++ compiler-init/sc5.scp | 342 +++ compiler-init/sc6.c | 1298 ++++++++ compiler-init/sc7.c | 703 +++++ compiler-init/sc7.scp | 2022 +++++++++++++ compiler-init/scexpand.c | 68 + compiler-init/sci18n.c | 428 +++ compiler-init/sclist.c | 486 +++ compiler-init/scmemfil.c | 179 ++ compiler-init/scstate.c | 375 +++ compiler-init/scvars.c | 113 + compiler-init/spcomp.sln | 21 + compiler-init/spcomp.vcproj | 305 ++ compiler-init/svnrev.h | 9 + 30 files changed, 20934 insertions(+) create mode 100644 compiler-init/amx.h create mode 100644 compiler-init/amxdbg.h create mode 100644 compiler-init/libpawnc.c create mode 100644 compiler-init/libpawnc.rc create mode 100644 compiler-init/lstring.c create mode 100644 compiler-init/lstring.h create mode 100644 compiler-init/memfile.c create mode 100644 compiler-init/memfile.h create mode 100644 compiler-init/osdefs.h create mode 100644 compiler-init/pawn.ico create mode 100644 compiler-init/pawncc.c create mode 100644 compiler-init/sc.h create mode 100644 compiler-init/sc1.c create mode 100644 compiler-init/sc2.c create mode 100644 compiler-init/sc3.c create mode 100644 compiler-init/sc4.c create mode 100644 compiler-init/sc5.c create mode 100644 compiler-init/sc5.scp create mode 100644 compiler-init/sc6.c create mode 100644 compiler-init/sc7.c create mode 100644 compiler-init/sc7.scp create mode 100644 compiler-init/scexpand.c create mode 100644 compiler-init/sci18n.c create mode 100644 compiler-init/sclist.c create mode 100644 compiler-init/scmemfil.c create mode 100644 compiler-init/scstate.c create mode 100644 compiler-init/scvars.c create mode 100644 compiler-init/spcomp.sln create mode 100644 compiler-init/spcomp.vcproj create mode 100644 compiler-init/svnrev.h diff --git a/compiler-init/amx.h b/compiler-init/amx.h new file mode 100644 index 00000000..6efdd35e --- /dev/null +++ b/compiler-init/amx.h @@ -0,0 +1,462 @@ +/* Pawn Abstract Machine (for the Pawn language) + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amx.h 3579 2006-06-06 13:35:29Z thiadmer $ + */ + +#ifndef AMX_H_INCLUDED +#define AMX_H_INCLUDED + +#include /* for size_t */ +#include + +#if defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#endif +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + +#if defined HAVE_STDINT_H + #include +#else + #if defined __LCC__ || defined __DMC__ || defined LINUX || (defined __WATCOMC__ && __WATCOMC__ >= 1200) + #if defined HAVE_INTTYPES_H + #include + #else + #include + #endif + #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L + /* The ISO C99 defines the int16_t and int_32t types. If the compiler got + * here, these types are probably undefined. + */ + #if defined __MACH__ + #include + typedef unsigned short int uint16_t; + typedef unsigned long int uint32_t; + #elif defined __FreeBSD__ + #include + #else + typedef short int int16_t; + typedef unsigned short int uint16_t; + #if defined SN_TARGET_PS2 + typedef int int32_t; + typedef unsigned int uint32_t; + #else + typedef long int int32_t; + typedef unsigned long int uint32_t; + #endif + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + #define HAVE_I64 + #elif defined __GNUC__ + typedef long long int64_t; + typedef unsigned long long uint64_t; + #define HAVE_I64 + #endif + #endif + #endif + #define HAVE_STDINT_H +#endif +#if defined _LP64 || defined WIN64 || defined _WIN64 + #if !defined __64BIT__ + #define __64BIT__ + #endif +#endif + +#if HAVE_ALLOCA_H + #include +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ + #if !defined alloca + #define alloca(n) _alloca(n) + #endif +#endif + +#if !defined arraysize + #define arraysize(array) (sizeof(array) / sizeof((array)[0])) +#endif +#if !defined assert_static + /* see "Compile-Time Assertions" by Ralf Holly, + * C/C++ Users Journal, November 2004 + */ + #define assert_static(e) \ + do { \ + enum { assert_static__ = 1/(e) }; \ + } while (0) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined PAWN_DLL + #if !defined AMX_NATIVE_CALL + #define AMX_NATIVE_CALL __stdcall + #endif + #if !defined AMXAPI + #define AMXAPI __stdcall + #endif +#endif + +/* calling convention for native functions */ +#if !defined AMX_NATIVE_CALL + #define AMX_NATIVE_CALL +#endif +/* calling convention for all interface functions and callback functions */ +#if !defined AMXAPI + #if defined STDECL + #define AMXAPI __stdcall + #elif defined CDECL + #define AMXAPI __cdecl + #elif defined GCC_HASCLASSVISIBILITY + #define AMXAPI __attribute__ ((visibility("default"))) + #else + #define AMXAPI + #endif +#endif +#if !defined AMXEXPORT + #define AMXEXPORT +#endif + +/* File format version (in CUR_FILE_VERSION) + * 0 (original version) + * 1 (opcodes JUMP.pri, SWITCH and CASETBL) + * 2 (compressed files) + * 3 (public variables) + * 4 (opcodes SWAP.pri/alt and PUSHADDR) + * 5 (tagnames table) + * 6 (reformatted header) + * 7 (name table, opcodes SYMTAG & SYSREQ.D) + * 8 (opcode STMT, renewed debug interface) + * 9 (macro opcodes) + * MIN_FILE_VERSION is the lowest file version number that the current AMX + * implementation supports. If the AMX file header gets new fields, this number + * often needs to be incremented. MAX_AMX_VERSION is the lowest AMX version that + * is needed to support the current file version. When there are new opcodes, + * this number needs to be incremented. + * The file version supported by the JIT may run behind MIN_AMX_VERSION. So + * there is an extra constant for it: MAX_FILE_VER_JIT. + */ +#define CUR_FILE_VERSION 9 /* current file version; also the current AMX version */ +#define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */ +#define MIN_AMX_VERSION 9 /* minimum AMX version needed to support the current file format */ +#define MAX_FILE_VER_JIT 8 /* file version supported by the JIT */ +#define MIN_AMX_VER_JIT 8 /* AMX version supported by the JIT */ + +#if !defined PAWN_CELL_SIZE + #define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */ +#endif +#if PAWN_CELL_SIZE==16 + typedef uint16_t ucell; + typedef int16_t cell; +#elif PAWN_CELL_SIZE==32 + typedef uint32_t ucell; + typedef int32_t cell; +#elif PAWN_CELL_SIZE==64 + typedef uint64_t ucell; + typedef int64_t cell; +#else + #error Unsupported cell size (PAWN_CELL_SIZE) +#endif + +#define UNPACKEDMAX (((cell)1 << (sizeof(cell)-1)*8) - 1) +#define UNLIMITED (~1u >> 1) + +struct tagAMX; +typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, cell *params); +typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index, + cell *result, cell *params); +typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx); +typedef int (AMXAPI *AMX_IDLE)(struct tagAMX *amx, int AMXAPI Exec(struct tagAMX *, cell *, int)); +#if !defined _FAR + #define _FAR +#endif + +#if defined _MSC_VER + #pragma warning(disable:4103) /* disable warning message 4103 that complains + * about pragma pack in a header file */ + #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ +#endif + +/* Some compilers do not support the #pragma align, which should be fine. Some + * compilers give a warning on unknown #pragmas, which is not so fine... + */ +#if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN + #define AMX_NO_ALIGN +#endif + +#if defined __GNUC__ + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=mac68k + #else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #if defined __TURBOC__ + #pragma option -a- /* "pack" pragma for older Borland compilers */ + #endif + #endif +#endif + +typedef struct tagAMX_NATIVE_INFO { + const char _FAR *name PACKED; + AMX_NATIVE func PACKED; +} AMX_NATIVE_INFO; + +#define AMX_USERNUM 4 +#define sEXPMAX 19 /* maximum name length for file version <= 6 */ +#define sNAMEMAX 31 /* maximum name length of symbol name */ + +typedef struct tagAMX_FUNCSTUB { + ucell address PACKED; + char name[sEXPMAX+1] PACKED; +} AMX_FUNCSTUB; + +typedef struct tagFUNCSTUBNT { + ucell address PACKED; + uint32_t nameofs PACKED; +} AMX_FUNCSTUBNT; + +/* The AMX structure is the internal structure for many functions. Not all + * fields are valid at all times; many fields are cached in local variables. + */ +typedef struct tagAMX { + unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */ + unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */ + AMX_CALLBACK callback PACKED; + AMX_DEBUG debug PACKED; /* debug callback */ + /* for external functions a few registers must be accessible from the outside */ + cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */ + cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */ + cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */ + cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */ + cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */ + cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */ + int flags PACKED; /* current status, see amx_Flags() */ + /* user data */ + long usertags[AMX_USERNUM] PACKED; + void _FAR *userdata[AMX_USERNUM] PACKED; + /* native functions can raise an error */ + int error PACKED; + /* passing parameters requires a "count" field */ + int paramcount; + /* the sleep opcode needs to store the full AMX status */ + cell pri PACKED; + cell alt PACKED; + cell reset_stk PACKED; + cell reset_hea PACKED; + cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */ + #if defined JIT + /* support variables for the JIT */ + int reloc_size PACKED; /* required temporary buffer for relocations */ + long code_size PACKED; /* estimated memory footprint of the native code */ + #endif +} AMX; + +/* The AMX_HEADER structure is both the memory format as the file format. The + * structure is used internaly. + */ +typedef struct tagAMX_HEADER { + int32_t size PACKED; /* size of the "file" */ + uint16_t magic PACKED; /* signature */ + char file_version PACKED; /* file format version */ + char amx_version PACKED; /* required version of the AMX */ + int16_t flags PACKED; + int16_t defsize PACKED; /* size of a definition record */ + int32_t cod PACKED; /* initial value of COD - code block */ + int32_t dat PACKED; /* initial value of DAT - data block */ + int32_t hea PACKED; /* initial value of HEA - start of the heap */ + int32_t stp PACKED; /* initial value of STP - stack top */ + int32_t cip PACKED; /* initial value of CIP - the instruction pointer */ + int32_t publics PACKED; /* offset to the "public functions" table */ + int32_t natives PACKED; /* offset to the "native functions" table */ + int32_t libraries PACKED; /* offset to the table of libraries */ + int32_t pubvars PACKED; /* the "public variables" table */ + int32_t tags PACKED; /* the "public tagnames" table */ + int32_t nametable PACKED; /* name table */ +} AMX_HEADER; + +#if PAWN_CELL_SIZE==16 + #define AMX_MAGIC 0xf1e2 +#elif PAWN_CELL_SIZE==32 + #define AMX_MAGIC 0xf1e0 +#elif PAWN_CELL_SIZE==64 + #define AMX_MAGIC 0xf1e1 +#endif + +enum { + AMX_ERR_NONE, + /* reserve the first 15 error codes for exit codes of the abstract machine */ + AMX_ERR_EXIT, /* forced exit */ + AMX_ERR_ASSERT, /* assertion failed */ + AMX_ERR_STACKERR, /* stack/heap collision */ + AMX_ERR_BOUNDS, /* index out of bounds */ + AMX_ERR_MEMACCESS, /* invalid memory access */ + AMX_ERR_INVINSTR, /* invalid instruction */ + AMX_ERR_STACKLOW, /* stack underflow */ + AMX_ERR_HEAPLOW, /* heap underflow */ + AMX_ERR_CALLBACK, /* no callback, or invalid callback */ + AMX_ERR_NATIVE, /* native function failed */ + AMX_ERR_DIVIDE, /* divide by zero */ + AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */ + AMX_ERR_INVSTATE, /* invalid state for this access */ + + AMX_ERR_MEMORY = 16, /* out of memory */ + AMX_ERR_FORMAT, /* invalid file format */ + AMX_ERR_VERSION, /* file is for a newer version of the AMX */ + AMX_ERR_NOTFOUND, /* function not found */ + AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */ + AMX_ERR_DEBUG, /* debugger cannot run */ + AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */ + AMX_ERR_USERDATA, /* unable to set user data field (table full) */ + AMX_ERR_INIT_JIT, /* cannot initialize the JIT */ + AMX_ERR_PARAMS, /* parameter error */ + AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */ + AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */ +}; + +/* AMX_FLAG_CHAR16 0x01 no longer used */ +#define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */ +#define AMX_FLAG_COMPACT 0x04 /* compact encoding */ +#define AMX_FLAG_SLEEP 0x08 /* script uses the sleep instruction (possible re-entry or power-down mode) */ +#define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no BREAK opcodes */ +#define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */ +#define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */ +#define AMX_FLAG_BROWSE 0x4000 /* busy browsing */ +#define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ + +#define AMX_EXEC_MAIN (-1) /* start at program entry point */ +#define AMX_EXEC_CONT (-2) /* continue from last address */ + +#define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24)) + +#if !defined AMX_COMPACTMARGIN + #define AMX_COMPACTMARGIN 64 +#endif + +/* for native functions that use floating point parameters, the following + * two macros are convenient for casting a "cell" into a "float" type _without_ + * changing the bit pattern + */ +#if PAWN_CELL_SIZE==32 + #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ + #define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */ +#elif PAWN_CELL_SIZE==64 + #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ + #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ +#else + #error Unsupported cell size +#endif + +#define amx_StrParam(amx,param,result) \ + do { \ + cell *amx_cstr_; int amx_length_; \ + amx_GetAddr((amx), (param), &amx_cstr_); \ + amx_StrLen(amx_cstr_, &amx_length_); \ + if (amx_length_ > 0 && \ + ((result) = (void*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \ + amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \ + else (result) = NULL; \ + } while (0) + +uint16_t * AMXAPI amx_Align16(uint16_t *v); +uint32_t * AMXAPI amx_Align32(uint32_t *v); +#if defined _I64_MAX || defined HAVE_I64 + uint64_t * AMXAPI amx_Align64(uint64_t *v); +#endif +int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); +int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params); +int AMXAPI amx_Cleanup(AMX *amx); +int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); +int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); +int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); +int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr); +int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname); +int AMXAPI amx_Flags(AMX *amx,uint16_t *flags); +int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr); +int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname); +int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname); +int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr); +int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size); +int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id); +int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr); +int AMXAPI amx_Init(AMX *amx, void *program); +int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code); +int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap); +int AMXAPI amx_NameLength(AMX *amx, int *length); +AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func); +int AMXAPI amx_NumNatives(AMX *amx, int *number); +int AMXAPI amx_NumPublics(AMX *amx, int *number); +int AMXAPI amx_NumPubVars(AMX *amx, int *number); +int AMXAPI amx_NumTags(AMX *amx, int *number); +int AMXAPI amx_Push(AMX *amx, cell value); +int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); +int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); +int AMXAPI amx_RaiseError(AMX *amx, int error); +int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); +int AMXAPI amx_Release(AMX *amx, cell amx_addr); +int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback); +int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug); +int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size); +int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr); +int AMXAPI amx_StrLen(const cell *cstring, int *length); +int AMXAPI amx_UTF8Check(const char *string, int *length); +int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value); +int AMXAPI amx_UTF8Len(const cell *cstr, int *length); +int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); + +#if PAWN_CELL_SIZE==16 + #define amx_AlignCell(v) amx_Align16(v) +#elif PAWN_CELL_SIZE==32 + #define amx_AlignCell(v) amx_Align32(v) +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) + #define amx_AlignCell(v) amx_Align64(v) +#else + #error Unsupported cell size +#endif + +#define amx_RegisterFunc(amx, name, func) \ + amx_Register((amx), amx_NativeInfo((name),(func)), 1); + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack() /* reset default packing */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=reset + #else + #pragma pack(pop) /* reset previous packing */ + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMX_H_INCLUDED */ diff --git a/compiler-init/amxdbg.h b/compiler-init/amxdbg.h new file mode 100644 index 00000000..acc4d1b3 --- /dev/null +++ b/compiler-init/amxdbg.h @@ -0,0 +1,172 @@ +/* Abstract Machine for the Pawn compiler, debugger support + * + * This file contains extra definitions that are convenient for debugger + * support. + * + * Copyright (c) ITB CompuPhase, 2005-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxdbg.h 3519 2006-02-17 17:57:04Z thiadmer $ + */ + +#ifndef AMXDBG_H_INCLUDED +#define AMXDBG_H_INCLUDED + +#ifndef AMX_H_INCLUDED + #include "amx.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Some compilers do not support the #pragma align, which should be fine. Some + * compilers give a warning on unknown #pragmas, which is not so fine... + */ +#if defined SN_TARGET_PS2 || defined __GNUC__ + #define AMX_NO_ALIGN +#endif + +#if defined __GNUC__ + #define PACKED __attribute__((packed)) +#else + #define PACKED +#endif + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=mac68k + #else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ + #if defined __TURBOC__ + #pragma option -a- /* "pack" pragma for older Borland compilers */ + #endif + #endif +#endif + +typedef struct tagAMX_DBG_HDR { + int32_t size PACKED; /* size of the debug information chunk */ + uint16_t magic PACKED; /* signature, must be 0xf1ef */ + char file_version PACKED; /* file format version */ + char amx_version PACKED; /* required version of the AMX */ + int16_t flags PACKED; /* currently unused */ + int16_t files PACKED; /* number of entries in the "file" table */ + int16_t lines PACKED; /* number of entries in the "line" table */ + int16_t symbols PACKED; /* number of entries in the "symbol" table */ + int16_t tags PACKED; /* number of entries in the "tag" table */ + int16_t automatons PACKED; /* number of entries in the "automaton" table */ + int16_t states PACKED; /* number of entries in the "state" table */ +} PACKED AMX_DBG_HDR; +#define AMX_DBG_MAGIC 0xf1ef + +typedef struct tagAMX_DBG_FILE { + ucell address PACKED; /* address in the code segment where generated code (for this file) starts */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_FILE; + +typedef struct tagAMX_DBG_LINE { + ucell address PACKED; /* address in the code segment where generated code (for this line) starts */ + int32_t line PACKED; /* line number */ +} PACKED AMX_DBG_LINE; + +typedef struct tagAMX_DBG_SYMBOL { + ucell address PACKED; /* address in the data segment or relative to the frame */ + int16_t tag PACKED; /* tag for the symbol */ + ucell codestart PACKED; /* address in the code segment from which this symbol is valid (in scope) */ + ucell codeend PACKED; /* address in the code segment until which this symbol is valid (in scope) */ + char ident PACKED; /* kind of symbol (function/variable) */ + char vclass PACKED; /* class of symbol (global/local) */ + int16_t dim PACKED; /* number of dimensions */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_SYMBOL; + +typedef struct tagAMX_DBG_SYMDIM { + int16_t tag PACKED; /* tag for the array dimension */ + ucell size PACKED; /* size of the array dimension */ +} PACKED AMX_DBG_SYMDIM; + +typedef struct tagAMX_DBG_TAG { + int16_t tag PACKED; /* tag id */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_TAG; + +typedef struct tagAMX_DBG_MACHINE { + int16_t automaton PACKED; /* automaton id */ + ucell address PACKED; /* address of state variable */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_MACHINE; + +typedef struct tagAMX_DBG_STATE { + int16_t state PACKED; /* state id */ + int16_t automaton PACKED; /* automaton id */ + const char name[1] PACKED; /* ASCII string, zero-terminated */ +} PACKED AMX_DBG_STATE; + +typedef struct tagAMX_DBG { + AMX_DBG_HDR *hdr PACKED; /* points to the AMX_DBG header */ + AMX_DBG_FILE **filetbl PACKED; + AMX_DBG_LINE *linetbl PACKED; + AMX_DBG_SYMBOL **symboltbl PACKED; + AMX_DBG_TAG **tagtbl PACKED; + AMX_DBG_MACHINE **automatontbl PACKED; + AMX_DBG_STATE **statetbl PACKED; +} PACKED AMX_DBG; + +#if !defined iVARIABLE + #define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ + #define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ + #define iARRAY 3 + #define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ + #define iFUNCTN 9 +#endif + + +int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg); +int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp); + +int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename); +int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname); +int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line); + +int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address); +int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address); +int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name); +int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name); +int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name); +int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym); +int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim); + + +#if !defined AMX_NO_ALIGN + #if defined LINUX || defined __FreeBSD__ + #pragma pack() /* reset default packing */ + #elif defined MACOS && defined __MWERKS__ + #pragma options align=reset + #else + #pragma pack(pop) /* reset previous packing */ + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AMXDBG_H_INCLUDED */ diff --git a/compiler-init/libpawnc.c b/compiler-init/libpawnc.c new file mode 100644 index 00000000..ca42d1a9 --- /dev/null +++ b/compiler-init/libpawnc.c @@ -0,0 +1,257 @@ +/* LIBPAWNC.C + * + * A "glue file" for building the Pawn compiler as a DLL or shared library. + * + * Copyright (c) ITB CompuPhase, 2000-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: libsc.c 3114 2005-03-17 14:48:29Z thiadmer $ + */ +#include +#include +#include +#include +#include +#include "sc.h" +#include "memfile.h" + +/* pc_printf() + * Called for general purpose "console" output. This function prints general + * purpose messages; errors go through pc_error(). The function is modelled + * after printf(). + */ +int pc_printf(const char *message,...) +{ + int ret; + va_list argptr; + + va_start(argptr,message); + ret=vprintf(message,argptr); + va_end(argptr); + + return ret; +} + +/* pc_error() + * Called for producing error output. + * number the error number (as documented in the manual) + * message a string describing the error with embedded %d and %s tokens + * filename the name of the file currently being parsed + * firstline the line number at which the expression started on which + * the error was found, or -1 if there is no "starting line" + * lastline the line number at which the error was detected + * argptr a pointer to the first of a series of arguments (for macro + * "va_arg") + * Return: + * If the function returns 0, the parser attempts to continue compilation. + * On a non-zero return value, the parser aborts. + */ +int pc_error(int number,char *message,char *filename,int firstline,int lastline,va_list argptr) +{ +static char *prefix[3]={ "error", "fatal error", "warning" }; + + if (number!=0) { + char *pre; + + pre=prefix[number/100]; + if (firstline>=0) + fprintf(stderr,"%s(%d -- %d) : %s %03d: ",filename,firstline,lastline,pre,number); + else + fprintf(stderr,"%s(%d) : %s %03d: ",filename,lastline,pre,number); + } /* if */ + vfprintf(stderr,message,argptr); + fflush(stderr); + return 0; +} + +/* pc_opensrc() + * Opens a source file (or include file) for reading. The "file" does not have + * to be a physical file, one might compile from memory. + * filename the name of the "file" to read from + * Return: + * The function must return a pointer, which is used as a "magic cookie" to + * all I/O functions. When failing to open the file for reading, the + * function must return NULL. + * Note: + * Several "source files" may be open at the same time. Specifically, one + * file can be open for reading and another for writing. + */ +void *pc_opensrc(char *filename) +{ + return fopen(filename,"rt"); +} + +/* pc_createsrc() + * Creates/overwrites a source file for writing. The "file" does not have + * to be a physical file, one might compile from memory. + * filename the name of the "file" to create + * Return: + * The function must return a pointer which is used as a "magic cookie" to + * all I/O functions. When failing to open the file for reading, the + * function must return NULL. + * Note: + * Several "source files" may be open at the same time. Specifically, one + * file can be open for reading and another for writing. + */ +void *pc_createsrc(char *filename) +{ + return fopen(filename,"wt"); +} + +/* pc_closesrc() + * Closes a source file (or include file). The "handle" parameter has the + * value that pc_opensrc() returned in an earlier call. + */ +void pc_closesrc(void *handle) +{ + assert(handle!=NULL); + fclose((FILE*)handle); +} + +/* pc_resetsrc() + * "position" may only hold a pointer that was previously obtained from + * pc_getpossrc() + */ +void pc_resetsrc(void *handle,void *position) +{ + assert(handle!=NULL); + fsetpos((FILE*)handle,(fpos_t *)position); +} + +/* pc_readsrc() + * Reads a single line from the source file (or up to a maximum number of + * characters if the line in the input file is too long). + */ +char *pc_readsrc(void *handle,unsigned char *target,int maxchars) +{ + return fgets((char*)target,maxchars,(FILE*)handle); +} + +/* pc_writesrc() + * Writes to to the source file. There is no automatic line ending; to end a + * line, write a "\n". + */ +int pc_writesrc(void *handle,unsigned char *source) +{ + return fputs((char*)source,(FILE*)handle) >= 0; +} + +void *pc_getpossrc(void *handle) +{ + static fpos_t lastpos; /* may need to have a LIFO stack of such positions */ + + fgetpos((FILE*)handle,&lastpos); + return &lastpos; +} + +int pc_eofsrc(void *handle) +{ + return feof((FILE*)handle); +} + +/* should return a pointer, which is used as a "magic cookie" to all I/O + * functions; return NULL for failure + */ +void *pc_openasm(char *filename) +{ + #if defined __MSDOS__ || defined SC_LIGHT + return fopen(filename,"w+t"); + #else + return mfcreate(filename); + #endif +} + +void pc_closeasm(void *handle, int deletefile) +{ + #if defined __MSDOS__ || defined SC_LIGHT + if (handle!=NULL) + fclose((FILE*)handle); + if (deletefile) + remove(outfname); + #else + if (handle!=NULL) { + if (!deletefile) + mfdump((MEMFILE*)handle); + mfclose((MEMFILE*)handle); + } /* if */ + #endif +} + +void pc_resetasm(void *handle) +{ + assert(handle!=NULL); + #if defined __MSDOS__ || defined SC_LIGHT + fflush((FILE*)handle); + fseek((FILE*)handle,0,SEEK_SET); + #else + mfseek((MEMFILE*)handle,0,SEEK_SET); + #endif +} + +int pc_writeasm(void *handle,char *string) +{ + #if defined __MSDOS__ || defined SC_LIGHT + return fputs(string,(FILE*)handle) >= 0; + #else + return mfputs((MEMFILE*)handle,string); + #endif +} + +char *pc_readasm(void *handle, char *string, int maxchars) +{ + #if defined __MSDOS__ || defined SC_LIGHT + return fgets(string,maxchars,(FILE*)handle); + #else + return mfgets((MEMFILE*)handle,string,maxchars); + #endif +} + +/* Should return a pointer, which is used as a "magic cookie" to all I/O + * functions; return NULL for failure. + */ +void *pc_openbin(char *filename) +{ + return memfile_creat(filename, 1); +} + +void pc_closebin(void *handle,int deletefile) +{ + if (deletefile) + { + memfile_destroy((memfile_t *)handle); + } +} + +/* pc_resetbin() + * Can seek to any location in the file. + * The offset is always from the start of the file. + */ +void pc_resetbin(void *handle,long offset) +{ + memfile_seek((memfile_t *)handle, offset); +} + +int pc_writebin(void *handle,void *buffer,int size) +{ + return memfile_write((memfile_t *)handle, buffer, size); +} + +long pc_lengthbin(void *handle) +{ + return memfile_tell((memfile_t *)handle); +} diff --git a/compiler-init/libpawnc.rc b/compiler-init/libpawnc.rc new file mode 100644 index 00000000..df89015a --- /dev/null +++ b/compiler-init/libpawnc.rc @@ -0,0 +1,55 @@ +#include +#if defined WIN32 || defined _WIN32 || defined __WIN32__ +# include +#else +# include +#endif +#include "svnrev.h" + +AppIcon ICON "pawn.ico" + +/* Version information + * + * All strings MUST have an explicit \0. See the Windows SDK documentation + * for details on version information and the VERSIONINFO structure. + */ +#define VERSION SMC_VERSION +#define REVISION SMC_REVISION +#define BUILD SMC_BUILD +#define VERSIONSTR SMC_VERSTRING +#define VERSIONNAME "smcomp.exe\0" +#define VERSIONDESCRIPTION "SourcePawn Compiler\0" +#define VERSIONPRODUCTNAME "smcomp\0" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VERSION, REVISION, BUILD, 0 +PRODUCTVERSION VERSION, REVISION, BUILD, 0 +FILEFLAGSMASK 0x0000003FL +FILEFLAGS 0 +#if defined(WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_DLL +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "(C)1998-2006 ITB CompuPhase, AlliedModders LLC\0" + VALUE "FileDescription", VERSIONDESCRIPTION + VALUE "FileVersion", VERSIONSTR + VALUE "InternalName", VERSIONNAME + VALUE "LegalCopyright", "(C)1998-2006 ITB CompuPhase, AlliedModders LLC\0" + VALUE "OriginalFilename", VERSIONNAME + VALUE "ProductName", VERSIONPRODUCTNAME + VALUE "ProductVersion", VERSIONSTR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/compiler-init/lstring.c b/compiler-init/lstring.c new file mode 100644 index 00000000..f5156b32 --- /dev/null +++ b/compiler-init/lstring.c @@ -0,0 +1,124 @@ +/* Safe string copying and concatenation + * These routines are originally distributed in two separate files. I have + * copied the files verbatim in this single source file, including all comments. + * The only change is that the second set of include files is commented out + * (there is no need to include the same files twice). + */ + +#include "lstring.h" + +#if !defined HAVE_SAFESTR + +/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + #include already included through lstring.h + */ +#include /* for strlen() */ + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + #include already included + #include already included +*/ + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +#endif /* #if !defined HAVE_SAFESTR */ diff --git a/compiler-init/lstring.h b/compiler-init/lstring.h new file mode 100644 index 00000000..ca62cb9a --- /dev/null +++ b/compiler-init/lstring.h @@ -0,0 +1,18 @@ +/* prototypes for strlcpy() and strlcat() */ + +#include + +#if defined __WATCOMC__ && __WATCOMC__ >= 1240 + /* OpenWatcom introduced BSD "safe string functions" with version 1.4 */ + #define HAVE_SAFESTR +#endif + +#if !defined HAVE_SAFESTR + +size_t +strlcpy(char *dst, const char *src, size_t siz); + +size_t +strlcat(char *dst, const char *src, size_t siz); + +#endif diff --git a/compiler-init/memfile.c b/compiler-init/memfile.c new file mode 100644 index 00000000..2047f710 --- /dev/null +++ b/compiler-init/memfile.c @@ -0,0 +1,105 @@ +#include "memfile.h" +#include +#include "osdefs.h" + +memfile_t *memfile_creat(const char *name, size_t init) +{ + memfile_t mf; + memfile_t *pmf; + + mf.size = init; + mf.base = (char *)malloc(init); + mf.usedoffs = 0; + if (!mf.base) + { + return NULL; + } + + mf.offs = 0; + mf._static = 0; + + pmf = (memfile_t *)malloc(sizeof(memfile_t)); + memcpy(pmf, &mf, sizeof(memfile_t)); + + pmf->name = strdup(name); + + return pmf; +} + +void memfile_destroy(memfile_t *mf) +{ + if (!mf->_static) + { + free(mf->name); + free(mf->base); + free(mf); + } +} + +void memfile_seek(memfile_t *mf, long seek) +{ + mf->offs = seek; +} + +long memfile_tell(memfile_t *mf) +{ + return mf->offs; +} + +size_t memfile_read(memfile_t *mf, void *buffer, size_t maxsize) +{ + if (!maxsize || mf->offs >= mf->usedoffs) + { + return 0; + } + + if (mf->usedoffs - mf->offs < (long)maxsize) + { + maxsize = mf->usedoffs - mf->offs; + if (!maxsize) + { + return 0; + } + } + + memcpy(buffer, mf->base + mf->offs, maxsize); + + mf->offs += maxsize; + + return maxsize; +} + +int memfile_write(memfile_t *mf, void *buffer, size_t size) +{ + if (mf->offs + size > mf->size) + { + size_t newsize = (mf->size + size) * 2; + if (mf->_static) + { + char *oldbase = mf->base; + mf->base = (char *)malloc(newsize); + if (!mf->base) + { + return 0; + } + memcpy(mf->base, oldbase, mf->size); + } else { + mf->base = (char *)realloc(mf->base, newsize); + if (!mf->base) + { + return 0; + } + } + mf->_static = 0; + mf->size = newsize; + } + memcpy(mf->base + mf->offs, buffer, size); + mf->offs += size; + + if (mf->offs > mf->usedoffs) + { + mf->usedoffs = mf->offs; + } + + return 1; +} diff --git a/compiler-init/memfile.h b/compiler-init/memfile.h new file mode 100644 index 00000000..f5222a9e --- /dev/null +++ b/compiler-init/memfile.h @@ -0,0 +1,23 @@ +#ifndef _INCLUDE_MEMFILE_H +#define _INCLUDE_MEMFILE_H + +#include + +typedef struct memfile_s +{ + char *name; + char *base; + long offs; + long usedoffs; + size_t size; + int _static; +} memfile_t; + +memfile_t *memfile_creat(const char *name, size_t init); +void memfile_destroy(memfile_t *mf); +void memfile_seek(memfile_t *mf, long seek); +int memfile_write(memfile_t *mf, void *buffer, size_t size); +size_t memfile_read(memfile_t *mf, void *buffer, size_t maxsize); +long memfile_tell(memfile_t *mf); + +#endif //_INCLUDE_MEMFILE_H diff --git a/compiler-init/osdefs.h b/compiler-init/osdefs.h new file mode 100644 index 00000000..299af953 --- /dev/null +++ b/compiler-init/osdefs.h @@ -0,0 +1,108 @@ +/* __MSDOS__ set when compiling for DOS (not Windows) + * _Windows set when compiling for any version of Microsoft Windows + * __WIN32__ set when compiling for Windows95 or WindowsNT (32 bit mode) + * __32BIT__ set when compiling in 32-bit "flat" mode (DOS or Windows) + * + * Copyright 1998-2005, ITB CompuPhase, The Netherlands. + * info@compuphase.com. + */ + +#ifndef _OSDEFS_H +#define _OSDEFS_H + +/* Every compiler uses different "default" macros to indicate the mode + * it is in. Throughout the source, we use the Borland C++ macros, so + * the macros of Watcom C/C++ and Microsoft Visual C/C++ are mapped to + * those of Borland C++. + */ +#if defined(__WATCOMC__) +# if defined(__WINDOWS__) || defined(__NT__) +# define _Windows 1 +# endif +# ifdef __386__ +# define __32BIT__ 1 +# endif +# if defined(_Windows) && defined(__32BIT__) +# define __WIN32__ 1 +# endif +#elif defined(_MSC_VER) +# if defined(_WINDOWS) || defined(_WIN32) +# define _Windows 1 +# endif +# ifdef _WIN32 +# define __WIN32__ 1 +# define __32BIT__ 1 +# endif +# if _MSC_VER >= 1400 +# if !defined _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# define strdup _strdup +# define stricmp _stricmp +# define access _access +# define chdir _chdir +# define strdup _strdup +# endif +#endif + +#if defined __FreeBSD__ + #include +#elif defined LINUX + #include +#endif + +/* Linux NOW has these */ +#if !defined BIG_ENDIAN + #define BIG_ENDIAN 4321 +#endif +#if !defined LITTLE_ENDIAN + #define LITTLE_ENDIAN 1234 +#endif + +/* educated guess, BYTE_ORDER is undefined, i386 is common => little endian */ +#if !defined BYTE_ORDER + #if defined UCLINUX + #define BYTE_ORDER BIG_ENDIAN + #else + #define BYTE_ORDER LITTLE_ENDIAN + #endif +#endif + +#if defined __MSDOS__ || defined __WIN32__ || defined _Windows + #define DIRSEP_CHAR '\\' +#elif defined macintosh + #define DIRSEP_CHAR ':' +#else + #define DIRSEP_CHAR '/' /* directory separator character */ +#endif + +/* _MAX_PATH is sometimes called differently and it may be in limits.h or + * stdlib.h instead of stdio.h. + */ +#if !defined _MAX_PATH + /* not defined, perhaps stdio.h was not included */ + #if !defined PATH_MAX + #include + #endif + #if !defined _MAX_PATH && !defined PATH_MAX + /* no _MAX_PATH and no MAX_PATH, perhaps it is in limits.h */ + #include + #endif + #if !defined _MAX_PATH && !defined PATH_MAX + /* no _MAX_PATH and no MAX_PATH, perhaps it is in stdlib.h */ + #include + #endif + /* if _MAX_PATH is undefined, try common alternative names */ + #if !defined _MAX_PATH + #if defined MAX_PATH + #define _MAX_PATH MAX_PATH + #elif defined _POSIX_PATH_MAX + #define _MAX_PATH _POSIX_PATH_MAX + #else + /* everything failed, actually we have a problem here... */ + #define _MAX_PATH 1024 + #endif + #endif +#endif + +#endif /* _OSDEFS_H */ diff --git a/compiler-init/pawn.ico b/compiler-init/pawn.ico new file mode 100644 index 0000000000000000000000000000000000000000..7a6ab60bc5be72862ee6fee5e3bd4585a6644f36 GIT binary patch literal 8478 zcmeI1J!~6C7RN`X0-1jOasId_?ko&&fT6+xS*394Ykk$=3c7X~x^Y9`0H!do zU5Z1HN;p`{=BPq$&)d5&1?24JqK^}mD=Z*^=YId$U22!&QoJ_M$p4+M_ujmDGn|?C zY|N7R#N4}QLi%CNm_Jc!G}8KCJ~8GaZH=tq%Ozvn7slMWmG=K|)tJAotDZ9a`-(At z{Jk+>(k+pH21^DarFxo8f4gen)6ELqtg^eiYj#Jw=KFt+&GPcHxq9`gSzTQ<&1TbF zzkc1^xN*bWxpT+dy?fU@di2OVeE86`S}k+`{(bZ8*)#J@a`{jUh0sF9Qga{z2~dLt z4n!aU9*D>S2O^LF55#DJ0})7o2NGt10})7oSKU(w34kK}Ild7{fCo~*0tX_H01u>; z1r9_Y0Uk(E3mk|*0z8oN7B~=r1bASXkOL7&fCmU?WEcJ-z6tO^leWNt2qeG*8Nvbw zB9H(X#sUW-kN^*4EDIu#01sq53nGvJ&xi?M88O13`b%V7S%x~4j2%Hi8^*T4iHDslm->rW7>Dp%>D(KSYTU|S<<~Y9H>4gMKU}!g6jn!&~>nXLqDM!1_ar9Z`03QPV7AbH@CFu7n=~qPG zkK?%Cha>0&{b?orIHiUm4h%tjYmSLh`eA(tLnozfwJXIN6HA%VXaI4mi7HCm4b@4i z^jsj#=-buiL`>XIiCay`1IIjdj5&e4t)(JiOE~7tR%&*7@vu%BjpBH#=i2kdn?AEm z(_2lY-|KLy%}McFC6Tgbe0rVM9BSs9r#|b;9GJ{pdQ*?Xh6tKy#!u#x(>n-5Nb~jr z)G$czAfKCcku(T9wC5I4Cdlg{f2+gRSwuQC+I1av#093ANZaZ4CB60Sq~(HwbZ^bw z_+$}bZ{9lzudfb*Gj@RaD}) z=)>_3eFeo-bDdM2Y4qZ$_UdYVI2cs;KHmR$jdzguAN3?6u|f}&kBzyxNS@Q@`L7v2 z`8VRv^IU#jpU(4s->r$w``wjtp6QwEJITj4Q@Ta-TyE7kdAnIk*F{Bwv22Jl9)b;-@ZAnUT6`<8$e!;x%6SscZ)Mi1Hip8p-9gUHYl`lD||+PI-Gz2fnEdQ#u9TLbBOHPM`jHeB)h0XL0$+j2eY8wvM(V1Kk8GoY{#3b)o3awbNH622m-o?cKW=)ma&+)y zfB)d{c&uY!Ivsw!zV6(5yVYp44}PBRHK%EaQ^7ZE`8` zmYZptyuXj9Ubx2d=1Kkm>&-BIslQTtsQQE&=E(1FDIRowZ{+gB(pSgR#@ZPy{;buL zh$o@Da)!qWp9GTGQ#cB-cu~xbSFyvjzmYL}iUaN75HFq0KaPnX5N;?x#qzn64@dtZ ze!(b(wo;Mk^C;fxm)c5|z52QML2%EzxX5yMi;(*~4Y}NMd*z}Fa($;eUDWkZ0-2AM8fMoHg+Orq*V!GLeM( zMk9Og&8iQ#Z8LqniN3aJH}u+?B&!EvZBng$jzmVxQ*CoN+}_q}FHNdLkyjaT6Zw_BT&6^Jd!x`T4bQX{MQx*?jp~gvZ9@Owr|Jc5XZ16`(s)ewY&tN|S*%NVaRAGI zxSyE|b+ +#include +#include +#if defined __BORLANDC__ && defined _Windows && !(defined __32BIT__ || defined __WIN32__) + /* setjmp() and longjmp() not well supported in 16-bit windows */ + #include + typedef int jmp_buf[9]; + #define setjmp(b) Catch(b) + #define longjmp(b,e) Throw(b,e) +#else + #include +#endif +#include "osdefs.h" +#include "amx.h" + +/* Note: the "cell" and "ucell" types are defined in AMX.H */ + +#define PUBLIC_CHAR '@' /* character that defines a function "public" */ +#define CTRL_CHAR '\\' /* default control character */ +#define sCHARBITS 8 /* size of a packed character */ + +#define sDIMEN_MAX 4 /* maximum number of array dimensions */ +#define sLINEMAX 1024 /* input line length (in characters) */ +#define sCOMP_STACK 32 /* maximum nesting of #if .. #endif sections */ +#define sDEF_LITMAX 500 /* initial size of the literal pool, in "cells" */ +#define sDEF_AMXSTACK 4096 /* default stack size for AMX files */ +#define PREPROC_TERM '\x7f'/* termination character for preprocessor expressions (the "DEL" code) */ +#define sDEF_PREFIX "sourcemod.inc" /* default prefix filename */ + +typedef union { + void *pv; /* e.g. a name */ + int i; +} stkitem; /* type of items stored on the compiler stack */ + +typedef struct s_arginfo { /* function argument info */ + char name[sNAMEMAX+1]; + char ident; /* iVARIABLE, iREFERENCE, iREFARRAY or iVARARGS */ + char usage; /* uCONST */ + int *tags; /* argument tag id. list */ + int numtags; /* number of tags in the tag list */ + int dim[sDIMEN_MAX]; + int idxtag[sDIMEN_MAX]; + int numdim; /* number of dimensions */ + unsigned char hasdefault; /* bit0: is there a default value? bit6: "tagof"; bit7: "sizeof" */ + union { + cell val; /* default value */ + struct { + char *symname; /* name of another symbol */ + short level; /* indirection level for that symbol */ + } size; /* used for "sizeof" default value */ + struct { + cell *data; /* values of default array */ + int size; /* complete length of default array */ + int arraysize; /* size to reserve on the heap */ + cell addr; /* address of the default array in the data segment */ + } array; + } defvalue; /* default value, or pointer to default array */ + int defvalue_tag; /* tag of the default value */ +} arginfo; + +/* Equate table, tagname table, library table */ +typedef struct s_constvalue { + struct s_constvalue *next; + char name[sNAMEMAX+1]; + cell value; + int index; /* index level, for constants referring to array sizes/tags + * automaton id. for states and automatons + * tag for enumeration lists */ +} constvalue; + +/* Symbol table format + * + * The symbol name read from the input file is stored in "name", the + * value of "addr" is written to the output file. The address in "addr" + * depends on the class of the symbol: + * global offset into the data segment + * local offset relative to the stack frame + * label generated hexadecimal number + * function offset into code segment + */ +typedef struct s_symbol { + struct s_symbol *next; + struct s_symbol *parent; /* hierarchical types (multi-dimensional arrays) */ + char name[sNAMEMAX+1]; + uint32_t hash; /* value derived from name, for quicker searching */ + cell addr; /* address or offset (or value for constant, index for native function) */ + cell codeaddr; /* address (in the code segment) where the symbol declaration starts */ + char vclass; /* sLOCAL if "addr" refers to a local symbol */ + char ident; /* see below for possible values */ + short usage; /* see below for possible values */ + char flags; /* see below for possible values */ + int compound; /* compound level (braces nesting level) */ + int tag; /* tagname id */ + union { + int declared; /* label: how many local variables are declared */ + struct { + int index; /* array & enum: tag of array indices or the enum item */ + int field; /* enumeration fields, where a size is attached to the field */ + } tags; /* extra tags */ + constvalue *lib; /* native function: library it is part of */ + long stacksize; /* normal/public function: stack requirements */ + } x; /* 'x' for 'extra' */ + union { + arginfo *arglist; /* types of all parameters for functions */ + constvalue *enumlist;/* list of names for the "root" of an enumeration */ + struct { + cell length; /* arrays: length (size) */ + short level; /* number of dimensions below this level */ + } array; + } dim; /* for 'dimension', both functions and arrays */ + constvalue *states; /* list of state function/state variable ids + addresses */ + int fnumber; /* static global variables: file number in which the declaration is visible */ + int lnumber; /* line number (in the current source file) for the declaration */ + struct s_symbol **refer; /* referrer list, functions that "use" this symbol */ + int numrefers; /* number of entries in the referrer list */ + char *documentation; /* optional documentation string */ +} symbol; + + +/* Possible entries for "ident". These are used in the "symbol", "value" + * and arginfo structures. Not every constant is valid for every use. + * In an argument list, the list is terminated with a "zero" ident; labels + * cannot be passed as function arguments, so the value 0 is overloaded. + */ +#define iLABEL 0 +#define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ +#define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ +#define iARRAY 3 +#define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ +#define iARRAYCELL 5 /* array element, cell that must be fetched indirectly */ +#define iARRAYCHAR 6 /* array element, character from cell from array */ +#define iEXPRESSION 7 /* expression result, has no address (rvalue) */ +#define iCONSTEXPR 8 /* constant expression (or constant symbol) */ +#define iFUNCTN 9 +#define iREFFUNC 10 +#define iVARARGS 11 /* function specified ... as argument(s) */ + +/* Possible entries for "usage" + * + * This byte is used as a serie of bits, the syntax is different for + * functions and other symbols: + * + * VARIABLE + * bits: 0 (uDEFINE) the variable is defined in the source file + * 1 (uREAD) the variable is "read" (accessed) in the source file + * 2 (uWRITTEN) the variable is altered (assigned a value) + * 3 (uCONST) the variable is constant (may not be assigned to) + * 4 (uPUBLIC) the variable is public + * 6 (uSTOCK) the variable is discardable (without warning) + * + * FUNCTION + * bits: 0 (uDEFINE) the function is defined ("implemented") in the source file + * 1 (uREAD) the function is invoked in the source file + * 2 (uRETVALUE) the function returns a value (or should return a value) + * 3 (uPROTOTYPED) the function was prototyped (implicitly via a definition or explicitly) + * 4 (uPUBLIC) the function is public + * 5 (uNATIVE) the function is native + * 6 (uSTOCK) the function is discardable (without warning) + * 7 (uMISSING) the function is not implemented in this source file + * 8 (uFORWARD) the function is explicitly forwardly declared + * + * CONSTANT + * bits: 0 (uDEFINE) the symbol is defined in the source file + * 1 (uREAD) the constant is "read" (accessed) in the source file + * 2 (uWRITTEN) redundant, but may be set for constants passed by reference + * 3 (uPREDEF) the constant is pre-defined and should be kept between passes + * 5 (uENUMROOT) the constant is the "root" of an enumeration + * 6 (uENUMFIELD) the constant is a field in a named enumeration + */ +#define uDEFINE 0x001 +#define uREAD 0x002 +#define uWRITTEN 0x004 +#define uRETVALUE 0x004 /* function returns (or should return) a value */ +#define uCONST 0x008 +#define uPROTOTYPED 0x008 +#define uPREDEF 0x008 /* constant is pre-defined */ +#define uPUBLIC 0x010 +#define uNATIVE 0x020 +#define uENUMROOT 0x020 +#define uSTOCK 0x040 +#define uENUMFIELD 0x040 +#define uMISSING 0x080 +#define uFORWARD 0x100 +/* uRETNONE is not stored in the "usage" field of a symbol. It is + * used during parsing a function, to detect a mix of "return;" and + * "return value;" in a few special cases. + */ +#define uRETNONE 0x10 + +#define flgDEPRICATED 0x01 /* symbol is depricated (avoid use) */ + +#define uTAGOF 0x40 /* set in the "hasdefault" field of the arginfo struct */ +#define uSIZEOF 0x80 /* set in the "hasdefault" field of the arginfo struct */ + +#define uMAINFUNC "main" +#define uENTRYFUNC "entry" + +#define sGLOBAL 0 /* global variable/constant class (no states) */ +#define sLOCAL 1 /* local variable/constant */ +#define sSTATIC 2 /* global life, local scope */ + +#define sSTATEVAR 3 /* criterion to find variables (sSTATEVAR implies a global variable) */ + +typedef struct s_value { + symbol *sym; /* symbol in symbol table, NULL for (constant) expression */ + cell constval; /* value of the constant expression (if ident==iCONSTEXPR) + * also used for the size of a literal array */ + int tag; /* tag (of the expression) */ + int cmptag; /* for searching symbols: choose the one with the matching tag */ + char ident; /* iCONSTEXPR, iVARIABLE, iARRAY, iARRAYCELL, + * iEXPRESSION or iREFERENCE */ + char boolresult; /* boolean result for relational operators */ + cell *arrayidx; /* last used array indices, for checking self assignment */ +} value; + +/* "while" statement queue (also used for "for" and "do - while" loops) */ +enum { + wqBRK, /* used to restore stack for "break" */ + wqCONT, /* used to restore stack for "continue" */ + wqLOOP, /* loop start label number */ + wqEXIT, /* loop exit label number (jump if false) */ + /* --- */ + wqSIZE /* "while queue" size */ +}; +#define wqTABSZ (24*wqSIZE) /* 24 nested loop statements */ + +enum { + statIDLE, /* not compiling yet */ + statFIRST, /* first pass */ + statWRITE, /* writing output */ + statSKIP, /* skipping output */ +}; + +typedef struct s_stringlist { + struct s_stringlist *next; + char *line; +} stringlist; + +typedef struct s_stringpair { + struct s_stringpair *next; + char *first; + char *second; + int matchlength; +} stringpair; + +/* macros for code generation */ +#define opcodes(n) ((n)*sizeof(cell)) /* opcode size */ +#define opargs(n) ((n)*sizeof(cell)) /* size of typical argument */ + +/* Tokens recognized by lex() + * Some of these constants are assigned as well to the variable "lastst" + */ +#define tFIRST 256 /* value of first multi-character operator */ +#define tMIDDLE 280 /* value of last multi-character operator */ +#define tLAST 326 /* value of last multi-character match-able token */ +/* multi-character operators */ +#define taMULT 256 /* *= */ +#define taDIV 257 /* /= */ +#define taMOD 258 /* %= */ +#define taADD 259 /* += */ +#define taSUB 260 /* -= */ +#define taSHL 261 /* <<= */ +#define taSHRU 262 /* >>>= */ +#define taSHR 263 /* >>= */ +#define taAND 264 /* &= */ +#define taXOR 265 /* ^= */ +#define taOR 266 /* |= */ +#define tlOR 267 /* || */ +#define tlAND 268 /* && */ +#define tlEQ 269 /* == */ +#define tlNE 270 /* != */ +#define tlLE 271 /* <= */ +#define tlGE 272 /* >= */ +#define tSHL 273 /* << */ +#define tSHRU 274 /* >>> */ +#define tSHR 275 /* >> */ +#define tINC 276 /* ++ */ +#define tDEC 277 /* -- */ +#define tELLIPS 278 /* ... */ +#define tDBLDOT 279 /* .. */ +#define tDBLCOLON 280 /* :: */ +/* reserved words (statements) */ +#define tASSERT 281 +#define tBREAK 282 +#define tCASE 283 +#define tCHAR 284 +#define tCONST 285 +#define tCONTINUE 286 +#define tDEFAULT 287 +#define tDEFINED 288 +#define tDO 289 +#define tELSE 290 +#define tENUM 291 +#define tEXIT 292 +#define tFOR 293 +#define tFORWARD 294 +#define tGOTO 295 +#define tIF 296 +#define tNATIVE 297 +#define tNEW 298 +#define tDECL 299 +#define tOPERATOR 300 +#define tPUBLIC 301 +#define tRETURN 303 +#define tSIZEOF 303 +#define tSLEEP 304 +#define tSTATE 305 +#define tSTATIC 306 +#define tSTOCK 307 +#define tSWITCH 308 +#define tTAGOF 309 +#define tWHILE 310 +/* compiler directives */ +#define tpASSERT 311 /* #assert */ +#define tpDEFINE 312 +#define tpELSE 313 /* #else */ +#define tpELSEIF 314 /* #elseif */ +#define tpEMIT 315 +#define tpENDIF 316 +#define tpENDINPUT 317 +#define tpENDSCRPT 318 +#define tpERROR 319 +#define tpFILE 320 +#define tpIF 321 /* #if */ +#define tINCLUDE 322 +#define tpLINE 323 +#define tpPRAGMA 324 +#define tpTRYINCLUDE 325 +#define tpUNDEF 326 +/* semicolon is a special case, because it can be optional */ +#define tTERM 327 /* semicolon or newline */ +#define tENDEXPR 328 /* forced end of expression */ +/* other recognized tokens */ +#define tNUMBER 329 /* integer number */ +#define tRATIONAL 330 /* rational number */ +#define tSYMBOL 331 +#define tLABEL 332 +#define tSTRING 333 +#define tEXPR 334 /* for assigment to "lastst" only */ + +/* (reversed) evaluation of staging buffer */ +#define sSTARTREORDER 0x01 +#define sENDREORDER 0x02 +#define sEXPRSTART 0x80 /* top bit set, rest is free */ +#define sMAXARGS 127 /* relates to the bit pattern of sEXPRSTART */ + +#define sDOCSEP 0x01 /* to separate documentation comments between functions */ + +/* codes for ffabort() */ +#define xEXIT 1 /* exit code in PRI */ +#define xASSERTION 2 /* abort caused by failing assertion */ +#define xSTACKERROR 3 /* stack/heap overflow */ +#define xBOUNDSERROR 4 /* array index out of bounds */ +#define xMEMACCESS 5 /* data access error */ +#define xINVINSTR 6 /* invalid instruction */ +#define xSTACKUNDERFLOW 7 /* stack underflow */ +#define xHEAPUNDERFLOW 8 /* heap underflow */ +#define xCALLBACKERR 9 /* no, or invalid, callback */ +#define xSLEEP 12 /* sleep, exit code in PRI, tag in ALT */ + +/* Miscellaneous */ +#if !defined TRUE + #define FALSE 0 + #define TRUE 1 +#endif +#define sIN_CSEG 1 /* if parsing CODE */ +#define sIN_DSEG 2 /* if parsing DATA */ +#define sCHKBOUNDS 1 /* bit position in "debug" variable: check bounds */ +#define sSYMBOLIC 2 /* bit position in "debug" variable: symbolic info */ +#define sRESET 0 /* reset error flag */ +#define sFORCESET 1 /* force error flag on */ +#define sEXPRMARK 2 /* mark start of expression */ +#define sEXPRRELEASE 3 /* mark end of expression */ +#define sSETPOS 4 /* set line number for the error */ + +enum { + sOPTIMIZE_NONE, /* no optimization */ + sOPTIMIZE_NOMACRO, /* no macro instructions */ + sOPTIMIZE_DEFAULT, /* full optimization */ + /* ----- */ + sOPTIMIZE_NUMBER +}; + +typedef enum s_regid { + sPRI, /* indicates the primary register */ + sALT, /* indicates the secundary register */ +} regid; + +typedef enum s_optmark { + sEXPR, /* end of expression (for expressions that form a statement) */ + sPARM, /* end of parameter (in a function parameter list) */ + sLDECL, /* start of local declaration (variable) */ +} optmark; + +#define suSLEEP_INSTR 0x01 /* the "sleep" instruction was used */ + +#if INT_MAX<0x8000u + #define PUBLICTAG 0x8000u + #define FIXEDTAG 0x4000u +#else + #define PUBLICTAG 0x80000000Lu + #define FIXEDTAG 0x40000000Lu +#endif +#define TAGMASK (~PUBLICTAG) +#define CELL_MAX (((ucell)1 << (sizeof(cell)*8-1)) - 1) + + +/* interface functions */ +#if defined __cplusplus + extern "C" { +#endif + +/* + * Functions you call from the "driver" program + */ +int pc_compile(int argc, char **argv); +int pc_addconstant(char *name,cell value,int tag); +int pc_addtag(char *name); +int pc_enablewarning(int number,int enable); + +/* + * Functions called from the compiler (to be implemented by you) + */ + +/* general console output */ +int pc_printf(const char *message,...); + +/* error report function */ +int pc_error(int number,char *message,char *filename,int firstline,int lastline,va_list argptr); + +/* input from source file */ +void *pc_opensrc(char *filename); /* reading only */ +void *pc_createsrc(char *filename); +void pc_closesrc(void *handle); /* never delete */ +void pc_resetsrc(void *handle,void *position); /* reset to a position marked earlier */ +char *pc_readsrc(void *handle,unsigned char *target,int maxchars); +int pc_writesrc(void *handle,unsigned char *source); +void *pc_getpossrc(void *handle); /* mark the current position */ +int pc_eofsrc(void *handle); + +/* output to intermediate (.ASM) file */ +void *pc_openasm(char *filename); /* read/write */ +void pc_closeasm(void *handle,int deletefile); +void pc_resetasm(void *handle); +int pc_writeasm(void *handle,char *str); +char *pc_readasm(void *handle,char *target,int maxchars); + +/* output to binary (.AMX) file */ +void *pc_openbin(char *filename); +void pc_closebin(void *handle,int deletefile); +void pc_resetbin(void *handle,long offset); +int pc_writebin(void *handle,void *buffer,int size); +long pc_lengthbin(void *handle); /* return the length of the file */ + +#if defined __cplusplus + } +#endif + + +/* by default, functions and variables used in throughout the compiler + * files are "external" + */ +#if !defined SC_FUNC + #define SC_FUNC +#endif +#if !defined SC_VDECL + #define SC_VDECL extern +#endif +#if !defined SC_VDEFINE + #define SC_VDEFINE +#endif + +/* function prototypes in SC1.C */ +SC_FUNC void set_extension(char *filename,char *extension,int force); +SC_FUNC symbol *fetchfunc(char *name,int tag); +SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag); +SC_FUNC char *funcdisplayname(char *dest,char *funcname); +SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr); +SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index); +SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index); +SC_FUNC void delete_consttable(constvalue *table); +SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag); +SC_FUNC void exporttag(int tag); +SC_FUNC void sc_attachdocumentation(symbol *sym); + +/* function prototypes in SC2.C */ +#define PUSHSTK_P(v) { stkitem s_; s_.pv=(v); pushstk(s_); } +#define PUSHSTK_I(v) { stkitem s_; s_.i=(v); pushstk(s_); } +#define POPSTK_P() (popstk().pv) +#define POPSTK_I() (popstk().i) +SC_FUNC void pushstk(stkitem val); +SC_FUNC stkitem popstk(void); +SC_FUNC void clearstk(void); +SC_FUNC int plungequalifiedfile(char *name); /* explicit path included */ +SC_FUNC int plungefile(char *name,int try_currentpath,int try_includepaths); /* search through "include" paths */ +SC_FUNC void preprocess(void); +SC_FUNC void lexinit(void); +SC_FUNC int lex(cell *lexvalue,char **lexsym); +SC_FUNC void lexpush(void); +SC_FUNC void lexclr(int clreol); +SC_FUNC int matchtoken(int token); +SC_FUNC int tokeninfo(cell *val,char **str); +SC_FUNC int needtoken(int token); +SC_FUNC void litadd(cell value); +SC_FUNC void litinsert(cell value,int pos); +SC_FUNC int alphanum(char c); +SC_FUNC int ishex(char c); +SC_FUNC void delete_symbol(symbol *root,symbol *sym); +SC_FUNC void delete_symbols(symbol *root,int level,int del_labels,int delete_functions); +SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom); +SC_FUNC void markusage(symbol *sym,int usage); +SC_FUNC uint32_t namehash(const char *name); +SC_FUNC symbol *findglb(const char *name,int filter); +SC_FUNC symbol *findloc(const char *name); +SC_FUNC symbol *findconst(const char *name,int *matchtag); +SC_FUNC symbol *finddepend(const symbol *parent); +SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag, + int usage); +SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag, + int dim[],int numdim,int idxtag[]); +SC_FUNC int getlabel(void); +SC_FUNC char *itoh(ucell val); + +/* function prototypes in SC3.C */ +SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, + value *lval,int *resulttag); +SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce); +SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult); +SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state); +SC_FUNC cell array_totalsize(symbol *sym); + +/* function prototypes in SC4.C */ +SC_FUNC void writeleader(symbol *root); +SC_FUNC void writetrailer(void); +SC_FUNC void begcseg(void); +SC_FUNC void begdseg(void); +SC_FUNC void setline(int chkbounds); +SC_FUNC void setfiledirect(char *name); +SC_FUNC void setlinedirect(int line); +SC_FUNC void setlabel(int index); +SC_FUNC void markexpr(optmark type,const char *name,cell offset); +SC_FUNC void startfunc(char *fname); +SC_FUNC void endfunc(void); +SC_FUNC void alignframe(int numbytes); +SC_FUNC void rvalue(value *lval); +SC_FUNC void address(symbol *ptr,regid reg); +SC_FUNC void store(value *lval); +SC_FUNC void loadreg(cell address,regid reg); +SC_FUNC void storereg(cell address,regid reg); +SC_FUNC void memcopy(cell size); +SC_FUNC void copyarray(symbol *sym,cell size); +SC_FUNC void fillarray(symbol *sym,cell size,cell value); +SC_FUNC void ldconst(cell val,regid reg); +SC_FUNC void moveto1(void); +SC_FUNC void pushreg(regid reg); +SC_FUNC void pushval(cell val); +SC_FUNC void popreg(regid reg); +SC_FUNC void swap1(void); +SC_FUNC void ffswitch(int label); +SC_FUNC void ffcase(cell value,char *labelname,int newtable); +SC_FUNC void ffcall(symbol *sym,const char *label,int numargs); +SC_FUNC void ffret(int remparams); +SC_FUNC void ffabort(int reason); +SC_FUNC void ffbounds(cell size); +SC_FUNC void jumplabel(int number); +SC_FUNC void defstorage(void); +SC_FUNC void modstk(int delta); +SC_FUNC void setstk(cell value); +SC_FUNC void modheap(int delta); +SC_FUNC void setheap_pri(void); +SC_FUNC void setheap(cell value); +SC_FUNC void cell2addr(void); +SC_FUNC void cell2addr_alt(void); +SC_FUNC void addr2cell(void); +SC_FUNC void char2addr(void); +SC_FUNC void charalign(void); +SC_FUNC void addconst(cell value); + +/* Code generation functions for arithmetic operators. + * + * Syntax: o[u|s|b]_name + * | | | +--- name of operator + * | | +----- underscore + * | +--------- "u"nsigned operator, "s"igned operator or "b"oth + * +------------- "o"perator + */ +SC_FUNC void os_mult(void); /* multiplication (signed) */ +SC_FUNC void os_div(void); /* division (signed) */ +SC_FUNC void os_mod(void); /* modulus (signed) */ +SC_FUNC void ob_add(void); /* addition */ +SC_FUNC void ob_sub(void); /* subtraction */ +SC_FUNC void ob_sal(void); /* shift left (arithmetic) */ +SC_FUNC void os_sar(void); /* shift right (arithmetic, signed) */ +SC_FUNC void ou_sar(void); /* shift right (logical, unsigned) */ +SC_FUNC void ob_or(void); /* bitwise or */ +SC_FUNC void ob_xor(void); /* bitwise xor */ +SC_FUNC void ob_and(void); /* bitwise and */ +SC_FUNC void ob_eq(void); /* equality */ +SC_FUNC void ob_ne(void); /* inequality */ +SC_FUNC void relop_prefix(void); +SC_FUNC void relop_suffix(void); +SC_FUNC void os_le(void); /* less or equal (signed) */ +SC_FUNC void os_ge(void); /* greater or equal (signed) */ +SC_FUNC void os_lt(void); /* less (signed) */ +SC_FUNC void os_gt(void); /* greater (signed) */ + +SC_FUNC void lneg(void); +SC_FUNC void neg(void); +SC_FUNC void invert(void); +SC_FUNC void nooperation(void); +SC_FUNC void inc(value *lval); +SC_FUNC void dec(value *lval); +SC_FUNC void jmp_ne0(int number); +SC_FUNC void jmp_eq0(int number); +SC_FUNC void outval(cell val,int newline); + +/* function prototypes in SC5.C */ +SC_FUNC int error(int number,...); +SC_FUNC void errorset(int code,int line); + +/* function prototypes in SC6.C */ +SC_FUNC int assemble(FILE *fout,FILE *fin); + +/* function prototypes in SC7.C */ +SC_FUNC void stgbuffer_cleanup(void); +SC_FUNC void stgmark(char mark); +SC_FUNC void stgwrite(const char *st); +SC_FUNC void stgout(int index); +SC_FUNC void stgdel(int index,cell code_index); +SC_FUNC int stgget(int *index,cell *code_index); +SC_FUNC void stgset(int onoff); +SC_FUNC int phopt_init(void); +SC_FUNC int phopt_cleanup(void); + +/* function prototypes in SCLIST.C */ +SC_FUNC char* duplicatestring(const char* sourcestring); +SC_FUNC stringpair *insert_alias(char *name,char *alias); +SC_FUNC stringpair *find_alias(char *name); +SC_FUNC int lookup_alias(char *target,char *name); +SC_FUNC void delete_aliastable(void); +SC_FUNC stringlist *insert_path(char *path); +SC_FUNC char *get_path(int index); +SC_FUNC void delete_pathtable(void); +SC_FUNC stringpair *insert_subst(char *pattern,char *substitution,int prefixlen); +SC_FUNC int get_subst(int index,char **pattern,char **substitution); +SC_FUNC stringpair *find_subst(char *name,int length); +SC_FUNC int delete_subst(char *name,int length); +SC_FUNC void delete_substtable(void); +SC_FUNC stringlist *insert_sourcefile(char *string); +SC_FUNC char *get_sourcefile(int index); +SC_FUNC void delete_sourcefiletable(void); +SC_FUNC stringlist *insert_docstring(char *string); +SC_FUNC char *get_docstring(int index); +SC_FUNC void delete_docstring(int index); +SC_FUNC void delete_docstringtable(void); +SC_FUNC stringlist *insert_autolist(char *string); +SC_FUNC char *get_autolist(int index); +SC_FUNC void delete_autolisttable(void); +SC_FUNC stringlist *insert_dbgfile(const char *filename); +SC_FUNC stringlist *insert_dbgline(int linenr); +SC_FUNC stringlist *insert_dbgsymbol(symbol *sym); +SC_FUNC char *get_dbgstring(int index); +SC_FUNC void delete_dbgstringtable(void); + +/* function prototypes in SCMEMFILE.C */ +#if !defined tMEMFILE + typedef unsigned char MEMFILE; + #define tMEMFILE 1 +#endif +MEMFILE *mfcreate(char *filename); +void mfclose(MEMFILE *mf); +int mfdump(MEMFILE *mf); +long mflength(MEMFILE *mf); +long mfseek(MEMFILE *mf,long offset,int whence); +unsigned int mfwrite(MEMFILE *mf,unsigned char *buffer,unsigned int size); +unsigned int mfread(MEMFILE *mf,unsigned char *buffer,unsigned int size); +char *mfgets(MEMFILE *mf,char *string,unsigned int size); +int mfputs(MEMFILE *mf,char *string); + +/* function prototypes in SCI18N.C */ +#define MAXCODEPAGE 12 +SC_FUNC int cp_path(const char *root,const char *directory); +SC_FUNC int cp_set(const char *name); +SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr); +SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr); +SC_FUNC int scan_utf8(FILE *fp,const char *filename); + +/* function prototypes in SCSTATE.C */ +SC_FUNC constvalue *automaton_add(const char *name); +SC_FUNC constvalue *automaton_find(const char *name); +SC_FUNC constvalue *automaton_findid(int id); +SC_FUNC constvalue *state_add(const char *name,int fsa_id); +SC_FUNC constvalue *state_find(const char *name,int fsa_id); +SC_FUNC constvalue *state_findid(int id); +SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid); +SC_FUNC int state_addlist(int *list,int count,int fsa_id); +SC_FUNC void state_deletetable(void); +SC_FUNC int state_getfsa(int listid); +SC_FUNC int state_count(int listid); +SC_FUNC int state_inlist(int listid,int state); +SC_FUNC int state_listitem(int listid,int index); +SC_FUNC void state_conflict(symbol *root); +SC_FUNC int state_conflict_id(int listid1,int listid2); + +/* external variables (defined in scvars.c) */ +#if !defined SC_SKIP_VDECL +SC_VDECL symbol loctab; /* local symbol table */ +SC_VDECL symbol glbtab; /* global symbol table */ +SC_VDECL cell *litq; /* the literal queue */ +SC_VDECL unsigned char pline[]; /* the line read from the input file */ +SC_VDECL const unsigned char *lptr;/* points to the current position in "pline" */ +SC_VDECL constvalue tagname_tab;/* tagname table */ +SC_VDECL constvalue libname_tab;/* library table (#pragma library "..." syntax) */ +SC_VDECL constvalue *curlibrary;/* current library */ +SC_VDECL int pc_addlibtable; /* is the library table added to the AMX file? */ +SC_VDECL symbol *curfunc; /* pointer to current function */ +SC_VDECL char *inpfname; /* name of the file currently read from */ +SC_VDECL char outfname[]; /* intermediate (assembler) file name */ +SC_VDECL char binfname[]; /* binary file name */ +SC_VDECL char errfname[]; /* error file name */ +SC_VDECL char sc_ctrlchar; /* the control character (or escape character) */ +SC_VDECL char sc_ctrlchar_org;/* the default control character */ +SC_VDECL int litidx; /* index to literal table */ +SC_VDECL int litmax; /* current size of the literal table */ +SC_VDECL int stgidx; /* index to the staging buffer */ +SC_VDECL int sc_labnum; /* number of (internal) labels */ +SC_VDECL int staging; /* true if staging output */ +SC_VDECL cell declared; /* number of local cells declared */ +SC_VDECL cell glb_declared; /* number of global cells declared */ +SC_VDECL cell code_idx; /* number of bytes with generated code */ +SC_VDECL int ntv_funcid; /* incremental number of native function */ +SC_VDECL int errnum; /* number of errors */ +SC_VDECL int warnnum; /* number of warnings */ +SC_VDECL int sc_debug; /* debug/optimization options (bit field) */ +SC_VDECL int sc_packstr; /* strings are packed by default? */ +SC_VDECL int sc_asmfile; /* create .ASM file? */ +SC_VDECL int sc_listing; /* create .LST file? */ +SC_VDECL int sc_compress; /* compress bytecode? */ +SC_VDECL int sc_needsemicolon;/* semicolon required to terminate expressions? */ +SC_VDECL int sc_dataalign; /* data alignment value */ +SC_VDECL int sc_alignnext; /* must frame of the next function be aligned? */ +SC_VDECL int pc_docexpr; /* must expression be attached to documentation comment? */ +SC_VDECL int curseg; /* 1 if currently parsing CODE, 2 if parsing DATA */ +SC_VDECL cell pc_stksize; /* stack size */ +SC_VDECL cell pc_amxlimit; /* abstract machine size limit (code + data, or only code) */ +SC_VDECL cell pc_amxram; /* abstract machine data size limit */ +SC_VDECL int freading; /* is there an input file ready for reading? */ +SC_VDECL int fline; /* the line number in the current file */ +SC_VDECL short fnumber; /* number of files in the file table (debugging) */ +SC_VDECL short fcurrent; /* current file being processed (debugging) */ +SC_VDECL short sc_intest; /* true if inside a test */ +SC_VDECL int sideeffect; /* true if an expression causes a side-effect */ +SC_VDECL int stmtindent; /* current indent of the statement */ +SC_VDECL int indent_nowarn; /* skip warning "217 loose indentation" */ +SC_VDECL int sc_tabsize; /* number of spaces that a TAB represents */ +SC_VDECL short sc_allowtags; /* allow/detect tagnames in lex() */ +SC_VDECL int sc_status; /* read/write status */ +SC_VDECL int sc_rationaltag; /* tag for rational numbers */ +SC_VDECL int rational_digits; /* number of fractional digits */ +SC_VDECL int sc_allowproccall;/* allow/detect tagnames in lex() */ +SC_VDECL short sc_is_utf8; /* is this source file in UTF-8 encoding */ +SC_VDECL char *pc_depricate; /* if non-NULL, mark next declaration as depricated */ +SC_VDECL int sc_curstates; /* ID of the current state list */ +SC_VDECL int pc_optimize; /* (peephole) optimization level */ +SC_VDECL int pc_memflags; /* special flags for the stack/heap usage */ + +SC_VDECL constvalue sc_automaton_tab; /* automaton table */ +SC_VDECL constvalue sc_state_tab; /* state table */ + +SC_VDECL FILE *inpf; /* file read from (source or include) */ +SC_VDECL FILE *inpf_org; /* main source file */ +SC_VDECL FILE *outf; /* file written to */ + +SC_VDECL jmp_buf errbuf; /* target of longjmp() on a fatal error */ + +#if !defined SC_LIGHT + SC_VDECL int sc_makereport; /* generate a cross-reference report */ +#endif + +#endif /* SC_SKIP_VDECL */ + +#endif /* SC_H_INCLUDED */ diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c new file mode 100644 index 00000000..64f44b92 --- /dev/null +++ b/compiler-init/sc1.c @@ -0,0 +1,5605 @@ +/* Pawn compiler + * + * Function and variable definition and declaration, statement parser. + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc1.c 3591 2006-06-25 14:30:07Z thiadmer $ + */ +#include +#include +#include +#include +#include +#include +#include + +#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ + #include + #include +#endif + +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include + #include /* from BinReloc, see www.autopackage.org */ +#endif + +#if defined FORTIFY + #include +#endif + +#if defined __BORLANDC__ || defined __WATCOMC__ + #include + static unsigned total_drives; /* dummy variable */ + #define dos_setdrive(i) _dos_setdrive(i,&total_drives) +#elif defined _MSC_VER && defined _WIN32 + #include /* for _chdrive() */ + #define dos_setdrive(i) _chdrive(i) +#endif +#if defined __BORLANDC__ + #include /* for chdir() */ +#elif defined __WATCOMC__ + #include /* for chdir() */ +#endif +#if defined __WIN32__ || defined _WIN32 || defined _Windows + #include +#endif + +#include "lstring.h" +#include "sc.h" +#include "svnrev.h" +#define VERSION_STR "3.2." SVN_REVSTR +#define VERSION_INT 0x0302 + +static void resetglobals(void); +static void initglobals(void); +static char *get_extension(char *filename); +static void setopt(int argc,char **argv,char *oname,char *ename,char *pname, + char *rname,char *codepage); +static void setconfig(char *root); +static void setcaption(void); +static void about(void); +static void setconstants(void); +static void parse(void); +static void dumplits(void); +static void dumpzero(int count); +static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst); +static void declglb(char *firstname,int firsttag,int fpublic,int fstatic, + int stock,int fconst); +static int declloc(int fstatic); +static void decl_const(int table); +static void decl_enum(int table); +static cell needsub(int *tag,constvalue **enumroot); +static void initials(int ident,int tag,cell *size,int dim[],int numdim, + constvalue *enumroot); +static cell initarray(int ident,int tag,int dim[],int numdim,int cur, + int startlit,int counteddim[],constvalue *lastdim, + constvalue *enumroot,int *errorfound); +static cell initvector(int ident,int tag,cell size,int fillzero, + constvalue *enumroot,int *errorfound); +static cell init(int ident,int *tag,int *errorfound); +static int getstates(const char *funcname); +static void attachstatelist(symbol *sym, int state_id); +static void funcstub(int fnative); +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock); +static int declargs(symbol *sym,int chkshadow); +static void doarg(char *name,int ident,int offset,int tags[],int numtags, + int fpublic,int fconst,int chkshadow,arginfo *arg); +static void make_report(symbol *root,FILE *log,char *sourcefile); +static void reduce_referrers(symbol *root); +static long max_stacksize(symbol *root,int *recursion); +static int testsymbols(symbol *root,int level,int testlabs,int testconst); +static void destructsymbols(symbol *root,int level); +static constvalue *find_constval_byval(constvalue *table,cell val); +static void statement(int *lastindent,int allow_decl); +static void compound(int stmt_sameline); +static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr, + int *tag,symbol **symptr,int chkfuncresult); +static void doassert(void); +static void doexit(void); +static void test(int label,int parens,int invert); +static void doif(void); +static void dowhile(void); +static void dodo(void); +static void dofor(void); +static void doswitch(void); +static void dogoto(void); +static void dolabel(void); +static symbol *fetchlab(char *name); +static void doreturn(void); +static void dobreak(void); +static void docont(void); +static void dosleep(void); +static void dostate(void); +static void addwhile(int *ptr); +static void delwhile(void); +static int *readwhile(void); + +static int norun = 0; /* the compiler never ran */ +static int lastst = 0; /* last executed statement type */ +static int nestlevel = 0; /* number of active (open) compound statements */ +static int rettype = 0; /* the type that a "return" expression should have */ +static int skipinput = 0; /* number of lines to skip from the first input file */ +static int optproccall = TRUE; /* support "procedure call" */ +static int verbosity = 1; /* verbosity level, 0=quiet, 1=normal, 2=verbose */ +static int sc_reparse = 0; /* needs 3th parse because of changed prototypes? */ +static int sc_parsenum = 0; /* number of the extra parses */ +static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */ +static int *wqptr; /* pointer to next entry */ +#if !defined SC_LIGHT + static char sc_rootpath[_MAX_PATH]; + static char *sc_documentation=NULL;/* main documentation */ +#endif +#if defined __WIN32__ || defined _WIN32 || defined _Windows + static HWND hwndFinish = 0; +#endif + +/* "main" of the compiler + */ +#if defined __cplusplus + extern "C" +#endif +int pc_compile(int argc, char *argv[]) +{ + int entry,i,jmpcode; + int retcode; + char incfname[_MAX_PATH]; + char reportname[_MAX_PATH]; + char codepage[MAXCODEPAGE+1]; + FILE *binf; + void *inpfmark; + int lcl_packstr,lcl_needsemicolon,lcl_tabsize; + #if !defined SC_LIGHT + int hdrsize=0; + #endif + char *ptr; + + /* set global variables to their initial value */ + binf=NULL; + initglobals(); + errorset(sRESET,0); + errorset(sEXPRRELEASE,0); + lexinit(); + + /* make sure that we clean up on a fatal error; do this before the first + * call to error(). */ + if ((jmpcode=setjmp(errbuf))!=0) + goto cleanup; + + /* allocate memory for fixed tables */ + inpfname=(char*)malloc(_MAX_PATH); + if (inpfname==NULL) + error(103); /* insufficient memory */ + litq=(cell*)malloc(litmax*sizeof(cell)); + if (litq==NULL) + error(103); /* insufficient memory */ + if (!phopt_init()) + error(103); /* insufficient memory */ + + setopt(argc,argv,outfname,errfname,incfname,reportname,codepage); + strcpy(binfname,outfname); + ptr=get_extension(binfname); + if (ptr!=NULL && stricmp(ptr,".asm")==0) + set_extension(binfname,".smx",TRUE); + else + set_extension(binfname,".smx",FALSE); + /* set output names that depend on the input name */ + if (sc_listing) + set_extension(outfname,".lst",TRUE); + else + set_extension(outfname,".asm",TRUE); + if (strlen(errfname)!=0) + remove(errfname); /* delete file on startup */ + else if (verbosity>0) + setcaption(); + setconfig(argv[0]); /* the path to the include and codepage files */ + sc_ctrlchar_org=sc_ctrlchar; + lcl_packstr=sc_packstr; + lcl_needsemicolon=sc_needsemicolon; + lcl_tabsize=sc_tabsize; + #if !defined NO_CODEPAGE + if (!cp_set(codepage)) /* set codepage */ + error(108); /* codepage mapping file not found */ + #endif + /* optionally create a temporary input file that is a collection of all + * input files + */ + assert(get_sourcefile(0)!=NULL); /* there must be at least one source file */ + if (get_sourcefile(1)!=NULL) { + /* there are at least two or more source files */ + char *tname,*sname; + FILE *ftmp,*fsrc; + int fidx; + #if defined __WIN32__ || defined _WIN32 + tname=_tempnam(NULL,"pawn"); + #elif defined __MSDOS__ || defined _Windows + tname=tempnam(NULL,"pawn"); + #elif defined(MACOS) && !defined(__MACH__) + /* tempnam is not supported for the Macintosh CFM build. */ + error(104,get_sourcefile(1)); + tname=NULL; + sname=NULL; + #else + tname=tempnam(NULL,"pawn"); + #endif + ftmp=(FILE*)pc_createsrc(tname); + for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) { + unsigned char tstring[128]; + fsrc=(FILE*)pc_opensrc(sname); + if (fsrc==NULL) { + strcpy(inpfname,sname); /* avoid invalid filename */ + error(100,sname); + } /* if */ + pc_writesrc(ftmp,(unsigned char*)"#file "); + pc_writesrc(ftmp,(unsigned char*)sname); + pc_writesrc(ftmp,(unsigned char*)"\n"); + while (!pc_eofsrc(fsrc)) { + pc_readsrc(fsrc,tstring,sizeof tstring); + pc_writesrc(ftmp,tstring); + } /* while */ + pc_closesrc(fsrc); + } /* for */ + pc_closesrc(ftmp); + strcpy(inpfname,tname); + free(tname); + } else { + strcpy(inpfname,get_sourcefile(0)); + } /* if */ + inpf_org=(FILE*)pc_opensrc(inpfname); + if (inpf_org==NULL) + error(100,inpfname); + freading=TRUE; + outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */ + if (outf==NULL) + error(101,outfname); + /* immediately open the binary file, for other programs to check */ + if (sc_asmfile || sc_listing) { + binf=NULL; + } else { + binf=(FILE*)pc_openbin(binfname); + if (binf==NULL) + error(101,binfname); + } /* if */ + setconstants(); /* set predefined constants and tagnames */ + for (i=0; i0) { + if (strcmp(incfname,sDEF_PREFIX)==0) { + plungefile(incfname,FALSE,TRUE); /* parse "default.inc" */ + } else { + if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */ + error(100,incfname); /* cannot read from ... (fatal error) */ + } /* if */ + } /* if */ + preprocess(); /* fetch first line */ + parse(); /* process all input */ + sc_parsenum++; + } while (sc_reparse); + + /* second (or third) pass */ + sc_status=statWRITE; /* set, to enable warnings */ + state_conflict(&glbtab); + + /* write a report, if requested */ + #if !defined SC_LIGHT + if (sc_makereport) { + FILE *frep=stdout; + if (strlen(reportname)>0) + frep=fopen(reportname,"wb"); /* avoid translation of \n to \r\n in DOS/Windows */ + if (frep!=NULL) { + make_report(&glbtab,frep,get_sourcefile(0)); + if (strlen(reportname)>0) + fclose(frep); + } /* if */ + if (sc_documentation!=NULL) { + free(sc_documentation); + sc_documentation=NULL; + } /* if */ + } /* if */ + #endif + if (sc_listing) + goto cleanup; + + /* ??? for re-parsing the listing file instead of the original source + * file (and doing preprocessing twice): + * - close input file, close listing file + * - re-open listing file for reading (inpf) + * - open assembler file (outf) + */ + + /* reset "defined" flag of all functions and global variables */ + reduce_referrers(&glbtab); + delete_symbols(&glbtab,0,TRUE,FALSE); + #if !defined NO_DEFINE + delete_substtable(); + #endif + resetglobals(); + sc_ctrlchar=sc_ctrlchar_org; + sc_packstr=lcl_packstr; + sc_needsemicolon=lcl_needsemicolon; + sc_tabsize=lcl_tabsize; + errorset(sRESET,0); + /* reset the source file */ + inpf=inpf_org; + freading=TRUE; + pc_resetsrc(inpf,inpfmark); /* reset file position */ + fline=skipinput; /* reset line number */ + lexinit(); /* clear internal flags of lex() */ + sc_status=statWRITE; /* allow to write --this variable was reset by resetglobals() */ + writeleader(&glbtab); + insert_dbgfile(inpfname); + if (strlen(incfname)>0) { + if (strcmp(incfname,sDEF_PREFIX)==0) + plungefile(incfname,FALSE,TRUE); /* parse "default.inc" (again) */ + else + plungequalifiedfile(incfname); /* parse implicit include file (again) */ + } /* if */ + preprocess(); /* fetch first line */ + parse(); /* process all input */ + /* inpf is already closed when readline() attempts to pop of a file */ + writetrailer(); /* write remaining stuff */ + + entry=testsymbols(&glbtab,0,TRUE,FALSE); /* test for unused or undefined + * functions and variables */ + if (!entry) + error(13); /* no entry point (no public functions) */ + +cleanup: + if (inpf!=NULL) /* main source file is not closed, do it now */ + pc_closesrc(inpf); + /* write the binary file (the file is already open) */ + if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) { + assert(binf!=NULL); + pc_resetasm(outf); /* flush and loop back, for reading */ + #if !defined SC_LIGHT + hdrsize= + #endif + assemble(binf,outf); /* assembler file is now input */ + } /* if */ + if (outf!=NULL) { + pc_closeasm(outf,!(sc_asmfile || sc_listing)); + outf=NULL; + } /* if */ + if (binf!=NULL) { + pc_closebin(binf,errnum!=0); + binf=NULL; + } /* if */ + + #if !defined SC_LIGHT + if (errnum==0 && strlen(errfname)==0) { + int recursion; + long stacksize=max_stacksize(&glbtab,&recursion); + int flag_exceed=0; + if (pc_amxlimit>0) { + long totalsize=hdrsize+code_idx; + if (pc_amxram==0) + totalsize+=(glb_declared+pc_stksize)*sizeof(cell); + if (totalsize>=pc_amxlimit) + flag_exceed=1; + } /* if */ + if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram) + flag_exceed=1; + if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) { + pc_printf("Header size: %8ld bytes\n", (long)hdrsize); + pc_printf("Code size: %8ld bytes\n", (long)code_idx); + pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell)); + pc_printf("Stack/heap size: %8ld bytes; ", (long)pc_stksize*sizeof(cell)); + pc_printf("estimated max. usage"); + if (recursion) + pc_printf(" is unknown, due to recursion\n"); + else if ((pc_memflags & suSLEEP_INSTR)!=0) + pc_printf(" is unknown, due to the \"sleep\" instruction\n"); + else + pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell)); + pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); + } /* if */ + if (flag_exceed) + error(106,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */ + } /* if */ + #endif + + if (inpfname!=NULL) { + if (get_sourcefile(1)!=NULL) + remove(inpfname); /* the "input file" was in fact a temporary file */ + free(inpfname); + } /* if */ + if (litq!=NULL) + free(litq); + phopt_cleanup(); + stgbuffer_cleanup(); + clearstk(); + assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols + * should already have been deleted */ + delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet + * done (i.e. on a fatal error) */ + delete_symbols(&glbtab,0,TRUE,TRUE); + delete_consttable(&tagname_tab); + delete_consttable(&libname_tab); + delete_consttable(&sc_automaton_tab); + delete_consttable(&sc_state_tab); + state_deletetable(); + delete_aliastable(); + delete_pathtable(); + delete_sourcefiletable(); + delete_dbgstringtable(); + #if !defined NO_DEFINE + delete_substtable(); + #endif + #if !defined SC_LIGHT + delete_docstringtable(); + if (sc_documentation!=NULL) + free(sc_documentation); + #endif + delete_autolisttable(); + if (errnum!=0) { + if (strlen(errfname)==0) + pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : ""); + retcode=1; + } else if (warnnum!=0){ + if (strlen(errfname)==0) + pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : ""); + retcode=0; /* use "0", so that MAKE and similar tools continue */ + } else { + retcode=jmpcode; + if (retcode==0 && verbosity>=2) + pc_printf("\nDone.\n"); + } /* if */ + #if defined __WIN32__ || defined _WIN32 || defined _Windows + if (IsWindow(hwndFinish)) + PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L); + #endif + #if defined FORTIFY + Fortify_ListAllMemory(); + #endif + return retcode; +} + +#if defined __cplusplus + extern "C" +#endif +int pc_addconstant(char *name,cell value,int tag) +{ + errorset(sFORCESET,0); /* make sure error engine is silenced */ + sc_status=statIDLE; + add_constant(name,value,sGLOBAL,tag); + return 1; +} + +#if defined __cplusplus + extern "C" +#endif +int pc_addtag(char *name) +{ + cell val; + constvalue *ptr; + int last,tag; + + if (name==NULL) { + /* no tagname was given, check for one */ + if (lex(&val,&name)!=tLABEL) { + lexpush(); + return 0; /* untagged */ + } /* if */ + } /* if */ + + assert(strchr(name,':')==NULL); /* colon should already have been stripped */ + last=0; + ptr=tagname_tab.next; + while (ptr!=NULL) { + tag=(int)(ptr->value & TAGMASK); + if (strcmp(name,ptr->name)==0) + return tag; /* tagname is known, return its sequence number */ + tag &= (int)~FIXEDTAG; + if (tag>last) + last=tag; + ptr=ptr->next; + } /* while */ + + /* tagname currently unknown, add it */ + tag=last+1; /* guaranteed not to exist already */ + if (isupper(*name)) + tag |= (int)FIXEDTAG; + append_constval(&tagname_tab,name,(cell)tag,0); + return tag; +} + +static void resetglobals(void) +{ + /* reset the subset of global variables that is modified by the first pass */ + curfunc=NULL; /* pointer to current function */ + lastst=0; /* last executed statement type */ + nestlevel=0; /* number of active (open) compound statements */ + rettype=0; /* the type that a "return" expression should have */ + litidx=0; /* index to literal table */ + stgidx=0; /* index to the staging buffer */ + sc_labnum=0; /* top value of (internal) labels */ + staging=0; /* true if staging output */ + declared=0; /* number of local cells declared */ + glb_declared=0; /* number of global cells declared */ + code_idx=0; /* number of bytes with generated code */ + ntv_funcid=0; /* incremental number of native function */ + curseg=0; /* 1 if currently parsing CODE, 2 if parsing DATA */ + freading=FALSE; /* no input file ready yet */ + fline=0; /* the line number in the current file */ + fnumber=0; /* the file number in the file table (debugging) */ + fcurrent=0; /* current file being processed (debugging) */ + sc_intest=FALSE; /* true if inside a test */ + sideeffect=0; /* true if an expression causes a side-effect */ + stmtindent=0; /* current indent of the statement */ + indent_nowarn=FALSE; /* do not skip warning "217 loose indentation" */ + sc_allowtags=TRUE; /* allow/detect tagnames */ + sc_status=statIDLE; + sc_allowproccall=FALSE; + pc_addlibtable=TRUE; /* by default, add a "library table" to the output file */ + sc_alignnext=FALSE; + pc_docexpr=FALSE; + pc_depricate=NULL; + sc_curstates=0; + pc_memflags=0; +} + +static void initglobals(void) +{ + resetglobals(); + + sc_asmfile=FALSE; /* do not create .ASM file */ + sc_listing=FALSE; /* do not create .LST file */ + skipinput=0; /* number of lines to skip from the first input file */ + sc_ctrlchar=CTRL_CHAR;/* the escape character */ + litmax=sDEF_LITMAX; /* current size of the literal table */ + errnum=0; /* number of errors */ + warnnum=0; /* number of warnings */ + optproccall=FALSE; /* sourcemod: do not support "procedure call" */ + verbosity=1; /* verbosity level, no copyright banner */ + sc_debug=sSYMBOLIC; /* sourcemod: full debug stuff */ + pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */ + sc_packstr=FALSE; /* strings are unpacked by default */ + #if AMX_COMPACTMARGIN > 2 + sc_compress=TRUE; /* compress output bytecodes */ + #else + sc_compress=FALSE; + #endif + sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */ + sc_dataalign=sizeof(cell); + pc_stksize=sDEF_AMXSTACK;/* default stack size */ + pc_amxlimit=0; /* no limit on size of the abstract machine */ + pc_amxram=0; /* no limit on data size of the abstract machine */ + sc_tabsize=8; /* assume a TAB is 8 spaces */ + sc_rationaltag=0; /* assume no support for rational numbers */ + rational_digits=0; /* number of fractional digits */ + + outfname[0]='\0'; /* output file name */ + errfname[0]='\0'; /* error file name */ + inpf=NULL; /* file read from */ + inpfname=NULL; /* pointer to name of the file currently read from */ + outf=NULL; /* file written to */ + litq=NULL; /* the literal queue */ + glbtab.next=NULL; /* clear global variables/constants table */ + loctab.next=NULL; /* " local " / " " */ + tagname_tab.next=NULL;/* tagname table */ + libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ + + pline[0]='\0'; /* the line read from the input file */ + lptr=NULL; /* points to the current position in "pline" */ + curlibrary=NULL; /* current library */ + inpf_org=NULL; /* main source file */ + + wqptr=wq; /* initialize while queue pointer */ + +#if !defined SC_LIGHT + sc_documentation=NULL; + sc_makereport=FALSE; /* do not generate a cross-reference report */ +#endif +} + +static char *get_extension(char *filename) +{ + char *ptr; + + assert(filename!=NULL); + ptr=strrchr(filename,'.'); + if (ptr!=NULL) { + /* ignore extension on a directory or at the start of the filename */ + if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR) + ptr=NULL; + } /* if */ + return ptr; +} + +/* set_extension + * Set the default extension, or force an extension. To erase the + * extension of a filename, set "extension" to an empty string. + */ +SC_FUNC void set_extension(char *filename,char *extension,int force) +{ + char *ptr; + + assert(extension!=NULL && (*extension=='\0' || *extension=='.')); + assert(filename!=NULL); + ptr=get_extension(filename); + if (force && ptr!=NULL) + *ptr='\0'; /* set zero terminator at the position of the period */ + if (force || ptr==NULL) + strcat(filename,extension); +} + +static const char *option_value(const char *optptr) +{ + return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1; +} + +static int toggle_option(const char *optptr, int option) +{ + switch (*option_value(optptr)) { + case '\0': + option=!option; + break; + case '-': + option=FALSE; + break; + case '+': + option=TRUE; + break; + default: + about(); + } /* switch */ + return option; +} + +/* Parsing command line options is indirectly recursive: parseoptions() + * calls parserespf() to handle options in a a response file and + * parserespf() calls parseoptions() at its turn after having created + * an "option list" from the contents of the file. + */ +static void parserespf(char *filename,char *oname,char *ename,char *pname, + char *rname, char *codepage); + +static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname, + char *rname, char *codepage) +{ + char str[_MAX_PATH],*name; + const char *ptr; + int arg,i,isoption; + + for (arg=1; arg1) + verbosity=1; + break; + case 'C': + #if AMX_COMPACTMARGIN > 2 + sc_compress=toggle_option(ptr,sc_compress); + #else + about(); + #endif + break; + case 'c': + strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */ + break; +#if defined dos_setdrive + case 'D': /* set active directory */ + ptr=option_value(ptr); + if (ptr[1]==':') + dos_setdrive(toupper(*ptr)-'A'+1); /* set active drive */ + chdir(ptr); + break; +#endif +#if 0 /* not allowed to change for SourceMod */ + case 'd': + switch (*option_value(ptr)) { + case '0': + sc_debug=0; + break; + case '1': + sc_debug=sCHKBOUNDS; /* assertions and bounds checking */ + break; + case '2': + sc_debug=sCHKBOUNDS | sSYMBOLIC; /* also symbolic info */ + break; + case '3': + sc_debug=sCHKBOUNDS | sSYMBOLIC; + pc_optimize=sOPTIMIZE_NONE; + /* also avoid peephole optimization */ + break; + default: + about(); + } /* switch */ + break; +#endif + case 'e': + strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */ + break; +#if defined __WIN32__ || defined _WIN32 || defined _Windows + case 'H': + hwndFinish=(HWND)atoi(option_value(ptr)); + if (!IsWindow(hwndFinish)) + hwndFinish=(HWND)0; + break; +#endif + case 'i': + strlcpy(str,option_value(ptr),sizeof str); /* set name of include directory */ + i=strlen(str); + if (i>0) { + if (str[i-1]!=DIRSEP_CHAR) { + str[i]=DIRSEP_CHAR; + str[i+1]='\0'; + } /* if */ + insert_path(str); + } /* if */ + break; + case 'l': + if (*(ptr+1)!='\0') + about(); + sc_listing=TRUE; /* skip second pass & code generation */ + break; + case 'o': + strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */ + break; + case 'O': + pc_optimize=*option_value(ptr) - '0'; + if (pc_optimize=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO) + about(); + break; + case 'p': + strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */ + break; +#if !defined SC_LIGHT + case 'r': + strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */ + sc_makereport=TRUE; + if (strlen(rname)>0) { + set_extension(rname,".xml",FALSE); + } else if ((name=get_sourcefile(0))!=NULL) { + assert(strlen(rname)==0); + assert(strlen(name)<_MAX_PATH); + if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL) + ptr++; /* strip path */ + else + ptr=name; + assert(strlen(ptr)<_MAX_PATH); + strcpy(rname,ptr); + set_extension(rname,".xml",TRUE); + } /* if */ + break; +#endif + case 'S': + i=atoi(option_value(ptr)); + if (i>32) + pc_stksize=(cell)i; /* stack size has minimum size */ + else + about(); + break; + case 's': + skipinput=atoi(option_value(ptr)); + break; + case 't': + sc_tabsize=atoi(option_value(ptr)); + break; + case 'v': + verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2; + if (sc_asmfile && verbosity>1) + verbosity=1; + break; + case 'w': + i=(int)strtol(option_value(ptr),(char **)&ptr,10); + if (*ptr=='-') + pc_enablewarning(i,0); + else if (*ptr=='+') + pc_enablewarning(i,1); + else if (*ptr=='\0') + pc_enablewarning(i,2); + break; + case 'X': + if (*(ptr+1)=='D') { + i=atoi(option_value(ptr+1)); + if (i>64) + pc_amxram=(cell)i; /* abstract machine data/stack has minimum size */ + else + about(); + } else { + i=atoi(option_value(ptr)); + if (i>64) + pc_amxlimit=(cell)i;/* abstract machine has minimum size */ + else + about(); + } /* if */ + break; + case '\\': /* use \ instead for escape characters */ + sc_ctrlchar='\\'; + break; + case '^': /* use ^ instead for escape characters */ + sc_ctrlchar='^'; + break; + case ';': + sc_needsemicolon=toggle_option(ptr,sc_needsemicolon); + break; +#if 0 /* not allowed to change in SourceMod */ + case '(': + optproccall=!toggle_option(ptr,!optproccall); + break; +#endif + default: /* wrong option */ + about(); + } /* switch */ + } else if (argv[arg][0]=='@') { + #if !defined SC_LIGHT + parserespf(&argv[arg][1],oname,ename,pname,rname,codepage); + #endif + } else if ((ptr=strchr(argv[arg],'='))!=NULL) { + i=(int)(ptr-argv[arg]); + if (i>sNAMEMAX) { + i=sNAMEMAX; + error(200,argv[arg],sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */ + } /* if */ + strlcpy(str,argv[arg],i+1); /* str holds symbol name */ + i=atoi(ptr+1); + add_constant(str,i,sGLOBAL,0); + } else { + strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".psrc" */ + set_extension(str,".psrc",FALSE); + insert_sourcefile(str); + /* The output name is the first input name with a different extension, + * but it is stored in a different directory + */ + if (strlen(oname)==0) { + if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL) + ptr++; /* strip path */ + else + ptr=str; + assert(strlen(ptr)<_MAX_PATH); + strcpy(oname,ptr); + } /* if */ + set_extension(oname,".asm",TRUE); +#if !defined SC_LIGHT + if (sc_makereport && strlen(rname)==0) { + if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL) + ptr++; /* strip path */ + else + ptr=str; + assert(strlen(ptr)<_MAX_PATH); + strcpy(rname,ptr); + set_extension(rname,".xml",TRUE); + } /* if */ +#endif + } /* if */ + } /* for */ +} + +#if !defined SC_LIGHT +static void parserespf(char *filename,char *oname,char *ename,char *pname, + char *rname,char *codepage) +{ +#define MAX_OPTIONS 100 + FILE *fp; + char *string, *ptr, **argv; + int argc; + long size; + + if ((fp=fopen(filename,"r"))==NULL) + error(100,filename); /* error reading input file */ + /* load the complete file into memory */ + fseek(fp,0L,SEEK_END); + size=ftell(fp); + fseek(fp,0L,SEEK_SET); + assert(size [filename...] [options]\n\n"); + pc_printf("Options:\n"); + pc_printf(" -A alignment in bytes of the data segment and the stack\n"); + pc_printf(" -a output assembler code\n"); +#if AMX_COMPACTMARGIN > 2 + pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-'); +#endif + pc_printf(" -c codepage name or number; e.g. 1252 for Windows Latin-1\n"); +#if defined dos_setdrive + pc_printf(" -Dpath active directory path\n"); +#endif +#if 0 /* not used for SourceMod */ + pc_printf(" -d debugging level (default=-d%d)\n",sc_debug); + pc_printf(" 0 no symbolic information, no run-time checks\n"); + pc_printf(" 1 run-time checks, no symbolic information\n"); + pc_printf(" 2 full debug information and dynamic checking\n"); + pc_printf(" 3 same as -d2, but implies -O0\n"); +#endif + pc_printf(" -e set name of error file (quiet compile)\n"); +#if defined __WIN32__ || defined _WIN32 || defined _Windows + pc_printf(" -H window handle to send a notification message on finish\n"); +#endif + pc_printf(" -i path for include files\n"); + pc_printf(" -l create list file (preprocess only)\n"); + pc_printf(" -o set base name of (P-code) output file\n"); + pc_printf(" -O optimization level (default=-O%d)\n",pc_optimize); + pc_printf(" 0 no optimization\n"); +#if 0 /* not used for SourceMod */ + pc_printf(" 1 JIT-compatible optimizations only\n"); +#endif + pc_printf(" 2 full optimizations\n"); + pc_printf(" -p set name of \"prefix\" file\n"); +#if !defined SC_LIGHT + pc_printf(" -r[name] write cross reference report to console or to specified file\n"); +#endif + pc_printf(" -S stack/heap size in cells (default=%d)\n",(int)pc_stksize); + pc_printf(" -s skip lines from the input file\n"); + pc_printf(" -t TAB indent size (in character positions, default=%d)\n",sc_tabsize); + pc_printf(" -v verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity); + pc_printf(" -w disable a specific warning by its number\n"); + pc_printf(" -X abstract machine size limit in bytes\n"); + pc_printf(" -XD abstract machine data/stack size limit in bytes\n"); + pc_printf(" -\\ use '\\' for escape characters\n"); + pc_printf(" -^ use '^' for escape characters\n"); + pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-'); +#if 0 /* not allowed in SourceMod */ + pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+'); +#endif + pc_printf(" sym=val define constant \"sym\" with value \"val\"\n"); + pc_printf(" sym= define constant \"sym\" with value 0\n"); +#if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__ + pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n"); + pc_printf("equivalent.\n"); +#endif + pc_printf("\nOptions with a value may optionally separate the value from the option letter\n"); + pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n"); + pc_printf("and \"-d:0\" are all equivalent.\n"); + } /* if */ + norun = 1; + longjmp(errbuf,3); /* user abort */ +} + +static void setconstants(void) +{ + int debug; + + assert(sc_status==statIDLE); + append_constval(&tagname_tab,"_",0,0);/* "untagged" */ + append_constval(&tagname_tab,"bool",1,0); + + add_constant("true",1,sGLOBAL,1); /* boolean flags */ + add_constant("false",0,sGLOBAL,1); + add_constant("EOS",0,sGLOBAL,0); /* End Of String, or '\0' */ + #if PAWN_CELL_SIZE==16 + add_constant("cellbits",16,sGLOBAL,0); + #if defined _I16_MAX + add_constant("cellmax",_I16_MAX,sGLOBAL,0); + add_constant("cellmin",_I16_MIN,sGLOBAL,0); + #else + add_constant("cellmax",SHRT_MAX,sGLOBAL,0); + add_constant("cellmin",SHRT_MIN,sGLOBAL,0); + #endif + #elif PAWN_CELL_SIZE==32 + add_constant("cellbits",32,sGLOBAL,0); + #if defined _I32_MAX + add_constant("cellmax",_I32_MAX,sGLOBAL,0); + add_constant("cellmin",_I32_MIN,sGLOBAL,0); + #else + add_constant("cellmax",LONG_MAX,sGLOBAL,0); + add_constant("cellmin",LONG_MIN,sGLOBAL,0); + #endif + #elif PAWN_CELL_SIZE==64 + #if !defined _I64_MIN + #define _I64_MIN (-9223372036854775807ULL - 1) + #define _I64_MAX 9223372036854775807ULL + #endif + add_constant("cellbits",64,sGLOBAL,0); + add_constant("cellmax",_I64_MAX,sGLOBAL,0); + add_constant("cellmin",_I64_MIN,sGLOBAL,0); + #else + #error Unsupported cell size + #endif + add_constant("charbits",sCHARBITS,sGLOBAL,0); + add_constant("charmin",0,sGLOBAL,0); + add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0); + add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0); + + add_constant("__Pawn",VERSION_INT,sGLOBAL,0); + + debug=0; + if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC)) + debug=2; + else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS) + debug=1; + add_constant("debug",debug,sGLOBAL,0); + + append_constval(&sc_automaton_tab,"",0,0); /* anonymous automaton */ +} + +static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst) +{ + int tok,err; + cell val; + char *str; + + assert(fconst!=NULL); + assert(fstock!=NULL); + assert(fstatic!=NULL); + assert(fpublic!=NULL); + *fconst=FALSE; + *fstock=FALSE; + *fstatic=FALSE; + *fpublic=FALSE; + switch (initialtok) { + case tCONST: + *fconst=TRUE; + break; + case tSTOCK: + *fstock=TRUE; + break; + case tSTATIC: + *fstatic=TRUE; + break; + case tPUBLIC: + *fpublic=TRUE; + break; + } /* switch */ + + err=0; + do { + tok=lex(&val,&str); /* read in (new) token */ + switch (tok) { + case tCONST: + if (*fconst) + err=42; /* invalid combination of class specifiers */ + *fconst=TRUE; + break; + case tSTOCK: + if (*fstock) + err=42; /* invalid combination of class specifiers */ + *fstock=TRUE; + break; + case tSTATIC: + if (*fstatic) + err=42; /* invalid combination of class specifiers */ + *fstatic=TRUE; + break; + case tPUBLIC: + if (*fpublic) + err=42; /* invalid combination of class specifiers */ + *fpublic=TRUE; + break; + default: + lexpush(); + tok=0; /* force break out of loop */ + } /* switch */ + } while (tok && err==0); + + /* extra checks */ + if (*fstatic && *fpublic) { + err=42; /* invalid combination of class specifiers */ + *fstatic=*fpublic=FALSE; + } /* if */ + + if (err) + error(err); + return err==0; +} + +/* parse - process all input text + * + * At this level, only static declarations and function definitions are legal. + */ +static void parse(void) +{ + int tok,fconst,fstock,fstatic,fpublic; + cell val; + char *str; + + while (freading){ + /* first try whether a declaration possibly is native or public */ + tok=lex(&val,&str); /* read in (new) token */ + switch (tok) { + case 0: + /* ignore zero's */ + break; + case tNEW: + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) + declglb(NULL,0,fpublic,fstatic,fstock,fconst); + break; + case tSTATIC: + /* This can be a static function or a static global variable; we know + * which of the two as soon as we have parsed up to the point where an + * opening paranthesis of a function would be expected. To back out after + * deciding it was a declaration of a static variable after all, we have + * to store the symbol name and tag. + */ + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + assert(!fpublic); + declfuncvar(fpublic,fstatic,fstock,fconst); + } /* if */ + break; + case tCONST: + decl_const(sGLOBAL); + break; + case tENUM: + decl_enum(sGLOBAL); + break; + case tPUBLIC: + /* This can be a public function or a public variable; see the comment + * above (for static functions/variables) for details. + */ + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + assert(!fstatic); + declfuncvar(fpublic,fstatic,fstock,fconst); + } /* if */ + break; + case tSTOCK: + /* This can be a stock function or a stock *global*) variable; see the + * comment above (for static functions/variables) for details. + */ + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + assert(fstock); + declfuncvar(fpublic,fstatic,fstock,fconst); + } /* if */ + break; + case tLABEL: + case tSYMBOL: + case tOPERATOR: + lexpush(); + if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) { + error(10); /* illegal function or declaration */ + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop the literal queue too */ + } /* if */ + break; + case tNATIVE: + funcstub(TRUE); /* create a dummy function */ + break; + case tFORWARD: + funcstub(FALSE); + break; + case '}': + error(54); /* unmatched closing brace */ + break; + case '{': + error(55); /* start of function body without function header */ + break; + default: + if (freading) { + error(10); /* illegal function or declaration */ + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop any literal arrays (strings) */ + } /* if */ + } /* switch */ + } /* while */ +} + +/* dumplits + * + * Dump the literal pool (strings etc.) + * + * Global references: litidx (referred to only) + */ +static void dumplits(void) +{ + int j,k; + + k=0; + while (k=litidx) + stgwrite("\n"); /* force a newline after 10 dumps */ + /* Note: stgwrite() buffers a line until it is complete. It recognizes + * the end of line as a sequence of "\n\0", so something like "\n\t" + * so should not be passed to stgwrite(). + */ + } /* while */ + } /* while */ +} + +/* dumpzero + * + * Dump zero's for default initial values + */ +static void dumpzero(int count) +{ + int i; + + if (count<=0) + return; + assert(curseg==2); + defstorage(); + i=0; + while (count-- > 0) { + outval(0, FALSE); + i=(i+1) % 16; + stgwrite((i==0 || count==0) ? "\n" : " "); + if (i==0 && count>0) + defstorage(); + } /* while */ +} + +static void aligndata(int numbytes) +{ + assert(numbytes % sizeof(cell) == 0); /* alignment must be a multiple of + * the cell size */ + assert(numbytes!=0); + + if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) { + while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) + litadd(0); + } /* if */ + +} + +#if !defined SC_LIGHT +/* sc_attachdocumentation() + * appends documentation comments to the passed-in symbol, or to a global + * string if "sym" is NULL. + */ +void sc_attachdocumentation(symbol *sym) +{ + int line; + size_t length; + char *str,*doc; + + if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) { + /* just clear the entire table */ + delete_docstringtable(); + return; + } /* if */ + /* in the case of state functions, multiple documentation sections may + * appear; we should concatenate these + * (with forward declarations, this is also already the case, so the assertion + * below is invalid) + */ + // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL); + + /* first check the size */ + length=0; + for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) { + if (length>0) + length++; /* count 1 extra for a separating space */ + length+=strlen(str); + } /* for */ + if (sym==NULL && sc_documentation!=NULL) { + length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "

" */ + assert(length>strlen(sc_documentation)); + } /* if */ + + if (length>0) { + /* allocate memory for the documentation */ + if (sym!=NULL && sym->documentation!=NULL) + length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "

" */ + doc=(char*)malloc((length+1)*sizeof(char)); + if (doc!=NULL) { + /* initialize string or concatenate */ + if (sym==NULL && sc_documentation!=NULL) { + strcpy(doc,sc_documentation); + strcat(doc,"

"); + } else if (sym!=NULL && sym->documentation!=NULL) { + strcpy(doc,sym->documentation); + strcat(doc,"

"); + free(sym->documentation); + sym->documentation=NULL; + } else { + doc[0]='\0'; + } /* if */ + /* collect all documentation */ + while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) { + if (doc[0]!='\0') + strcat(doc," "); + strcat(doc,str); + delete_docstring(0); + } /* while */ + if (str!=NULL) { + /* also delete the separator */ + assert(*str==sDOCSEP); + delete_docstring(0); + } /* if */ + if (sym!=NULL) { + assert(sym->documentation==NULL); + sym->documentation=doc; + } else { + if (sc_documentation!=NULL) + free(sc_documentation); + sc_documentation=doc; + } /* if */ + } /* if */ + } else { + /* delete an empty separator, if present */ + if ((str=get_docstring(0))!=NULL && *str==sDOCSEP) + delete_docstring(0); + } /* if */ +} + +static void insert_docstring_separator(void) +{ + char sep[2]={sDOCSEP,'\0'}; + insert_docstring(sep); +} +#else + #define sc_attachdocumentation(s) (void)(s) + #define insert_docstring_separator() +#endif + +static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst) +{ + char name[sNAMEMAX+11]; + int tok,tag; + char *str; + cell val; + int invalidfunc; + + tag=pc_addtag(NULL); + tok=lex(&val,&str); + /* if we arrived here, this may not be a declaration of a native function + * or variable + */ + if (tok==tNATIVE) { + error(42); /* invalid combination of class specifiers */ + return; + } /* if */ + + if (tok!=tSYMBOL && tok!=tOPERATOR) { + lexpush(); + needtoken(tSYMBOL); + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop the literal queue too */ + return; + } /* if */ + if (tok==tOPERATOR) { + lexpush(); /* push "operator" keyword back (for later analysis) */ + if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) { + error(10); /* illegal function or declaration */ + lexclr(TRUE); /* drop the rest of the line */ + litidx=0; /* drop the literal queue too */ + } /* if */ + } else { + /* so tok is tSYMBOL */ + assert(strlen(str)<=sNAMEMAX); + strcpy(name,str); + /* only variables can be "const" or both "public" and "stock" */ + invalidfunc= fconst || (fpublic && fstock); + if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) { + /* if not a function, try a global variable */ + declglb(name,tag,fpublic,fstatic,fstock,fconst); + } /* if */ + } /* if */ +} + +/* declglb - declare global symbols + * + * Declare a static (global) variable. Global variables are stored in + * the DATA segment. + * + * global references: glb_declared (altered) + */ +static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst) +{ + int ident,tag,ispublic; + int idxtag[sDIMEN_MAX]; + char name[sNAMEMAX+1]; + cell val,size,cidx; + ucell address; + int glb_incr; + char *str; + int dim[sDIMEN_MAX]; + int numdim; + short filenum; + symbol *sym; + constvalue *enumroot; + #if !defined NDEBUG + cell glbdecl=0; + #endif + + assert(!fpublic || !fstatic); /* may not both be set */ + insert_docstring_separator(); /* see comment in newfunc() */ + filenum=fcurrent; /* save file number at the start of the declaration */ + do { + size=1; /* single size (no array) */ + numdim=0; /* no dimensions */ + ident=iVARIABLE; + if (firstname!=NULL) { + assert(strlen(firstname)<=sNAMEMAX); + strcpy(name,firstname); /* save symbol name */ + tag=firsttag; + firstname=NULL; + } else { + tag=pc_addtag(NULL); + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ + error(20,str); /* invalid symbol name */ + assert(strlen(str)<=sNAMEMAX); + strcpy(name,str); /* save symbol name */ + } /* if */ + ispublic=fpublic; + if (name[0]==PUBLIC_CHAR) { + ispublic=TRUE; /* implicitly public variable */ + assert(!fstatic); + } /* if */ + while (matchtoken('[')) { + ident=iARRAY; + if (numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return; + } /* if */ + size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + if (ispublic) + error(56,name); /* arrays cannot be public */ + dim[numdim++]=(int)size; + } /* while */ + assert(sc_curstates==0); + sc_curstates=getstates(name); + if (sc_curstates<0) { + error(85,name); /* empty state list on declaration */ + sc_curstates=0; + } else if (sc_curstates>0 && ispublic) { + error(88,name); /* public variables may not have states */ + sc_curstates=0; + } /* if */ + sym=findconst(name,NULL); + if (sym==NULL) { + sym=findglb(name,sSTATEVAR); + /* if a global variable without states is found and this declaration has + * states, the declaration is okay + */ + if (sym!=NULL && sym->states==NULL && sc_curstates>0) + sym=NULL; /* set to NULL, we found the global variable */ + if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL) + error(233,name); /* state variable shadows a global variable */ + } /* if */ + /* we have either: + * a) not found a matching variable (or rejected it, because it was a shadow) + * b) found a global variable and we were looking for that global variable + * c) found a state variable in the automaton that we were looking for + */ + assert(sym==NULL + || sym->states==NULL && sc_curstates==0 + || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates); + /* a state variable may only have a single id in its list (so either this + * variable has no states, or it has a single list) + */ + assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL); + /* it is okay for the (global) variable to exist, as long as it belongs to + * a different automaton + */ + if (sym!=NULL && (sym->usage & uDEFINE)!=0) + error(21,name); /* symbol already defined */ + /* if this variable is never used (which can be detected only in the + * second stage), shut off code generation + */ + cidx=0; /* only to avoid a compiler warning */ + if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) { + sc_status=statSKIP; + cidx=code_idx; + #if !defined NDEBUG + glbdecl=glb_declared; + #endif + } /* if */ + begdseg(); /* real (initialized) data in data segment */ + assert(litidx==0); /* literal queue should be empty */ + if (sc_alignnext) { + litidx=0; + aligndata(sc_dataalign); + dumplits(); /* dump the literal queue */ + sc_alignnext=FALSE; + litidx=0; /* global initial data is dumped, so restart at zero */ + } /* if */ + assert(litidx==0); /* literal queue should be empty (again) */ + initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */ + assert(size>=litidx); + if (numdim==1) + dim[0]=(int)size; + /* before dumping the initial values (or zeros) check whether this variable + * overlaps another + */ + if (sc_curstates>0) { + unsigned char *map; + + if (litidx!=0) + error(89,name); /* state variables may not be initialized */ + /* find an appropriate address for the state variable */ + /* assume that it cannot be found */ + address=sizeof(cell)*glb_declared; + glb_incr=(int)size; + /* use a memory map in which every cell occupies one bit */ + if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) { + int fsa=state_getfsa(sc_curstates); + symbol *sweep; + cell sweepsize,addr; + memset(map,0,(glb_declared+7)/8); + assert(fsa>=0); + /* fill in all variables belonging to this automaton */ + for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) { + if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym) + continue; /* hierarchical type, or no states, or same as this variable */ + if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY) + continue; /* a function or a constant */ + if ((sweep->usage & uDEFINE)==0) + continue; /* undefined variable, ignore */ + if (fsa!=state_getfsa(sweep->states->next->index)) + continue; /* wrong automaton */ + /* when arrived here, this is a global variable, with states and + * belonging to the same automaton as the variable we are declaring + */ + sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); + assert(sweep->addr % sizeof(cell) == 0); + addr=sweep->addr/sizeof(cell); + /* mark this address range */ + while (sweepsize-->0) { + map[addr/8] |= (unsigned char)(1 << (addr % 8)); + addr++; + } /* while */ + } /* for */ + /* go over it again, clearing any ranges that have conflicts */ + for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) { + if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym) + continue; /* hierarchical type, or no states, or same as this variable */ + if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY) + continue; /* a function or a constant */ + if ((sweep->usage & uDEFINE)==0) + continue; /* undefined variable, ignore */ + if (fsa!=state_getfsa(sweep->states->next->index)) + continue; /* wrong automaton */ + /* when arrived here, this is a global variable, with states and + * belonging to the same automaton as the variable we are declaring + */ + /* if the lists of states of the existing variable and the new + * variable have a non-empty intersection, this is not a suitable + * overlap point -> wipe the address range + */ + if (state_conflict_id(sc_curstates,sweep->states->next->index)) { + sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); + assert(sweep->addr % sizeof(cell) == 0); + addr=sweep->addr/sizeof(cell); + /* mark this address range */ + while (sweepsize-->0) { + map[addr/8] &= (unsigned char)(~(1 << (addr % 8))); + addr++; + } /* while */ + } /* if */ + } /* for */ + /* now walk through the map and find a starting point that is big enough */ + sweepsize=0; + for (addr=0; addr=size) + break; /* fitting range found, no need to search further */ + } /* for */ + if (sweepsize-addr>=size) + break; /* fitting range found, no need to search further */ + addr=sweepsize; + } /* for */ + free(map); + if (sweepsize-addr>=size) { + address=sizeof(cell)*addr; /* fitting range found, set it */ + glb_incr=0; + } /* if */ + } /* if */ + } else { + address=sizeof(cell)*glb_declared; + glb_incr=(int)size; + } /* if */ + if (address==sizeof(cell)*glb_declared) { + dumplits(); /* dump the literal queue */ + dumpzero((int)size-litidx); + } /* if */ + litidx=0; + if (sym==NULL) { /* define only if not yet defined */ + sym=addvariable(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag); + if (sc_curstates>0) + attachstatelist(sym,sc_curstates); + } else { /* if declared but not yet defined, adjust the variable's address */ + assert(sym->states==NULL && sc_curstates==0 + || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL); + sym->addr=address; + sym->codeaddr=code_idx; + sym->usage|=uDEFINE; + } /* if */ + assert(sym!=NULL); + sc_curstates=0; + if (ispublic) + sym->usage|=uPUBLIC; + if (fconst) + sym->usage|=uCONST; + if (fstock) + sym->usage|=uSTOCK; + if (fstatic) + sym->fnumber=filenum; + sc_attachdocumentation(sym);/* attach any documenation to the variable */ + if (sc_status==statSKIP) { + sc_status=statWRITE; + code_idx=cidx; + assert(glb_declared==glbdecl); + } else { + glb_declared+=glb_incr; /* add total number of cells (if added to the end) */ + } /* if */ + } while (matchtoken(',')); /* enddo */ /* more? */ + needtoken(tTERM); /* if not comma, must be semicolumn */ +} + +/* declloc - declare local symbols + * + * Declare local (automatic) variables. Since these variables are relative + * to the STACK, there is no switch to the DATA segment. These variables + * cannot be initialized either. + * + * global references: declared (altered) + * funcstatus (referred to only) + */ +static int declloc(int fstatic) +{ + int ident,tag; + int idxtag[sDIMEN_MAX]; + char name[sNAMEMAX+1]; + symbol *sym; + constvalue *enumroot; + cell val,size; + char *str; + value lval = {0}; + int cur_lit=0; + int dim[sDIMEN_MAX]; + int numdim; + int fconst; + int staging_start; + + fconst=matchtoken(tCONST); + do { + ident=iVARIABLE; + size=1; + numdim=0; /* no dimensions */ + tag=pc_addtag(NULL); + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ + error(20,str); /* invalid symbol name */ + assert(strlen(str)<=sNAMEMAX); + strcpy(name,str); /* save symbol name */ + if (name[0]==PUBLIC_CHAR) + error(56,name); /* local variables cannot be public */ + /* Note: block locals may be named identical to locals at higher + * compound blocks (as with standard C); so we must check (and add) + * the "nesting level" of local variables to verify the + * multi-definition of symbols. + */ + if ((sym=findloc(name))!=NULL && sym->compound==nestlevel) + error(21,name); /* symbol already defined */ + /* Although valid, a local variable whose name is equal to that + * of a global variable or to that of a local variable at a lower + * level might indicate a bug. + */ + if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL) + error(219,name); /* variable shadows another symbol */ + while (matchtoken('[')){ + ident=iARRAY; + if (numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return ident; + } /* if */ + size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + dim[numdim++]=(int)size; + } /* while */ + if (getstates(name)) + error(88,name); /* local variables may not have states */ + if (ident==iARRAY || fstatic) { + if (sc_alignnext) { + aligndata(sc_dataalign); + sc_alignnext=FALSE; + } /* if */ + cur_lit=litidx; /* save current index in the literal table */ + initials(ident,tag,&size,dim,numdim,enumroot); + if (size==0) + return ident; /* error message already given */ + if (numdim==1) + dim[0]=(int)size; + } /* if */ + /* reserve memory (on the stack) for the variable */ + if (fstatic) { + /* write zeros for uninitialized fields */ + while (litidxusage & uNATIVE)==0); + if (curfunc->x.stacksizex.stacksize=declared+1; /* +1 for PROC opcode */ + } /* if */ + /* now that we have reserved memory for the variable, we can proceed + * to initialize it */ + assert(sym!=NULL); /* we declared it, it must be there */ + sym->compound=nestlevel; /* for multiple declaration/shadowing check */ + if (fconst) + sym->usage|=uCONST; + if (!fstatic) { /* static variables already initialized */ + if (ident==iVARIABLE) { + /* simple variable, also supports initialization */ + int ctag = tag; /* set to "tag" by default */ + int explicit_init=FALSE;/* is the variable explicitly initialized? */ + if (matchtoken('=')) { + doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE); + explicit_init=TRUE; + } else { + ldconst(0,sPRI); /* uninitialized variable, set to zero */ + } /* if */ + /* now try to save the value (still in PRI) in the variable */ + lval.sym=sym; + lval.ident=iVARIABLE; + lval.constval=0; + lval.tag=tag; + check_userop(NULL,ctag,lval.tag,2,NULL,&ctag); + store(&lval); + markexpr(sEXPR,NULL,0); /* full expression ends after the store */ + assert(staging); /* end staging phase (optimize expression) */ + stgout(staging_start); + stgset(FALSE); + if (!matchtag(tag,ctag,TRUE)) + error(213); /* tag mismatch */ + /* if the variable was not explicitly initialized, reset the + * "uWRITTEN" flag that store() set */ + if (!explicit_init) + sym->usage &= ~uWRITTEN; + } else { + /* an array */ + assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax); + assert(size>0 && size>=sym->dim.array.length); + assert(numdim>1 || size==sym->dim.array.length); + /* final literal values that are zero make no sense to put in the literal + * pool, because values get zero-initialized anyway; we check for this, + * because users often explicitly initialize strings to "" + */ + while (litidx>cur_lit && litq[litidx-1]==0) + litidx--; + /* if the array is not completely filled, set all values to zero first */ + if (litidx-cur_lit=0 && cur<=numdim); + if (cur==numdim) + return 0; + subsize=calc_arraysize(dim,numdim,cur+1); + newsize=dim[cur]+dim[cur]*subsize; + if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize<(ucell)subsize + || newsize*sizeof(cell)>=CELL_MAX) + return CELL_MAX; + return newsize; +} + +static cell adjust_indirectiontables(int dim[],int numdim,int cur,cell increment, + int startlit,constvalue *lastdim,int *skipdim) +{ +static int base; + int d; + cell accum; + + assert(cur>=0 && cur=0); + assert(cur>0 && startlit==-1 || startlit>=0 && startlit<=litidx); + if (cur==0) + base=startlit; + if (cur==numdim-1) + return 0; + /* 2 or more dimensions left, fill in an indirection vector */ + assert(dim[cur]>0); + if (dim[cur+1]>0) { + for (d=0; dnext; d<*skipdim; d++,ld=ld->next) { + assert(ld!=NULL); + } /* for */ + for (d=0; dname,NULL,16)==d); + litq[base++]=(dim[cur]+accum+increment) * sizeof(cell); + accum+=ld->value-1; + *skipdim+=1; + ld=ld->next; + } /* for */ + } /* if */ + /* create the indirection tables for the lower level */ + if (cur+2=dim[cur]) { + error(18); /* initialization data exceeds array size */ + break; + } /* if */ + if (cur+20) { + if (idxcounteddim[cur]) + error(18); /* initialization data exceeds declared size */ + } /* if */ + counteddim[cur]=idx; + + return totalsize+dim[cur]; /* size of sub-arrays + indirection vector */ +} + +/* initvector + * Initialize a single dimensional array + */ +static cell initvector(int ident,int tag,cell size,int fillzero, + constvalue *enumroot,int *errorfound) +{ + cell prev1=0,prev2=0; + int ellips=FALSE; + int curlit=litidx; + int rtag,ctag; + + assert(ident==iARRAY || ident==iREFARRAY); + if (matchtoken('{')) { + constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL; + do { + int fieldlit=litidx; + int matchbrace,i; + if (matchtoken('}')) { /* to allow for trailing ',' after the initialization */ + lexpush(); + break; + } /* if */ + if ((ellips=matchtoken(tELLIPS))!=0) + break; + /* for enumeration fields, allow another level of braces ("{...}") */ + matchbrace=0; /* preset */ + ellips=0; + if (enumfield!=NULL) + matchbrace=matchtoken('{'); + for ( ;; ) { + prev2=prev1; + prev1=init(ident,&ctag,errorfound); + if (!matchbrace) + break; + if ((ellips=matchtoken(tELLIPS))!=0) + break; + if (!matchtoken(',')) { + needtoken('}'); + break; + } /* for */ + } /* for */ + /* if this array is based on an enumeration, fill the "field" up with + * zeros, and toggle the tag + */ + if (enumroot!=NULL && enumfield==NULL) + error(227); /* more initiallers than enum fields */ + rtag=tag; /* preset, may be overridden by enum field tag */ + if (enumfield!=NULL) { + cell step; + int cmptag=enumfield->index; + symbol *symfield=findconst(enumfield->name,&cmptag); + if (cmptag>1) + error(91,enumfield->name); /* ambiguous constant, needs tag override */ + assert(symfield!=NULL); + assert(fieldlitsymfield->dim.array.length) + error(228); /* length of initialler exceeds size of the enum field */ + if (ellips) { + step=prev1-prev2; + } else { + step=0; + prev1=0; + } /* if */ + for (i=litidx-fieldlit; idim.array.length; i++) { + prev1+=step; + litadd(prev1); + } /* for */ + rtag=symfield->x.tags.index; /* set the expected tag to the index tag */ + enumfield=enumfield->next; + } /* if */ + if (!matchtag(rtag,ctag,TRUE)) + error(213); /* tag mismatch */ + } while (matchtoken(',')); /* do */ + needtoken('}'); + } else { + init(ident,&ctag,errorfound); + if (!matchtag(tag,ctag,TRUE)) + error(213); /* tagname mismatch */ + } /* if */ + /* fill up the literal queue with a series */ + if (ellips) { + cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2; + if (size==0 || (litidx-curlit)==0) + error(41); /* invalid ellipsis, array size unknown */ + else if ((litidx-curlit)==(int)size) + error(18); /* initialisation data exceeds declared size */ + while ((litidx-curlit)<(int)size) { + prev1+=step; + litadd(prev1); + } /* while */ + } /* if */ + if (fillzero && size>0) { + while ((litidx-curlit)<(int)size) + litadd(0); + } /* if */ + if (size==0) { + size=litidx-curlit; /* number of elements defined */ + } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */ + error(18); /* initialisation data exceeds declared size */ + litidx=(int)size+curlit; /* avoid overflow in memory moves */ + } /* if */ + return size; +} + +/* init + * + * Evaluate one initializer. + */ +static cell init(int ident,int *tag,int *errorfound) +{ + cell i = 0; + + if (matchtoken(tSTRING)){ + /* lex() automatically stores strings in the literal table (and + * increases "litidx") */ + if (ident==iVARIABLE) { + error(6); /* must be assigned to an array */ + litidx=1; /* reset literal queue */ + } /* if */ + *tag=0; + } else if (constexpr(&i,tag,NULL)){ + litadd(i); /* store expression result in literal table */ + } else { + if (errorfound!=NULL) + *errorfound=TRUE; + } /* if */ + return i; +} + +/* needsub + * + * Get required array size + */ +static cell needsub(int *tag,constvalue **enumroot) +{ + cell val; + symbol *sym; + + assert(tag!=NULL); + *tag=0; + if (enumroot!=NULL) + *enumroot=NULL; /* preset */ + if (matchtoken(']')) /* we have already seen "[" */ + return 0; /* zero size (like "char msg[]") */ + + constexpr(&val,tag,&sym); /* get value (must be constant expression) */ + if (val<0) { + error(9); /* negative array size is invalid; assumed zero */ + val=0; + } /* if */ + needtoken(']'); + + if (enumroot!=NULL) { + /* get the field list for an enumeration */ + assert(*enumroot==NULL);/* should have been preset */ + assert(sym==NULL || sym->ident==iCONSTEXPR); + if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) { + assert(sym->dim.enumlist!=NULL); + *enumroot=sym->dim.enumlist; + } /* if */ + } /* if */ + + return val; /* return array size */ +} + +/* decl_const - declare a single constant + * + */ +static void decl_const(int vclass) +{ + char constname[sNAMEMAX+1]; + cell val; + char *str; + int tag,exprtag; + int symbolline; + symbol *sym; + + insert_docstring_separator(); /* see comment in newfunc() */ + tag=pc_addtag(NULL); + if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ + error(20,str); /* invalid symbol name */ + symbolline=fline; /* save line where symbol was found */ + strcpy(constname,str); /* save symbol name */ + needtoken('='); + constexpr(&val,&exprtag,NULL);/* get value */ + needtoken(tTERM); + /* add_constant() checks for duplicate definitions */ + if (!matchtag(tag,exprtag,FALSE)) { + /* temporarily reset the line number to where the symbol was defined */ + int orgfline=fline; + fline=symbolline; + error(213); /* tagname mismatch */ + fline=orgfline; + } /* if */ + sym=add_constant(constname,val,vclass,tag); + if (sym!=NULL) + sc_attachdocumentation(sym);/* attach any documenation to the function */ +} + +/* decl_enum - declare enumerated constants + * + */ +static void decl_enum(int vclass) +{ + char enumname[sNAMEMAX+1],constname[sNAMEMAX+1]; + cell val,value,size; + char *str; + int tag,explicittag; + cell increment,multiplier; + constvalue *enumroot; + symbol *enumsym; + + /* get an explicit tag, if any (we need to remember whether an explicit + * tag was passed, even if that explicit tag was "_:", so we cannot call + * pc_addtag() here + */ + if (lex(&val,&str)==tLABEL) { + tag=pc_addtag(str); + explicittag=TRUE; + } else { + lexpush(); + tag=0; + explicittag=FALSE; + } /* if */ + + /* get optional enum name (also serves as a tag if no explicit tag was set) */ + if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */ + strcpy(enumname,str); /* save enum name (last constant) */ + if (!explicittag) + tag=pc_addtag(enumname); + } else { + lexpush(); /* analyze again */ + enumname[0]='\0'; + } /* if */ + + /* get increment and multiplier */ + increment=1; + multiplier=1; + if (matchtoken('(')) { + if (matchtoken(taADD)) { + constexpr(&increment,NULL,NULL); + } else if (matchtoken(taMULT)) { + constexpr(&multiplier,NULL,NULL); + } else if (matchtoken(taSHL)) { + constexpr(&val,NULL,NULL); + while (val-->0) + multiplier*=2; + } /* if */ + needtoken(')'); + } /* if */ + + if (strlen(enumname)>0) { + /* already create the root symbol, so the fields can have it as their "parent" */ + enumsym=add_constant(enumname,0,vclass,tag); + if (enumsym!=NULL) + enumsym->usage |= uENUMROOT; + /* start a new list for the element names */ + if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(103); /* insufficient memory (fatal error) */ + memset(enumroot,0,sizeof(constvalue)); + } else { + enumsym=NULL; + enumroot=NULL; + } /* if */ + + needtoken('{'); + /* go through all constants */ + value=0; /* default starting value */ + do { + int idxtag,fieldtag; + symbol *sym; + if (matchtoken('}')) { /* quick exit if '}' follows ',' */ + lexpush(); + break; + } /* if */ + idxtag=pc_addtag(NULL); /* optional explicit item tag */ + if (needtoken(tSYMBOL)) { /* read in (new) token */ + tokeninfo(&val,&str); /* get the information */ + strcpy(constname,str); /* save symbol name */ + } else { + constname[0]='\0'; + } /* if */ + size=increment; /* default increment of 'val' */ + fieldtag=0; /* default field tag */ + if (matchtoken('[')) { + constexpr(&size,&fieldtag,NULL); /* get size */ + needtoken(']'); + } /* if */ + if (matchtoken('=')) + constexpr(&value,NULL,NULL); /* get value */ + /* add_constant() checks whether a variable (global or local) or + * a constant with the same name already exists + */ + sym=add_constant(constname,value,vclass,tag); + if (sym==NULL) + continue; /* error message already given */ + /* set the item tag and the item size, for use in indexing arrays */ + sym->x.tags.index=idxtag; + sym->x.tags.field=fieldtag; + sym->dim.array.length=size; + sym->dim.array.level=0; + sym->parent=enumsym; + /* add the constant to a separate list as well */ + if (enumroot!=NULL) { + sym->usage |= uENUMFIELD; + append_constval(enumroot,constname,value,tag); + } /* if */ + if (multiplier==1) + value+=size; + else + value*=size*multiplier; + } while (matchtoken(',')); + needtoken('}'); /* terminates the constant list */ + matchtoken(';'); /* eat an optional ; */ + + /* set the enum name to the "next" value (typically the last value plus one) */ + if (enumsym!=NULL) { + assert((enumsym->usage & uENUMROOT)!=0); + enumsym->addr=value; + /* assign the constant list */ + assert(enumroot!=NULL); + enumsym->dim.enumlist=enumroot; + sc_attachdocumentation(enumsym); /* attach any documenation to the enumeration */ + } /* if */ +} + +static int getstates(const char *funcname) +{ + char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1]; + cell val; + char *str; + constvalue *automaton; + constvalue *state; + int fsa,islabel; + int *list; + int count,listsize,state_id; + + if (!matchtoken('<')) + return 0; + if (matchtoken('>')) + return -1; /* special construct: all other states (fall-back) */ + + count=0; + listsize=0; + list=NULL; + fsa=-1; + + do { + if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL)) + break; + tokeninfo(&val,&str); + assert(strlen(str)=0 && automaton->index!=fsa) + error(83,funcname); /* multiple automatons for a single function/variable */ + fsa=automaton->index; + } /* if */ + state=state_add(statename,fsa); + /* add this state to the state combination list (it will be attached to the + * automaton later) */ + state_buildlist(&list,&listsize,&count,(int)state->value); + } while (matchtoken(',')); + needtoken('>'); + + if (count>0) { + assert(automaton!=NULL); + assert(fsa>=0); + state_id=state_addlist(list,count,fsa); + assert(state_id>0); + } else { + /* error is already given */ + state_id=0; + } /* if */ + if (list!=NULL) + free(list); + + return state_id; +} + +static void attachstatelist(symbol *sym, int state_id) +{ + assert(sym!=NULL); + if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0)) + error(21,sym->name); /* function already defined, either without states or the current definition has no states */ + + if (state_id!=0) { + /* add the state list id */ + constvalue *stateptr; + if (sym->states==NULL) { + if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(103); /* insufficient memory (fatal error) */ + memset(sym->states,0,sizeof(constvalue)); + } /* if */ + /* see whether the id already exists (add new state only if it does not + * yet exist + */ + assert(sym->states!=NULL); + for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next) + /* nothing */; + assert(state_id<=SHRT_MAX); + if (stateptr==NULL) + append_constval(sym->states,"",code_idx,(short)state_id); + else if (stateptr->value==0) + stateptr->value=code_idx; + else + error(84,sym->name); + /* also check for another conflicting situation: a fallback function + * without any states + */ + if (state_id==-1 && sc_status!=statFIRST) { + /* in the second round, all states should have been accumulated */ + assert(sym->states!=NULL); + for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next) + /* nothing */; + if (stateptr==NULL) + error(85,sym->name); /* no states are defined for this function */ + } /* if */ + } /* if */ +} + +/* + * Finds a function in the global symbol table or creates a new entry. + * It does some basic processing and error checking. + */ +SC_FUNC symbol *fetchfunc(char *name,int tag) +{ + symbol *sym; + + if ((sym=findglb(name,sGLOBAL))!=0) { /* already in symbol table? */ + if (sym->ident!=iFUNCTN) { + error(21,name); /* yes, but not as a function */ + return NULL; /* make sure the old symbol is not damaged */ + } else if ((sym->usage & uNATIVE)!=0) { + error(21,name); /* yes, and it is a native */ + } /* if */ + assert(sym->vclass==sGLOBAL); + if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag) + error(25); /* mismatch from earlier prototype */ + if ((sym->usage & uDEFINE)==0) { + /* as long as the function stays undefined, update the address and the tag */ + if (sym->states==NULL) + sym->addr=code_idx; + sym->tag=tag; + } /* if */ + } else { + /* don't set the "uDEFINE" flag; it may be a prototype */ + sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0); + assert(sym!=NULL); /* fatal error 103 must be given on error */ + /* assume no arguments */ + sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo)); + sym->dim.arglist[0].ident=0; + /* set library ID to NULL (only for native functions) */ + sym->x.lib=NULL; + /* set the required stack size to zero (only for non-native functions) */ + sym->x.stacksize=1; /* 1 for PROC opcode */ + } /* if */ + if (pc_depricate!=NULL) { + assert(sym!=NULL); + sym->flags|=flgDEPRICATED; + if (sc_status==statWRITE) { + if (sym->documentation!=NULL) { + free(sym->documentation); + sym->documentation=NULL; + } /* if */ + sym->documentation=pc_depricate; + } else { + free(pc_depricate); + } /* if */ + pc_depricate=NULL; + } /* if */ + + return sym; +} + +/* This routine adds symbolic information for each argument. + */ +static void define_args(void) +{ + symbol *sym; + + /* At this point, no local variables have been declared. All + * local symbols are function arguments. + */ + sym=loctab.next; + while (sym!=NULL) { + assert(sym->ident!=iLABEL); + assert(sym->vclass==sLOCAL); + markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */ + sym=sym->next; + } /* while */ +} + +static int operatorname(char *name) +{ + int opertok; + char *str; + cell val; + + assert(name!=NULL); + + /* check the operator */ + opertok=lex(&val,&str); + switch (opertok) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '>': + case '<': + case '!': + case '~': + case '=': + name[0]=(char)opertok; + name[1]='\0'; + break; + case tINC: + strcpy(name,"++"); + break; + case tDEC: + strcpy(name,"--"); + break; + case tlEQ: + strcpy(name,"=="); + break; + case tlNE: + strcpy(name,"!="); + break; + case tlLE: + strcpy(name,"<="); + break; + case tlGE: + strcpy(name,">="); + break; + default: + name[0]='\0'; + error(7); /* operator cannot be redefined (or bad operator name) */ + return 0; + } /* switch */ + + return opertok; +} + +static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag) +{ + int tags[2]={0,0}; + int count=0; + arginfo *arg; + char tmpname[sNAMEMAX+1]; + symbol *oldsym; + + if (opertok==0) + return TRUE; + + assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL); + /* count arguments and save (first two) tags */ + while (arg=&sym->dim.arglist[count], arg->ident!=0) { + if (count<2) { + if (arg->numtags>1) + error(65,count+1); /* function argument may only have a single tag */ + else if (arg->numtags==1) + tags[count]=arg->tags[0]; + } /* if */ + if (opertok=='~' && count==0) { + if (arg->ident!=iREFARRAY) + error(73,arg->name);/* must be an array argument */ + } else { + if (arg->ident!=iVARIABLE) + error(66,arg->name);/* must be non-reference argument */ + } /* if */ + if (arg->hasdefault) + error(59,arg->name); /* arguments of an operator may not have a default value */ + count++; + } /* while */ + + /* for '!', '++' and '--', count must be 1 + * for '-', count may be 1 or 2 + * for '=', count must be 1, and the resulttag is also important + * for all other (binary) operators and the special '~' operator, count must be 2 + */ + switch (opertok) { + case '!': + case '=': + case tINC: + case tDEC: + if (count!=1) + error(62); /* number or placement of the operands does not fit the operator */ + break; + case '-': + if (count!=1 && count!=2) + error(62); /* number or placement of the operands does not fit the operator */ + break; + default: + if (count!=2) + error(62); /* number or placement of the operands does not fit the operator */ + } /* switch */ + + if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0)) + error(64); /* cannot change predefined operators */ + + /* change the operator name */ + assert(strlen(opername)>0); + operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag); + if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) { + int i; + if ((oldsym->usage & uDEFINE)!=0) { + char errname[2*sNAMEMAX+16]; + funcdisplayname(errname,tmpname); + error(21,errname); /* symbol already defined */ + } /* if */ + sym->usage|=oldsym->usage; /* copy flags from the previous definition */ + for (i=0; inumrefers; i++) + if (oldsym->refer[i]!=NULL) + refer_symbol(sym,oldsym->refer[i]); + delete_symbol(&glbtab,oldsym); + } /* if */ + strcpy(sym->name,tmpname); + sym->hash=namehash(sym->name);/* calculate new hash */ + + /* operators should return a value, except the '~' operator */ + if (opertok!='~') + sym->usage |= uRETVALUE; + + return TRUE; +} + +static int check_operatortag(int opertok,int resulttag,char *opername) +{ + assert(opername!=NULL && strlen(opername)>0); + switch (opertok) { + case '!': + case '<': + case '>': + case tlEQ: + case tlNE: + case tlLE: + case tlGE: + if (resulttag!=pc_addtag("bool")) { + error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */ + return FALSE; + } /* if */ + break; + case '~': + if (resulttag!=0) { + error(63,opername,"_:"); /* operator "~" requires a "_:" result tag */ + return FALSE; + } /* if */ + break; + } /* switch */ + return TRUE; +} + +static char *tag2str(char *dest,int tag) +{ + tag &= TAGMASK; + assert(tag>=0); + sprintf(dest,"0%x",tag); + return isdigit(dest[1]) ? &dest[1] : dest; +} + +SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag) +{ + char tagstr1[10], tagstr2[10]; + int opertok; + + assert(numtags>=1 && numtags<=2); + opertok= (opername[1]=='\0') ? opername[0] : 0; + if (opertok=='=') + sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1)); + else if (numtags==1 || opertok=='~') + sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1)); + else + sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2)); + return symname; +} + +static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname) +{ + char *ptr,*name; + int unary; + + /* tags are only positive, so if the function name starts with a '-', + * the operator is an unary '-' or '--' operator. + */ + if (*fname=='-') { + *tag1=0; + unary=TRUE; + ptr=fname; + } else { + *tag1=(int)strtol(fname,&ptr,16); + unary= ptr==fname; /* unary operator if it doesn't start with a tag name */ + } /* if */ + assert(!unary || *tag1==0); + assert(*ptr!='\0'); + for (name=opname; !isdigit(*ptr); ) + *name++ = *ptr++; + *name='\0'; + *tag2=(int)strtol(ptr,NULL,16); + return unary; +} + +static constvalue *find_tag_byval(int tag) +{ + constvalue *tagsym; + tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG); + if (tagsym==NULL) + tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG); + return tagsym; +} + +SC_FUNC char *funcdisplayname(char *dest,char *funcname) +{ + int tags[2]; + char opname[10]; + constvalue *tagsym[2]; + int unary; + + if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') { + if (dest!=funcname) + strcpy(dest,funcname); + return dest; + } /* if */ + + unary=parse_funcname(funcname,&tags[0],&tags[1],opname); + tagsym[1]=find_tag_byval(tags[1]); + assert(tagsym[1]!=NULL); + if (unary) { + sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name); + } else { + tagsym[0]=find_tag_byval(tags[0]); + assert(tagsym[0]!=NULL); + /* special case: the assignment operator has the return value as the 2nd tag */ + if (opname[0]=='=' && opname[1]=='\0') + sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name); + else + sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name); + } /* if */ + return dest; +} + +static void funcstub(int fnative) +{ + int tok,tag,fpublic; + char *str; + cell val,size; + char symbolname[sNAMEMAX+1]; + int idxtag[sDIMEN_MAX]; + int dim[sDIMEN_MAX]; + int numdim; + symbol *sym,*sub; + int opertok; + + opertok=0; + lastst=0; + litidx=0; /* clear the literal pool */ + assert(loctab.next==NULL); /* local symbol table should be empty */ + + tag=pc_addtag(NULL); /* get the tag of the return value */ + numdim=0; + while (matchtoken('[')) { + /* the function returns an array, get this tag for the index and the array + * dimensions + */ + if (numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return; + } /* if */ + size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */ + if (size==0) + error(9); /* invalid array size */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + dim[numdim++]=(int)size; + } /* while */ + + tok=lex(&val,&str); + fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR); + if (fnative) { + if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR) + error(42); /* invalid combination of class specifiers */ + } else { + if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC) + tok=lex(&val,&str); + } /* if */ + + if (tok==tOPERATOR) { + opertok=operatorname(symbolname); + if (opertok==0) + return; /* error message already given */ + check_operatortag(opertok,tag,symbolname); + } else { + if (tok!=tSYMBOL && freading) { + error(10); /* illegal function or declaration */ + return; + } /* if */ + strcpy(symbolname,str); + } /* if */ + needtoken('('); /* only functions may be native/forward */ + + sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ + if (sym==NULL) + return; + if (fnative) { + sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); + sym->x.lib=curlibrary; + } else if (fpublic) { + sym->usage|=uPUBLIC; + } /* if */ + sym->usage|=uFORWARD; + + declargs(sym,FALSE); + /* "declargs()" found the ")" */ + sc_attachdocumentation(sym); /* attach any documenation to the function */ + if (!operatoradjust(opertok,sym,symbolname,tag)) + sym->usage &= ~uDEFINE; + + if (getstates(symbolname)!=0) { + if (fnative || opertok!=0) + error(82); /* native functions and operators may not have states */ + else + error(231); /* ignoring state specifications on forward declarations */ + } /* if */ + + /* for a native operator, also need to specify an "exported" function name; + * for a native function, this is optional + */ + if (fnative) { + if (opertok!=0) { + needtoken('='); + lexpush(); /* push back, for matchtoken() to retrieve again */ + } /* if */ + if (matchtoken('=')) { + /* allow number or symbol */ + if (matchtoken(tSYMBOL)) { + tokeninfo(&val,&str); + insert_alias(sym->name,str); + } else { + constexpr(&val,NULL,NULL); + sym->addr=val; + /* At the moment, I have assumed that this syntax is only valid if + * val < 0. To properly mix "normal" native functions and indexed + * native functions, one should use negative indices anyway. + * Special code for a negative index in sym->addr exists in SC4.C + * (ffcall()) and in SC6.C (the loops for counting the number of native + * variables and for writing them). + */ + } /* if */ + } /* if */ + } /* if */ + needtoken(tTERM); + + /* attach the array to the function symbol */ + if (numdim>0) { + assert(sym!=NULL); + sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag); + sub->parent=sym; + } /* if */ + + litidx=0; /* clear the literal pool */ + delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */ +} + +/* newfunc - begin a function + * + * This routine is called from "parse" and tries to make a function + * out of the following text + * + * Global references: funcstatus,lastst,litidx + * rettype (altered) + * curfunc (altered) + * declared (altered) + * glb_declared (altered) + * sc_alignnext (altered) + */ +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock) +{ + symbol *sym; + int argcnt,tok,tag,funcline; + int opertok,opererror; + char symbolname[sNAMEMAX+1]; + char *str; + cell val,cidx,glbdecl; + short filenum; + int state_id; + + assert(litidx==0); /* literal queue should be empty */ + litidx=0; /* clear the literal pool (should already be empty) */ + opertok=0; + lastst=0; /* no statement yet */ + cidx=0; /* just to avoid compiler warnings */ + glbdecl=0; + assert(loctab.next==NULL); /* local symbol table should be empty */ + filenum=fcurrent; /* save file number at the start of the declaration */ + + if (firstname!=NULL) { + assert(strlen(firstname)<=sNAMEMAX); + strcpy(symbolname,firstname); /* save symbol name */ + tag=firsttag; + } else { + tag= (firsttag>=0) ? firsttag : pc_addtag(NULL); + tok=lex(&val,&str); + assert(!fpublic); + if (tok==tNATIVE || tok==tPUBLIC && stock) + error(42); /* invalid combination of class specifiers */ + if (tok==tOPERATOR) { + opertok=operatorname(symbolname); + if (opertok==0) + return TRUE; /* error message already given */ + check_operatortag(opertok,tag,symbolname); + } else { + if (tok!=tSYMBOL && freading) { + error(20,str); /* invalid symbol name */ + return FALSE; + } /* if */ + assert(strlen(str)<=sNAMEMAX); + strcpy(symbolname,str); + } /* if */ + } /* if */ + /* check whether this is a function or a variable declaration */ + if (!matchtoken('(')) + return FALSE; + /* so it is a function, proceed */ + funcline=fline; /* save line at which the function is defined */ + if (symbolname[0]==PUBLIC_CHAR) { + fpublic=TRUE; /* implicitly public function */ + if (stock) + error(42); /* invalid combination of class specifiers */ + } /* if */ + sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ + if (sym==NULL || (sym->usage & uNATIVE)!=0) + return TRUE; /* it was recognized as a function declaration, but not as a valid one */ + if (fpublic) + sym->usage|=uPUBLIC; + if (fstatic) + sym->fnumber=filenum; + /* if the function was used before being declared, and it has a tag for the + * result, add a third pass (as second "skimming" parse) because the function + * result may have been used with user-defined operators, which have now + * been incorrectly flagged (as the return tag was unknown at the time of + * the call) + */ + if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) { + int curstatus=sc_status; + sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */ + error(208); + sc_status=curstatus; + sc_reparse=TRUE; /* must add another pass to "initial scan" phase */ + } /* if */ + /* we want public functions to be explicitly prototyped, as they are called + * from the outside + */ + if (fpublic && (sym->usage & uFORWARD)==0) + error(235,symbolname); + /* declare all arguments */ + argcnt=declargs(sym,TRUE); + opererror=!operatoradjust(opertok,sym,symbolname,tag); + if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) { + if (argcnt>0) + error(5); /* "main()" and "entry()" functions may not have any arguments */ + sym->usage|=uREAD; /* "main()" is the program's entry point: always used */ + } /* if */ + state_id=getstates(symbolname); + if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0)) + error(82); /* operators may not have states, main() may neither */ + attachstatelist(sym,state_id); + /* "declargs()" found the ")"; if a ";" appears after this, it was a + * prototype */ + if (matchtoken(';')) { + sym->usage|=uFORWARD; + if (!sc_needsemicolon) + error(218); /* old style prototypes used with optional semicolumns */ + delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */ + return TRUE; + } /* if */ + /* so it is not a prototype, proceed */ + /* if this is a function that is not referred to (this can only be detected + * in the second stage), shut code generation off */ + if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) { + sc_status=statSKIP; + cidx=code_idx; + glbdecl=glb_declared; + } /* if */ + if ((sym->flags & flgDEPRICATED)!=0) { + char *ptr= (sym->documentation!=NULL) ? sym->documentation : ""; + error(234,symbolname,ptr); /* depricated (probably a public function) */ + } /* if */ + begcseg(); + sym->usage|=uDEFINE; /* set the definition flag */ + if (stock) + sym->usage|=uSTOCK; + if (opertok!=0 && opererror) + sym->usage &= ~uDEFINE; + /* if the function has states, dump the label to the start of the function */ + if (state_id!=0) { + constvalue *ptr=sym->states->next; + while (ptr!=NULL) { + assert(sc_status!=statWRITE || strlen(ptr->name)>0); + if (ptr->index==state_id) { + setlabel((int)strtol(ptr->name,NULL,16)); + break; + } /* if */ + ptr=ptr->next; + } /* while */ + } /* if */ + startfunc(sym->name); /* creates stack frame */ + insert_dbgline(funcline); + setline(FALSE); + if (sc_alignnext) { + alignframe(sc_dataalign); + sc_alignnext=FALSE; + } /* if */ + declared=0; /* number of local cells */ + rettype=(sym->usage & uRETVALUE); /* set "return type" variable */ + curfunc=sym; + define_args(); /* add the symbolic info for the function arguments */ + #if !defined SC_LIGHT + if (matchtoken('{')) { + lexpush(); + } else { + /* Insert a separator so that comments following the statement will not + * be attached to this function; they should be attached to the next + * function. This is not a problem for functions having a compound block, + * because the closing brace is an explicit "end token" for the function. + * With single statement functions, the preprocessor may overread the + * source code before the parser determines an "end of statement". + */ + insert_docstring_separator(); + } /* if */ + #endif + sc_curstates=state_id;/* set state id, for accessing global state variables */ + statement(NULL,FALSE); + sc_curstates=0; + if ((rettype & uRETVALUE)!=0) + sym->usage|=uRETVALUE; + if (declared!=0) { + /* This happens only in a very special (and useless) case, where a function + * has only a single statement in its body (no compound block) and that + * statement declares a new variable + */ + modstk((int)declared*sizeof(cell)); /* remove all local variables */ + declared=0; + } /* if */ + if ((lastst!=tRETURN) && (lastst!=tGOTO)){ + ldconst(0,sPRI); + ffret(strcmp(sym->name,uENTRYFUNC)!=0); + if ((sym->usage & uRETVALUE)!=0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + error(209,symname); /* function should return a value */ + } /* if */ + } /* if */ + endfunc(); + sym->codeaddr=code_idx; + sc_attachdocumentation(sym); /* attach collected documenation to the function */ + if (litidx) { /* if there are literals defined */ + glb_declared+=litidx; + begdseg(); /* flip to DATA segment */ + dumplits(); /* dump literal strings */ + litidx=0; + } /* if */ + testsymbols(&loctab,0,TRUE,TRUE); /* test for unused arguments and labels */ + delete_symbols(&loctab,0,TRUE,TRUE); /* clear local variables queue */ + assert(loctab.next==NULL); + curfunc=NULL; + if (sc_status==statSKIP) { + sc_status=statWRITE; + code_idx=cidx; + glb_declared=glbdecl; + } /* if */ + return TRUE; +} + +static int argcompare(arginfo *a1,arginfo *a2) +{ + int result,level,i; + + result= strcmp(a1->name,a2->name)==0; /* name */ + if (result) + result= a1->ident==a2->ident; /* type/class */ + if (result) + result= a1->usage==a2->usage; /* "const" flag */ + if (result) + result= a1->numtags==a2->numtags; /* tags (number and names) */ + for (i=0; result && inumtags; i++) + result= a1->tags[i]==a2->tags[i]; + if (result) + result= a1->numdim==a2->numdim; /* array dimensions & index tags */ + for (level=0; result && levelnumdim; level++) + result= a1->dim[level]==a2->dim[level]; + for (level=0; result && levelnumdim; level++) + result= a1->idxtag[level]==a2->idxtag[level]; + if (result) + result= a1->hasdefault==a2->hasdefault; /* availability of default value */ + if (a1->hasdefault) { + if (a1->ident==iREFARRAY) { + if (result) + result= a1->defvalue.array.size==a2->defvalue.array.size; + if (result) + result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize; + /* ??? should also check contents of the default array (these troubles + * go away in a 2-pass compiler that forbids double declarations, but + * Pawn currently does not forbid them) */ + } else { + if (result) { + if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0) + result= a1->hasdefault==a2->hasdefault + && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0 + && a1->defvalue.size.level==a2->defvalue.size.level; + else + result= a1->defvalue.val==a2->defvalue.val; + } /* if */ + } /* if */ + if (result) + result= a1->defvalue_tag==a2->defvalue_tag; + } /* if */ + return result; +} + +/* declargs() + * + * This routine adds an entry in the local symbol table for each argument + * found in the argument list. It returns the number of arguments. + */ +static int declargs(symbol *sym,int chkshadow) +{ + #define MAXTAGS 16 + char *ptr; + int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags; + cell val; + arginfo arg, *arglist; + char name[sNAMEMAX+1]; + int ident,fpublic,fconst; + int idx; + + /* if the function is already defined earlier, get the number of arguments + * of the existing definition + */ + oldargcnt=0; + if ((sym->usage & uPROTOTYPED)!=0) + while (sym->dim.arglist[oldargcnt].ident!=0) + oldargcnt++; + argcnt=0; /* zero aruments up to now */ + ident=iVARIABLE; + numtags=0; + fconst=FALSE; + fpublic= (sym->usage & uPUBLIC)!=0; + /* the '(' parantheses has already been parsed */ + if (!matchtoken(')')){ + do { /* there are arguments; process them */ + /* any legal name increases argument count (and stack offset) */ + tok=lex(&val,&ptr); + switch (tok) { + case 0: + /* nothing */ + break; + case '&': + if (ident!=iVARIABLE || numtags>0) + error(1,"-identifier-","&"); + ident=iREFERENCE; + break; + case tCONST: + if (ident!=iVARIABLE || numtags>0) + error(1,"-identifier-","const"); + fconst=TRUE; + break; + case tLABEL: + if (numtags>0) + error(1,"-identifier-","-tagname-"); + tags[0]=pc_addtag(ptr); + numtags=1; + break; + case '{': + if (numtags>0) + error(1,"-identifier-","-tagname-"); + numtags=0; + while (numtags=sMAXARGS) + error(45); /* too many function arguments */ + strcpy(name,ptr); /* save symbol name */ + if (name[0]==PUBLIC_CHAR) + error(56,name); /* function arguments cannot be public */ + if (numtags==0) + tags[numtags++]=0; /* default tag */ + /* Stack layout: + * base + 0*sizeof(cell) == previous "base" + * base + 1*sizeof(cell) == function return address + * base + 2*sizeof(cell) == number of arguments + * base + 3*sizeof(cell) == first argument of the function + * So the offset of each argument is "(argcnt+3) * sizeof(cell)". + */ + doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg); + if (fpublic && arg.hasdefault) + error(59,name); /* arguments of a public function may not have a default value */ + if ((sym->usage & uPROTOTYPED)==0) { + /* redimension the argument list, add the entry */ + sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo)); + if (sym->dim.arglist==0) + error(103); /* insufficient memory */ + memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */ + sym->dim.arglist[argcnt]=arg; + } else { + /* check the argument with the earlier definition */ + if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg)) + error(25); /* function definition does not match prototype */ + /* may need to free default array argument and the tag list */ + if (arg.ident==iREFARRAY && arg.hasdefault) + free(arg.defvalue.array.data); + else if (arg.ident==iVARIABLE + && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0)) + free(arg.defvalue.size.symname); + free(arg.tags); + } /* if */ + argcnt++; + ident=iVARIABLE; + numtags=0; + fconst=FALSE; + break; + case tELLIPS: + if (ident!=iVARIABLE) + error(10); /* illegal function or declaration */ + if (numtags==0) + tags[numtags++]=0; /* default tag */ + if ((sym->usage & uPROTOTYPED)==0) { + /* redimension the argument list, add the entry iVARARGS */ + sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo)); + if (sym->dim.arglist==0) + error(103); /* insufficient memory */ + memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */ + sym->dim.arglist[argcnt].ident=iVARARGS; + sym->dim.arglist[argcnt].hasdefault=FALSE; + sym->dim.arglist[argcnt].defvalue.val=0; + sym->dim.arglist[argcnt].defvalue_tag=0; + sym->dim.arglist[argcnt].numtags=numtags; + sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]); + if (sym->dim.arglist[argcnt].tags==NULL) + error(103); /* insufficient memory */ + memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]); + } else { + if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS) + error(25); /* function definition does not match prototype */ + } /* if */ + argcnt++; + break; + default: + error(10); /* illegal function or declaration */ + } /* switch */ + } while (tok=='&' || tok==tLABEL || tok==tCONST + || tok!=tELLIPS && matchtoken(',')); /* more? */ + /* if the next token is not ",", it should be ")" */ + needtoken(')'); + } /* if */ + /* resolve any "sizeof" arguments (now that all arguments are known) */ + assert(sym->dim.arglist!=NULL); + arglist=sym->dim.arglist; + for (idx=0; idx=argcnt) { + error(17,ptr); /* undefined symbol */ + } else { + assert(arglist[idx].defvalue.size.symname!=NULL); + /* check the level against the number of dimensions */ + if (arglist[idx].defvalue.size.level>0 + && arglist[idx].defvalue.size.level>=arglist[altidx].numdim) + error(28,arglist[idx].name); /* invalid subscript */ + /* check the type of the argument whose size to take; for a iVARIABLE + * or a iREFERENCE, this is always 1 (so the code is redundant) + */ + assert(arglist[altidx].ident!=iVARARGS); + if (arglist[altidx].ident!=iREFARRAY && (arglist[idx].hasdefault & uSIZEOF)!=0) { + if ((arglist[idx].hasdefault & uTAGOF)!=0) { + error(81,arglist[idx].name); /* cannot take "tagof" an indexed array */ + } else { + assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE); + error(223,ptr); /* redundant sizeof */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* for */ + + sym->usage|=uPROTOTYPED; + errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/ + return argcnt; +} + +/* doarg - declare one argument type + * + * this routine is called from "declargs()" and adds an entry in the local + * symbol table for one argument. + * + * "fpublic" indicates whether the function for this argument list is public. + * The arguments themselves are never public. + */ +static void doarg(char *name,int ident,int offset,int tags[],int numtags, + int fpublic,int fconst,int chkshadow,arginfo *arg) +{ + symbol *argsym; + constvalue *enumroot; + cell size; + + strcpy(arg->name,name); + arg->hasdefault=FALSE; /* preset (most common case) */ + arg->defvalue.val=0; /* clear */ + arg->defvalue_tag=0; + arg->numdim=0; + if (matchtoken('[')) { + if (ident==iREFERENCE) + error(67,name); /* illegal declaration ("&name[]" is unsupported) */ + do { + if (arg->numdim == sDIMEN_MAX) { + error(53); /* exceeding maximum number of dimensions */ + return; + } /* if */ + size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */ + #if INT_MAX < LONG_MAX + if (size > INT_MAX) + error(105); /* overflow, exceeding capacity */ + #endif + arg->dim[arg->numdim]=(int)size; + arg->numdim+=1; + } while (matchtoken('[')); + ident=iREFARRAY; /* "reference to array" (is a pointer) */ + if (matchtoken('=')) { + lexpush(); /* initials() needs the "=" token again */ + assert(litidx==0); /* at the start of a function, this is reset */ + assert(numtags>0); + initials(ident,tags[0],&size,arg->dim,arg->numdim,enumroot); + assert(size>=litidx); + /* allocate memory to hold the initial values */ + arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell)); + if (arg->defvalue.array.data!=NULL) { + int i; + memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell)); + arg->hasdefault=TRUE; /* argument has default value */ + arg->defvalue.array.size=litidx; + arg->defvalue.array.addr=-1; + /* calulate size to reserve on the heap */ + arg->defvalue.array.arraysize=1; + for (i=0; inumdim; i++) + arg->defvalue.array.arraysize*=arg->dim[i]; + if (arg->defvalue.array.arraysize < arg->defvalue.array.size) + arg->defvalue.array.arraysize = arg->defvalue.array.size; + } /* if */ + litidx=0; /* reset */ + } /* if */ + } else { + if (matchtoken('=')) { + unsigned char size_tag_token; + assert(ident==iVARIABLE || ident==iREFERENCE); + arg->hasdefault=TRUE; /* argument has a default value */ + size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0); + if (size_tag_token==0) + size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0); + if (size_tag_token!=0) { + int paranthese; + if (ident==iREFERENCE) + error(66,name); /* argument may not be a reference */ + paranthese=0; + while (matchtoken('(')) + paranthese++; + if (needtoken(tSYMBOL)) { + /* save the name of the argument whose size id to take */ + char *name; + cell val; + tokeninfo(&val,&name); + if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL) + error(103); /* insufficient memory */ + arg->defvalue.size.level=0; + if (size_tag_token==uSIZEOF) { + while (matchtoken('[')) { + arg->defvalue.size.level+=(short)1; + needtoken(']'); + } /* while */ + } /* if */ + if (ident==iVARIABLE) /* make sure we set this only if not a reference */ + arg->hasdefault |= size_tag_token; /* uSIZEOF or uTAGOF */ + } /* if */ + while (paranthese--) + needtoken(')'); + } else { + constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL); + assert(numtags>0); + if (!matchtag(tags[0],arg->defvalue_tag,TRUE)) + error(213); /* tagname mismatch */ + } /* if */ + } /* if */ + } /* if */ + arg->ident=(char)ident; + arg->usage=(char)(fconst ? uCONST : 0); + arg->numtags=numtags; + arg->tags=(int*)malloc(numtags*sizeof tags[0]); + if (arg->tags==NULL) + error(103); /* insufficient memory */ + memcpy(arg->tags,tags,numtags*sizeof tags[0]); + argsym=findloc(name); + if (argsym!=NULL) { + error(21,name); /* symbol already defined */ + } else { + if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN) + error(219,name); /* variable shadows another symbol */ + /* add details of type and address */ + assert(numtags>0); + argsym=addvariable(name,offset,ident,sLOCAL,tags[0], + arg->dim,arg->numdim,arg->idxtag); + argsym->compound=0; + if (ident==iREFERENCE) + argsym->usage|=uREAD; /* because references are passed back */ + if (fpublic) + argsym->usage|=uREAD; /* arguments of public functions are always "used" */ + if (fconst) + argsym->usage|=uCONST; + } /* if */ +} + +static int count_referrers(symbol *entry) +{ + int i,count; + + count=0; + for (i=0; inumrefers; i++) + if (entry->refer[i]!=NULL) + count++; + return count; +} + +#if !defined SC_LIGHT +static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue, + char **outer_start,int *outer_length, + char **inner_start,int *inner_length) +{ + char *ptr,*inner_end; + int xmltag_len,xmlparam_len,xmlvalue_len; + int match; + + assert(source!=NULL); + assert(xmltag!=NULL); + assert(outer_start!=NULL); + assert(outer_length!=NULL); + assert(inner_start!=NULL); + assert(inner_length!=NULL); + + /* both NULL or both non-NULL */ + assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL); + + xmltag_len=strlen(xmltag); + xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0; + xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0; + ptr=source; + /* find an opening '<' */ + while ((ptr=strchr(ptr,'<'))!=NULL) { + *outer_start=ptr; /* be optimistic... */ + match=FALSE; /* ...and pessimistic at the same time */ + ptr++; /* skip '<' */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) { + /* xml tag found, optionally check the parameter */ + ptr+=xmltag_len; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (xmlparam!=NULL) { + if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) { + ptr+=xmlparam_len; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (*ptr=='=') { + ptr++; /* skip '=' */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (*ptr=='"' || *ptr=='\'') + ptr++; /* skip " or ' */ + assert(xmlvalue!=NULL); + if (strncmp(ptr,xmlvalue,xmlvalue_len)==0 + && (*(ptr+xmlvalue_len)<=' ' + || *(ptr+xmlvalue_len)=='>' + || *(ptr+xmlvalue_len)=='"' + || *(ptr+xmlvalue_len)=='\'')) + match=TRUE; /* found it */ + } /* if */ + } /* if */ + } else { + match=TRUE; /* don't check the parameter */ + } /* if */ + } /* if */ + if (match) { + /* now find the end of the opening tag */ + while (*ptr!='\0' && *ptr!='>') + ptr++; + if (*ptr=='>') + ptr++; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + *inner_start=ptr; + /* find the start of the closing tag (assume no nesting) */ + while ((ptr=strchr(ptr,'<'))!=NULL) { + inner_end=ptr; + ptr++; /* skip '<' */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (*ptr=='/') { + ptr++; /* skip / */ + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip white space */ + if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) { + /* find the end of the closing tag */ + while (*ptr!='\0' && *ptr!='>') + ptr++; + if (*ptr=='>') + ptr++; + /* set the lengths of the inner and outer segment */ + assert(*inner_start!=NULL); + *inner_length=(int)(inner_end-*inner_start); + assert(*outer_start!=NULL); + *outer_length=(int)(ptr-*outer_start); + break; /* break out of the loop */ + } /* if */ + } /* if */ + } /* while */ + return TRUE; + } /* if */ + } /* while */ + return FALSE; /* not found */ +} + +static char *xmlencode(char *dest,char *source) +{ + char temp[2*sNAMEMAX+20],*ptr; + + /* replace < by < and such; normally, such a symbol occurs at most once in + * a symbol name (e.g. "operator<") + */ + ptr=temp; + while (*source!='\0') { + switch (*source) { + case '<': + strcpy(ptr,"<"); + ptr+=4; + break; + case '>': + strcpy(ptr,">"); + ptr+=4; + break; + case '&': + strcpy(ptr,"&"); + ptr+=5; + break; + default: + *ptr++=*source; + } /* switch */ + source++; + } /* while */ + *ptr='\0'; + strcpy(dest,temp); + return dest; +} + +static void make_report(symbol *root,FILE *log,char *sourcefile) +{ + char symname[_MAX_PATH]; + int i,arg; + symbol *sym,*ref; + constvalue *tagsym; + constvalue *enumroot; + char *ptr; + + /* adapt the installation directory */ + strcpy(symname,sc_rootpath); + #if DIRSEP_CHAR=='\\' + while ((ptr=strchr(symname,':'))!=NULL) + *ptr='|'; + while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL) + *ptr='/'; + #endif + + /* the XML header */ + fprintf(log,"\n"); + fprintf(log,"\n",symname); + fprintf(log,"\n",sourcefile); + ptr=strrchr(sourcefile,DIRSEP_CHAR); + if (ptr!=NULL) + ptr++; + else + ptr=sourcefile; + fprintf(log,"\t\n\t\t%s\n\t\n",ptr); + + /* attach the global documentation, if any */ + if (sc_documentation!=NULL) { + fprintf(log,"\n\t\n"); + fprintf(log,"\t\n\t\t"); + fputs(sc_documentation,log); + fprintf(log,"\n\t\n\n"); + } /* if */ + + /* use multiple passes to print constants variables and functions in + * separate sections + */ + fprintf(log,"\t\n"); + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE + || sym->ident==iARRAY || sym->ident==iFUNCTN); + if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0) + continue; + if ((sym->usage & uREAD)==0) + continue; + fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name),sym->addr); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + /* browse through all fields */ + if ((enumroot=sym->dim.enumlist)!=NULL) { + enumroot=enumroot->next; /* skip root */ + while (enumroot!=NULL) { + fprintf(log,"\t\t\t\n",funcdisplayname(symname,enumroot->name),enumroot->value); + /* find the constant with this name and get the tag */ + ref=findglb(enumroot->name,sGLOBAL); + if (ref!=NULL) { + if (ref->x.tags.index!=0) { + tagsym=find_tag_byval(ref->x.tags.index); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\t\n",tagsym->name); + } /* if */ + if (ref->dim.array.length!=1) + fprintf(log,"\t\t\t\t\n",(long)ref->dim.array.length); + } /* if */ + fprintf(log,"\t\t\t\n"); + enumroot=enumroot->next; + } /* while */ + } /* if */ + assert(sym->refer!=NULL); + for (i=0; inumrefers; i++) { + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE + || sym->ident==iARRAY || sym->ident==iFUNCTN); + if (sym->ident!=iCONSTEXPR) + continue; + if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0) + continue; + fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name),sym->addr); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + assert(sym->refer!=NULL); + for (i=0; inumrefers; i++) { + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + if (sym->ident!=iVARIABLE && sym->ident!=iARRAY) + continue; + fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name)); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + assert(sym->refer!=NULL); + if ((sym->usage & uPUBLIC)!=0) + fprintf(log,"\t\t\t\n"); + for (i=0; inumrefers; i++) { + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\t\n"); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + if (sym->ident!=iFUNCTN) + continue; + if ((sym->usage & (uREAD | uNATIVE))==uNATIVE) + continue; /* unused native function */ + funcdisplayname(symname,sym->name); + xmlencode(symname,symname); + fprintf(log,"\t\tdim.arglist!=NULL); + for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) { + int dim; + if (arg>0) + fprintf(log,", "); + switch (sym->dim.arglist[arg].ident) { + case iVARIABLE: + fprintf(log,"%s",sym->dim.arglist[arg].name); + break; + case iREFERENCE: + fprintf(log,"&%s",sym->dim.arglist[arg].name); + break; + case iREFARRAY: + fprintf(log,"%s",sym->dim.arglist[arg].name); + for (dim=0; dimdim.arglist[arg].numdim;dim++) + fprintf(log,"[]"); + break; + case iVARARGS: + fprintf(log,"..."); + break; + } /* switch */ + } /* for */ + /* ??? should also print an "array return" size */ + fprintf(log,")\">\n"); + if (sym->tag!=0) { + tagsym=find_tag_byval(sym->tag); + assert(tagsym!=NULL); + fprintf(log,"\t\t\t\n",tagsym->name); + } /* if */ + /* check whether this function is called from the outside */ + if ((sym->usage & uNATIVE)!=0) + fprintf(log,"\t\t\t\n"); + if ((sym->usage & uPUBLIC)!=0) + fprintf(log,"\t\t\t\n"); + if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0) + fprintf(log,"\t\t\t\n"); + if ((sym->usage & uNATIVE)==0) + fprintf(log,"\t\t\t\n",(long)sym->x.stacksize); + if (sym->states!=NULL) { + constvalue *stlist=sym->states->next; + assert(stlist!=NULL); /* there should be at least one state item */ + while (stlist!=NULL && stlist->index==-1) + stlist=stlist->next; + assert(stlist!=NULL); /* state id should be found */ + i=state_getfsa(stlist->index); + assert(i>=0); /* automaton 0 exists */ + stlist=automaton_findid(i); + assert(stlist!=NULL); /* automaton should be found */ + fprintf(log,"\t\t\t\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)"); + //??? dump state decision table + } /* if */ + assert(sym->refer!=NULL); + for (i=0; inumrefers; i++) + if ((ref=sym->refer[i])!=NULL) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + /* print all symbols that are required for this function to compile */ + for (ref=root->next; ref!=NULL; ref=ref->next) { + if (ref==sym) + continue; + for (i=0; inumrefers; i++) + if (ref->refer[i]==sym) + fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); + } /* for */ + /* print parameter list, with tag & const information, plus descriptions */ + assert(sym->dim.arglist!=NULL); + for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) { + int dim,paraminfo; + char *outer_start,*inner_start; + int outer_length,inner_length; + if (sym->dim.arglist[arg].ident==iVARARGS) + fprintf(log,"\t\t\t\n"); + else + fprintf(log,"\t\t\t\n",sym->dim.arglist[arg].name); + /* print the tag name(s) for each parameter */ + assert(sym->dim.arglist[arg].numtags>0); + assert(sym->dim.arglist[arg].tags!=NULL); + paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) + || sym->dim.arglist[arg].ident==iREFERENCE + || sym->dim.arglist[arg].ident==iREFARRAY; + if (paraminfo) + fprintf(log,"\t\t\t\t"); + if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) { + assert(paraminfo); + if (sym->dim.arglist[arg].numtags>1) + fprintf(log," {"); + for (i=0; idim.arglist[arg].numtags; i++) { + if (i>0) + fprintf(log,","); + tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]); + assert(tagsym!=NULL); + fprintf(log,"%s",tagsym->name); + } /* for */ + if (sym->dim.arglist[arg].numtags>1) + fprintf(log,"}"); + } /* if */ + switch (sym->dim.arglist[arg].ident) { + case iREFERENCE: + fprintf(log," &"); + break; + case iREFARRAY: + fprintf(log," "); + for (dim=0; dimdim.arglist[arg].numdim; dim++) { + if (sym->dim.arglist[arg].dim[dim]==0) { + fprintf(log,"[]"); + } else { + //??? find index tag + fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]); + } /* if */ + } /* for */ + break; + } /* switch */ + if (paraminfo) + fprintf(log," \n"); + /* print the user description of the parameter (parse through + * sym->documentation) + */ + if (sym->documentation!=NULL + && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name, + &outer_start, &outer_length, &inner_start, &inner_length)) + { + char *tail; + fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start); + /* delete from documentation string */ + tail=outer_start+outer_length; + memmove(outer_start,tail,strlen(tail)+1); + } /* if */ + fprintf(log,"\t\t\t\n"); + } /* for */ + if (sym->documentation!=NULL) + fprintf(log,"\t\t\t%s\n",sym->documentation); + fprintf(log,"\t\t\n"); + } /* for */ + + fprintf(log,"\n\t\n"); + fprintf(log,"\n"); +} +#endif + +/* Every symbol has a referrer list, that contains the functions that use + * the symbol. Now, if function "apple" is accessed by functions "banana" and + * "citron", but neither function "banana" nor "citron" are used by anyone + * else, then, by inference, function "apple" is not used either. + */ +static void reduce_referrers(symbol *root) +{ + int i,restart; + symbol *sym,*ref; + + do { + restart=0; + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL) + continue; /* hierarchical data type */ + if (sym->ident==iFUNCTN + && (sym->usage & uNATIVE)==0 + && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0 + && count_referrers(sym)==0) + { + sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */ + /* find all symbols that are referred by this symbol */ + for (ref=root->next; ref!=NULL; ref=ref->next) { + if (ref->parent!=NULL) + continue; /* hierarchical data type */ + assert(ref->refer!=NULL); + for (i=0; inumrefers && ref->refer[i]!=sym; i++) + /* nothing */; + if (inumrefers) { + assert(ref->refer[i]==sym); + ref->refer[i]=NULL; + restart++; + } /* if */ + } /* for */ + } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY) + && (sym->usage & uPUBLIC)==0 + && sym->parent==NULL + && count_referrers(sym)==0) + { + sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */ + } /* if */ + } /* for */ + /* after removing a symbol, check whether more can be removed */ + } while (restart>0); +} + +#if !defined SC_LIGHT +static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion) +{ + long size,maxsize; + int i; + + assert(sym!=NULL); + assert(sym->ident==iFUNCTN); + assert((sym->usage & uNATIVE)==0); + assert(recursion!=NULL); + + maxsize=sym->x.stacksize; + for (i=0; inumrefers; i++) { + if (sym->refer[i]!=NULL) { + assert(sym->refer[i]->ident==iFUNCTN); + assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */ + if (sym->refer[i]==sourcesym) { /* recursion detection */ + *recursion=1; + break; /* recursion was detected, quit loop */ + } /* if */ + size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion); + if (maxsizeusage & uPUBLIC)!=0) { + /* Find out how many parameters a public function has, then see if this + * is bigger than some maximum + */ + arginfo *arg=sym->dim.arglist; + int count=0; + assert(arg!=0); + while (arg->ident!=0) { + count++; + arg++; + } /* while */ + assert(pubfuncparams!=0); + if (count>*pubfuncparams) + *pubfuncparams=count; + } /* if */ + + return maxsize+basesize; +} + +static long max_stacksize(symbol *root,int *recursion) +{ + /* Loop over all non-native functions. For each function, loop + * over all of its referrers, accumulating the stack requirements. + * Detect (indirect) recursion with a "mark-and-sweep" algorithm. + * I (mis-)use the "compound" field of the symbol structure for + * the marker, as this field is unused for functions. + * + * Note that the stack is shared with the heap. A host application + * may "eat" cells from the heap as well, through amx_Allot(). The + * stack requirements are thus only an estimate. + */ + long size,maxsize; + int maxparams; + symbol *sym; + + assert(root!=NULL); + assert(recursion!=NULL); + #if !defined NDEBUG + for (sym=root->next; sym!=NULL; sym=sym->next) + if (sym->ident==iFUNCTN) + assert(sym->compound==0); + #endif + + maxsize=0; + maxparams=0; + *recursion=0; /* assume no recursion */ + for (sym=root->next; sym!=NULL; sym=sym->next) { + /* drop out if this is not a user-implemented function */ + if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0) + continue; + /* accumulate stack size for this symbol */ + size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion); + assert(size>=0); + if (maxsizenext; + while (sym!=NULL && sym->compound>=level) { + switch (sym->ident) { + case iLABEL: + if (testlabs) { + if ((sym->usage & uDEFINE)==0) { + error(19,sym->name); /* not a label: ... */ + } else if ((sym->usage & uREAD)==0) { + errorset(sSETPOS,sym->lnumber); + error(203,sym->name); /* symbol isn't used: ... */ + } /* if */ + } /* if */ + break; + case iFUNCTN: + if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) { + funcdisplayname(symname,sym->name); + if (strlen(symname)>0) + error(203,symname); /* symbol isn't used ... (and not public/native/stock) */ + } /* if */ + if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0) + entry=TRUE; /* there is an entry point */ + /* also mark the function to the debug information */ + if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0) + insert_dbgsymbol(sym); + break; + case iCONSTEXPR: + if (testconst && (sym->usage & uREAD)==0) { + errorset(sSETPOS,sym->lnumber); + error(203,sym->name); /* symbol isn't used: ... */ + } /* if */ + break; + default: + /* a variable */ + if (sym->parent!=NULL) + break; /* hierarchical data type */ + if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) { + if (testconst) + errorset(sSETPOS,sym->lnumber); + error(203,sym->name,sym->lnumber); /* symbol isn't used (and not stock) */ + } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) { + errorset(sSETPOS,sym->lnumber); + error(204,sym->name); /* value assigned to symbol is never used */ +#if 0 // ??? not sure whether it is a good idea to force people use "const" + } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) { + errorset(sSETPOS,sym->lnumber); + error(214,sym->name); /* make array argument "const" */ +#endif + } /* if */ + /* also mark the variable (local or global) to the debug information */ + if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0) + insert_dbgsymbol(sym); + } /* if */ + sym=sym->next; + } /* while */ + + return entry; +} + +static cell calc_array_datasize(symbol *sym, cell *offset) +{ + cell length; + + assert(sym!=NULL); + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + length=sym->dim.array.length; + if (sym->dim.array.level > 0) { + cell sublength=calc_array_datasize(finddepend(sym),offset); + if (offset!=NULL) + *offset=length*(*offset+sizeof(cell)); + if (sublength>0) + length*=length*sublength; + else + length=0; + } else { + if (offset!=NULL) + *offset=0; + } /* if */ + return length; +} + +static void destructsymbols(symbol *root,int level) +{ + cell offset=0; + int savepri=FALSE; + symbol *sym=root->next; + while (sym!=NULL && sym->compound>=level) { + if (sym->ident==iVARIABLE || sym->ident==iARRAY) { + char symbolname[16]; + symbol *opsym; + cell elements; + /* check that the '~' operator is defined for this tag */ + operator_symname(symbolname,"~",sym->tag,0,1,0); + if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) { + /* save PRI, in case of a return statment */ + if (!savepri) { + pushreg(sPRI); /* right-hand operand is in PRI */ + savepri=TRUE; + } /* if */ + /* if the variable is an array, get the number of elements */ + if (sym->ident==iARRAY) { + elements=calc_array_datasize(sym,&offset); + /* "elements" can be zero when the variable is declared like + * new mytag: myvar[2][] = { {1, 2}, {3, 4} } + * one should declare all dimensions! + */ + if (elements==0) + error(46,sym->name); /* array size is unknown */ + } else { + elements=1; + offset=0; + } /* if */ + pushval(elements); + /* call the '~' operator */ + address(sym,sPRI); + addconst(offset); /* add offset to array data to the address */ + pushreg(sPRI); + pushval(2*sizeof(cell));/* 2 parameters */ + assert(opsym->ident==iFUNCTN); + ffcall(opsym,NULL,1); + if (sc_status!=statSKIP) + markusage(opsym,uREAD); /* do not mark as "used" when this call itself is skipped */ + if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL) + opsym->x.lib->value += 1; /* increment "usage count" of the library */ + } /* if */ + } /* if */ + sym=sym->next; + } /* while */ + /* restore PRI, if it was saved */ + if (savepri) + popreg(sPRI); +} + +static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val, + int index) +{ + constvalue *cur; + + if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL) + error(103); /* insufficient memory (fatal error) */ + memset(cur,0,sizeof(constvalue)); + if (name!=NULL) { + assert(strlen(name)name,name); + } /* if */ + cur->value=val; + cur->index=index; + cur->next=next; + prev->next=cur; + return cur; +} + +SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index) +{ + constvalue *cur,*prev; + + /* find the end of the constant table */ + for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next) + /* nothing */; + return insert_constval(prev,NULL,name,val,index); +} + +SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index) +{ + constvalue *ptr = table->next; + + while (ptr!=NULL) { + if (strcmp(name,ptr->name)==0 && ptr->index==index) + return ptr; + ptr=ptr->next; + } /* while */ + return NULL; +} + +static constvalue *find_constval_byval(constvalue *table,cell val) +{ + constvalue *ptr = table->next; + + while (ptr!=NULL) { + if (ptr->value==val) + return ptr; + ptr=ptr->next; + } /* while */ + return NULL; +} + +#if 0 /* never used */ +static int delete_constval(constvalue *table,char *name) +{ + constvalue *prev = table; + constvalue *cur = prev->next; + + while (cur!=NULL) { + if (strcmp(name,cur->name)==0) { + prev->next=cur->next; + free(cur); + return TRUE; + } /* if */ + prev=cur; + cur=cur->next; + } /* while */ + return FALSE; +} +#endif + +SC_FUNC void delete_consttable(constvalue *table) +{ + constvalue *cur=table->next, *next; + + while (cur!=NULL) { + next=cur->next; + free(cur); + cur=next; + } /* while */ + memset(table,0,sizeof(constvalue)); +} + +/* add_constant + * + * Adds a symbol to the symbol table. Returns NULL on failure. + */ +SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag) +{ + symbol *sym; + + /* Test whether a global or local symbol with the same name exists. Since + * constants are stored in the symbols table, this also finds previously + * defind constants. */ + sym=findglb(name,sSTATEVAR); + if (!sym) + sym=findloc(name); + if (sym) { + int redef=0; + if (sym->ident!=iCONSTEXPR) + redef=1; /* redefinition a function/variable to a constant is not allowed */ + if ((sym->usage & uENUMFIELD)!=0) { + /* enum field, special case if it has a different tag and the new symbol is also an enum field */ + constvalue *tagid; + symbol *tagsym; + if (sym->tag==tag) + redef=1; /* enumeration field is redefined (same tag) */ + tagid=find_tag_byval(tag); + if (tagid==NULL) { + redef=1; /* new constant does not have a tag */ + } else { + tagsym=findconst(tagid->name,NULL); + if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0) + redef=1; /* new constant is not an enumeration field */ + } /* if */ + /* in this particular case (enumeration field that is part of a different + * enum, and non-conflicting with plain constants) we want to be able to + * redefine it + */ + if (!redef) + goto redef_enumfield; + } else if (sym->tag!=tag) { + redef=1; /* redefinition of a constant (non-enum) to a different tag is not allowed */ + } /* if */ + if (redef) { + error(21,name); /* symbol already defined */ + return NULL; + } else if (sym->addr!=val) { + error(201,name); /* redefinition of constant (different value) */ + sym->addr=val; /* set new value */ + } /* if */ + /* silently ignore redefinitions of constants with the same value & tag */ + return sym; + } /* if */ + + /* constant doesn't exist yet (or is allowed to be redefined) */ +redef_enumfield: + sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE); + assert(sym!=NULL); /* fatal error 103 must be given on error */ + if (sc_status == statIDLE) + sym->usage |= uPREDEF; + return sym; +} + +/* statement - The Statement Parser + * + * This routine is called whenever the parser needs to know what statement + * it encounters (i.e. whenever program syntax requires a statement). + */ +static void statement(int *lastindent,int allow_decl) +{ + int tok; + cell val; + char *st; + + if (!freading) { + error(36); /* empty statement */ + return; + } /* if */ + errorset(sRESET,0); + + tok=lex(&val,&st); + if (tok!='{') { + insert_dbgline(fline); + setline(TRUE); + } /* if */ + /* lex() has set stmtindent */ + if (lastindent!=NULL && tok!=tLABEL) { + if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0) + error(217); /* loose indentation */ + *lastindent=stmtindent; + indent_nowarn=FALSE; /* if warning was blocked, re-enable it */ + } /* if */ + switch (tok) { + case 0: + /* nothing */ + break; + case tNEW: + if (allow_decl) { + declloc(FALSE); + lastst=tNEW; + } else { + error(3); /* declaration only valid in a block */ + } /* if */ + break; + case tSTATIC: + if (allow_decl) { + declloc(TRUE); + lastst=tNEW; + } else { + error(3); /* declaration only valid in a block */ + } /* if */ + break; + case '{': + tok=fline; + if (!matchtoken('}')) /* {} is the empty statement */ + compound(tok==fline); + /* lastst (for "last statement") does not change */ + break; + case ';': + error(36); /* empty statement */ + break; + case tIF: + doif(); + lastst=tIF; + break; + case tWHILE: + dowhile(); + lastst=tWHILE; + break; + case tDO: + dodo(); + lastst=tDO; + break; + case tFOR: + dofor(); + lastst=tFOR; + break; + case tSWITCH: + doswitch(); + lastst=tSWITCH; + break; + case tCASE: + case tDEFAULT: + error(14); /* not in switch */ + break; + case tGOTO: + dogoto(); + lastst=tGOTO; + break; + case tLABEL: + dolabel(); + lastst=tLABEL; + break; + case tRETURN: + doreturn(); + lastst=tRETURN; + break; + case tBREAK: + dobreak(); + lastst=tBREAK; + break; + case tCONTINUE: + docont(); + lastst=tCONTINUE; + break; + case tEXIT: + doexit(); + lastst=tEXIT; + break; + case tASSERT: + doassert(); + lastst=tASSERT; + break; + case tSLEEP: + dosleep(); + lastst=tSLEEP; + break; + case tSTATE: + dostate(); + lastst=tSTATE; + break; + case tCONST: + decl_const(sLOCAL); + break; + case tENUM: + decl_enum(sLOCAL); + break; + default: /* non-empty expression */ + sc_allowproccall=optproccall; + lexpush(); /* analyze token later */ + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); + needtoken(tTERM); + lastst=tEXPR; + sc_allowproccall=FALSE; + } /* switch */ +} + +static void compound(int stmt_sameline) +{ + int indent=-1; + cell save_decl=declared; + int count_stmt=0; + int block_start=fline; /* save line where the compound block started */ + + /* if there is more text on this line, we should adjust the statement indent */ + if (stmt_sameline) { + int i; + const unsigned char *p=lptr; + /* go back to the opening brace */ + while (*p!='{') { + assert(p>pline); + p--; + } /* while */ + assert(*p=='{'); /* it should be found */ + /* go forward, skipping white-space */ + p++; + while (*p<=' ' && *p!='\0') + p++; + assert(*p!='\0'); /* a token should be found */ + stmtindent=0; + for (i=0; i<(int)(p-pline); i++) + if (pline[i]=='\t' && sc_tabsize>0) + stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize); + else + stmtindent++; + } /* if */ + + nestlevel+=1; /* increase compound statement level */ + while (matchtoken('}')==0){ /* repeat until compound statement is closed */ + if (!freading){ + error(30,block_start); /* compound block not closed at end of file */ + break; + } else { + if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE)) + error(225); /* unreachable code */ + statement(&indent,TRUE); /* do a statement */ + count_stmt++; + } /* if */ + } /* while */ + if (lastst!=tRETURN) + destructsymbols(&loctab,nestlevel); + if (lastst!=tRETURN && lastst!=tGOTO) + modstk((int)(declared-save_decl)*sizeof(cell)); /* delete local variable space */ + testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ + declared=save_decl; + delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but + * retain block local labels + * (within the function) */ + nestlevel-=1; /* decrease compound statement level */ +} + +/* doexpr + * + * Global references: stgidx (referred to only) + */ +static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr, + int *tag,symbol **symptr,int chkfuncresult) +{ + int index,ident; + int localstaging=FALSE; + cell val; + + if (!staging) { + stgset(TRUE); /* start stage-buffering */ + localstaging=TRUE; + assert(stgidx==0); + } /* if */ + index=stgidx; + errorset(sEXPRMARK,0); + do { + /* on second round through, mark the end of the previous expression */ + if (index!=stgidx) + markexpr(sEXPR,NULL,0); + sideeffect=FALSE; + ident=expression(&val,tag,symptr,chkfuncresult); + if (!allowarray && (ident==iARRAY || ident==iREFARRAY)) + error(33,"-unknown-"); /* array must be indexed */ + if (chkeffect && !sideeffect) + error(215); /* expression has no effect */ + sc_allowproccall=FALSE; /* cannot use "procedure call" syntax anymore */ + } while (comma && matchtoken(',')); /* more? */ + if (mark_endexpr) + markexpr(sEXPR,NULL,0); /* optionally, mark the end of the expression */ + errorset(sEXPRRELEASE,0); + if (localstaging) { + stgout(index); + stgset(FALSE); /* stop staging */ + } /* if */ + return ident; +} + +/* constexpr + */ +SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr) +{ + int ident,index; + cell cidx; + + stgset(TRUE); /* start stage-buffering */ + stgget(&index,&cidx); /* mark position in code generator */ + errorset(sEXPRMARK,0); + ident=expression(val,tag,symptr,FALSE); + stgdel(index,cidx); /* scratch generated code */ + stgset(FALSE); /* stop stage-buffering */ + if (ident!=iCONSTEXPR) { + error(8); /* must be constant expression */ + if (val!=NULL) + *val=0; + if (tag!=NULL) + *tag=0; + if (symptr!=NULL) + *symptr=NULL; + } /* if */ + errorset(sEXPRRELEASE,0); + return (ident==iCONSTEXPR); +} + +/* test + * + * In the case a "simple assignment" operator ("=") is used within a test, + * the warning "possibly unintended assignment" is displayed. This routine + * sets the global variable "sc_intest" to true, it is restored upon termination. + * In the case the assignment was intended, use parantheses around the + * expression to avoid the warning; primary() sets "sc_intest" to 0. + * + * Global references: sc_intest (altered, but restored upon termination) + */ +static void test(int label,int parens,int invert) +{ + int index,tok; + cell cidx; + int ident,tag; + cell constval; + symbol *sym; + int localstaging=FALSE; + + if (!staging) { + stgset(TRUE); /* start staging */ + localstaging=TRUE; + #if !defined NDEBUG + stgget(&index,&cidx); /* should start at zero if started locally */ + assert(index==0); + #endif + } /* if */ + + PUSHSTK_I(sc_intest); + sc_intest=TRUE; + if (parens) + needtoken('('); + do { + stgget(&index,&cidx); /* mark position (of last expression) in + * code generator */ + ident=expression(&constval,&tag,&sym,TRUE); + tok=matchtoken(','); + if (tok) + markexpr(sEXPR,NULL,0); + } while (tok); /* do */ + if (parens) + needtoken(')'); + if (ident==iARRAY || ident==iREFARRAY) { + char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } /* if */ + if (ident==iCONSTEXPR) { /* constant expression */ + sc_intest=(short)POPSTK_I();/* restore stack */ + stgdel(index,cidx); + if (constval) { /* code always executed */ + error(206); /* redundant test: always non-zero */ + } else { + error(205); /* redundant code: never executed */ + jumplabel(label); + } /* if */ + if (localstaging) { + stgout(0); /* write "jumplabel" code */ + stgset(FALSE); /* stop staging */ + } /* if */ + return; + } /* if */ + if (tag!=0 && tag!=pc_addtag("bool")) + if (check_userop(lneg,tag,0,1,NULL,&tag)) + invert= !invert; /* user-defined ! operator inverted result */ + if (invert) + jmp_ne0(label); /* jump to label if true (different from 0) */ + else + jmp_eq0(label); /* jump to label if false (equal to 0) */ + markexpr(sEXPR,NULL,0); /* end expression (give optimizer a chance) */ + sc_intest=(short)POPSTK_I(); /* double typecast to avoid warning with Microsoft C */ + if (localstaging) { + stgout(0); /* output queue from the very beginning (see + * assert() when localstaging is set to TRUE) */ + stgset(FALSE); /* stop staging */ + } /* if */ +} + +static void doif(void) +{ + int flab1,flab2; + int ifindent; + + ifindent=stmtindent; /* save the indent of the "if" instruction */ + flab1=getlabel(); /* get label number for false branch */ + test(flab1,TRUE,FALSE); /* get expression, branch to flab1 if false */ + statement(NULL,FALSE); /* if true, do a statement */ + if (matchtoken(tELSE)==0){ /* if...else ? */ + setlabel(flab1); /* no, simple if..., print false label */ + } else { + /* to avoid the "dangling else" error, we want a warning if the "else" + * has a lower indent than the matching "if" */ + if (stmtindent0) + error(217); /* loose indentation */ + flab2=getlabel(); + if ((lastst!=tRETURN) && (lastst!=tGOTO)) + jumplabel(flab2); + setlabel(flab1); /* print false label */ + statement(NULL,FALSE); /* do "else" clause */ + setlabel(flab2); /* print true label */ + } /* endif */ +} + +static void dowhile(void) +{ + int wq[wqSIZE]; /* allocate local queue */ + + addwhile(wq); /* add entry to queue for "break" */ + setlabel(wq[wqLOOP]); /* loop label */ + /* The debugger uses the "break" opcode to be able to "break" out of + * a loop. To make sure that each loop has a break opcode, even for the + * tiniest loop, set it below the top of the loop + */ + setline(TRUE); + test(wq[wqEXIT],TRUE,FALSE); /* branch to wq[wqEXIT] if false */ + statement(NULL,FALSE); /* if so, do a statement */ + jumplabel(wq[wqLOOP]); /* and loop to "while" start */ + setlabel(wq[wqEXIT]); /* exit label */ + delwhile(); /* delete queue entry */ +} + +/* + * Note that "continue" will in this case not jump to the top of the loop, but + * to the end: just before the TRUE-or-FALSE testing code. + */ +static void dodo(void) +{ + int wq[wqSIZE],top; + + addwhile(wq); /* see "dowhile" for more info */ + top=getlabel(); /* make a label first */ + setlabel(top); /* loop label */ + statement(NULL,FALSE); + needtoken(tWHILE); + setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */ + setline(TRUE); + test(wq[wqEXIT],TRUE,FALSE); + jumplabel(top); + setlabel(wq[wqEXIT]); + delwhile(); + needtoken(tTERM); +} + +static void dofor(void) +{ + int wq[wqSIZE],skiplab; + cell save_decl; + int save_nestlevel,index; + int *ptr; + + save_decl=declared; + save_nestlevel=nestlevel; + + addwhile(wq); + skiplab=getlabel(); + needtoken('('); + if (matchtoken(';')==0) { + /* new variable declarations are allowed here */ + if (matchtoken(tNEW)) { + /* The variable in expr1 of the for loop is at a + * 'compound statement' level of it own. + */ + nestlevel++; + declloc(FALSE); /* declare local variable */ + } else { + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */ + needtoken(';'); + } /* if */ + } /* if */ + /* Adjust the "declared" field in the "while queue", in case that + * local variables were declared in the first expression of the + * "for" loop. These are deleted in separately, so a "break" or a "continue" + * must ignore these fields. + */ + ptr=readwhile(); + assert(ptr!=NULL); + ptr[wqBRK]=(int)declared; + ptr[wqCONT]=(int)declared; + jumplabel(skiplab); /* skip expression 3 1st time */ + setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */ + setline(TRUE); + /* Expressions 2 and 3 are reversed in the generated code: expression 3 + * precedes expression 2. When parsing, the code is buffered and marks for + * the start of each expression are insterted in the buffer. + */ + assert(!staging); + stgset(TRUE); /* start staging */ + assert(stgidx==0); + index=stgidx; + stgmark(sSTARTREORDER); + stgmark((char)(sEXPRSTART+0)); /* mark start of 2nd expression in stage */ + setlabel(skiplab); /* jump to this point after 1st expression */ + if (matchtoken(';')==0) { + test(wq[wqEXIT],FALSE,FALSE); /* expression 2 (jump to wq[wqEXIT] if false) */ + needtoken(';'); + } /* if */ + stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */ + if (matchtoken(')')==0) { + doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 3 */ + needtoken(')'); + } /* if */ + stgmark(sENDREORDER); /* mark end of reversed evaluation */ + stgout(index); + stgset(FALSE); /* stop staging */ + statement(NULL,FALSE); + jumplabel(wq[wqLOOP]); + setlabel(wq[wqEXIT]); + delwhile(); + + assert(nestlevel>=save_nestlevel); + if (nestlevel>save_nestlevel) { + /* Clean up the space and the symbol table for the local + * variable in "expr1". + */ + destructsymbols(&loctab,nestlevel); + modstk((int)(declared-save_decl)*sizeof(cell)); + testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ + declared=save_decl; + delete_symbols(&loctab,nestlevel,FALSE,TRUE); + nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */ + } /* if */ +} + +/* The switch statement is incompatible with its C sibling: + * 1. the cases are not drop through + * 2. only one instruction may appear below each case, use a compound + * instruction to execute multiple instructions + * 3. the "case" keyword accepts a comma separated list of values to + * match, it also accepts a range using the syntax "1 .. 4" + * + * SWITCH param + * PRI = expression result + * param = table offset (code segment) + * + */ +static void doswitch(void) +{ + int lbl_table,lbl_exit,lbl_case; + int tok,swdefault,casecount; + cell val; + char *str; + constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */ + constvalue *cse,*csp; + char labelname[sNAMEMAX+1]; + + needtoken('('); + doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */ + needtoken(')'); + /* generate the code for the switch statement, the label is the address + * of the case table (to be generated later). + */ + lbl_table=getlabel(); + lbl_case=0; /* just to avoid a compiler warning */ + ffswitch(lbl_table); + + needtoken('{'); + lbl_exit=getlabel(); /* get label number for jumping out of switch */ + swdefault=FALSE; + casecount=0; + do { + tok=lex(&val,&str); /* read in (new) token */ + switch (tok) { + case tCASE: + if (swdefault!=FALSE) + error(15); /* "default" case must be last in switch statement */ + lbl_case=getlabel(); + PUSHSTK_I(sc_allowtags); + sc_allowtags=FALSE; /* do not allow tagnames here */ + do { + casecount++; + + /* ??? enforce/document that, in a switch, a statement cannot start + * with a label. Then, you can search for: + * * the first semicolon (marks the end of a statement) + * * an opening brace (marks the start of a compound statement) + * and search for the right-most colon before that statement + * Now, by replacing the ':' by a special COLON token, you can + * parse all expressions until that special token. + */ + + constexpr(&val,NULL,NULL); + /* Search the insertion point (the table is kept in sorted order, so + * that advanced abstract machines can sift the case table with a + * binary search). Check for duplicate case values at the same time. + */ + for (csp=&caselist, cse=caselist.next; + cse!=NULL && cse->valuenext) + /* nothing */; + if (cse!=NULL && cse->value==val) + error(40,val); /* duplicate "case" label */ + /* Since the label is stored as a string in the "constvalue", the + * size of an identifier must be at least 8, as there are 8 + * hexadecimal digits in a 32-bit number. + */ + #if sNAMEMAX < 8 + #error Length of identifier (sNAMEMAX) too small. + #endif + assert(csp!=NULL); + assert(csp->next==cse); + insert_constval(csp,cse,itoh(lbl_case),val,0); + if (matchtoken(tDBLDOT)) { + cell end; + constexpr(&end,NULL,NULL); + if (end<=val) + error(50); /* invalid range */ + while (++val<=end) { + casecount++; + /* find the new insertion point */ + for (csp=&caselist, cse=caselist.next; + cse!=NULL && cse->valuenext) + /* nothing */; + if (cse!=NULL && cse->value==val) + error(40,val); /* duplicate "case" label */ + assert(csp!=NULL); + assert(csp->next==cse); + insert_constval(csp,cse,itoh(lbl_case),val,0); + } /* if */ + } /* if */ + } while (matchtoken(',')); + needtoken(':'); /* ':' ends the case */ + sc_allowtags=(short)POPSTK_I(); /* reset */ + setlabel(lbl_case); + statement(NULL,FALSE); + jumplabel(lbl_exit); + break; + case tDEFAULT: + if (swdefault!=FALSE) + error(16); /* multiple defaults in switch */ + lbl_case=getlabel(); + setlabel(lbl_case); + needtoken(':'); + swdefault=TRUE; + statement(NULL,FALSE); + /* Jump to lbl_exit, even thouh this is the last clause in the + * switch, because the jump table is generated between the last + * clause of the switch and the exit label. + */ + jumplabel(lbl_exit); + break; + case '}': + /* nothing, but avoid dropping into "default" */ + break; + default: + error(2); + indent_nowarn=TRUE; /* disable this check */ + tok='}'; /* break out of the loop after an error */ + } /* switch */ + } while (tok!='}'); + + #if !defined NDEBUG + /* verify that the case table is sorted (unfortunatly, duplicates can + * occur; there really shouldn't be duplicate cases, but the compiler + * may not crash or drop into an assertion for a user error). */ + for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next) + assert(cse->value <= cse->next->value); + #endif + /* generate the table here, before lbl_exit (general jump target) */ + setlabel(lbl_table); + assert(swdefault==FALSE || swdefault==TRUE); + if (swdefault==FALSE) { + /* store lbl_exit as the "none-matched" label in the switch table */ + strcpy(labelname,itoh(lbl_exit)); + } else { + /* lbl_case holds the label of the "default" clause */ + strcpy(labelname,itoh(lbl_case)); + } /* if */ + ffcase(casecount,labelname,TRUE); + /* generate the rest of the table */ + for (cse=caselist.next; cse!=NULL; cse=cse->next) + ffcase(cse->value,cse->name,FALSE); + + setlabel(lbl_exit); + delete_consttable(&caselist); /* clear list of case labels */ +} + +static void doassert(void) +{ + int flab1,index; + cell cidx; + + if ((sc_debug & sCHKBOUNDS)!=0) { + flab1=getlabel(); /* get label number for "OK" branch */ + test(flab1,FALSE,TRUE); /* get expression and branch to flab1 if true */ + insert_dbgline(fline); /* make sure we can find the correct line number */ + ffabort(xASSERTION); + setlabel(flab1); + } else { + stgset(TRUE); /* start staging */ + stgget(&index,&cidx); /* mark position in code generator */ + do { + expression(NULL,NULL,NULL,FALSE); + stgdel(index,cidx); /* just scrap the code */ + } while (matchtoken(',')); + stgset(FALSE); /* stop staging */ + } /* if */ + needtoken(tTERM); +} + +static void dogoto(void) +{ + char *st; + cell val; + symbol *sym; + + if (lex(&val,&st)==tSYMBOL) { + sym=fetchlab(st); + jumplabel((int)sym->addr); + sym->usage|=uREAD; /* set "uREAD" bit */ + // ??? if the label is defined (check sym->usage & uDEFINE), check + // sym->compound (nesting level of the label) against nestlevel; + // if sym->compound < nestlevel, call the destructor operator + } else { + error(20,st); /* illegal symbol name */ + } /* if */ + needtoken(tTERM); +} + +static void dolabel(void) +{ + char *st; + cell val; + symbol *sym; + + tokeninfo(&val,&st); /* retrieve label name again */ + if (find_constval(&tagname_tab,st,0)!=NULL) + error(221,st); /* label name shadows tagname */ + sym=fetchlab(st); + setlabel((int)sym->addr); + /* since one can jump around variable declarations or out of compound + * blocks, the stack must be manually adjusted + */ + setstk(-declared*sizeof(cell)); + sym->usage|=uDEFINE; /* label is now defined */ +} + +/* fetchlab + * + * Finds a label from the (local) symbol table or adds one to it. + * Labels are local in scope. + * + * Note: The "_usage" bit is set to zero. The routines that call "fetchlab()" + * must set this bit accordingly. + */ +static symbol *fetchlab(char *name) +{ + symbol *sym; + + sym=findloc(name); /* labels are local in scope */ + if (sym){ + if (sym->ident!=iLABEL) + error(19,sym->name); /* not a label: ... */ + } else { + sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0); + assert(sym!=NULL); /* fatal error 103 must be given on error */ + sym->x.declared=(int)declared; + sym->compound=nestlevel; + } /* if */ + return sym; +} + +/* doreturn + * + * Global references: rettype (altered) + */ +static void doreturn(void) +{ + int tag,ident; + int level; + symbol *sym,*sub; + + if (!matchtoken(tTERM)) { + /* "return " */ + if ((rettype & uRETNONE)!=0) + error(78); /* mix "return;" and "return value;" */ + ident=doexpr(TRUE,FALSE,TRUE,TRUE,&tag,&sym,TRUE); + needtoken(tTERM); + /* see if this function already has a sub type (an array attached) */ + sub=finddepend(curfunc); + assert(sub==NULL || sub->ident==iREFARRAY); + if ((rettype & uRETVALUE)!=0) { + int retarray=(ident==iARRAY || ident==iREFARRAY); + /* there was an earlier "return" statement in this function */ + if (sub==NULL && retarray || sub!=NULL && !retarray) + error(79); /* mixing "return array;" and "return value;" */ + if (retarray && (curfunc->usage & uPUBLIC)!=0) + error(90,curfunc->name); /* public function may not return array */ + } /* if */ + rettype|=uRETVALUE; /* function returns a value */ + /* check tagname with function tagname */ + assert(curfunc!=NULL); + if (!matchtag(curfunc->tag,tag,TRUE)) + error(213); /* tagname mismatch */ + if (ident==iARRAY || ident==iREFARRAY) { + int dim[sDIMEN_MAX],numdim; + cell arraysize; + assert(sym!=NULL); + if (sub!=NULL) { + assert(sub->ident==iREFARRAY); + /* this function has an array attached already; check that the current + * "return" statement returns exactly the same array + */ + level=sym->dim.array.level; + if (sub->dim.array.level!=level) { + error(48); /* array dimensions must match */ + } else { + for (numdim=0; numdim<=level; numdim++) { + dim[numdim]=(int)sub->dim.array.length; + if (sym->dim.array.length!=dim[numdim]) + error(47); /* array sizes must match */ + if (numdimdim.array.level; + for (numdim=0; numdim<=level; numdim++) { + dim[numdim]=(int)sub->dim.array.length; + idxtag[numdim]=sub->x.tags.index; + if (numdimname); + } /* for */ + /* the address of the array is stored in a hidden parameter; the address + * of this parameter is 1 + the number of parameters (times the size of + * a cell) + the size of the stack frame and the return address + * base + 0*sizeof(cell) == previous "base" + * base + 1*sizeof(cell) == function return address + * base + 2*sizeof(cell) == number of arguments + * base + 3*sizeof(cell) == first argument of the function + * ... + * base + ((n-1)+3)*sizeof(cell) == last argument of the function + * base + (n+3)*sizeof(cell) == hidden parameter with array address + */ + assert(curfunc!=NULL); + assert(curfunc->dim.arglist!=NULL); + for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++) + /* nothing */; + sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag); + sub->parent=curfunc; + } /* if */ + /* get the hidden parameter, copy the array (the array is on the heap; + * it stays on the heap for the moment, and it is removed -usually- at + * the end of the expression/statement, see expression() in SC3.C) + */ + address(sub,sALT); /* ALT = destination */ + arraysize=calc_arraysize(dim,numdim,0); + memcopy(arraysize*sizeof(cell)); /* source already in PRI */ + /* moveto1(); is not necessary, callfunction() does a popreg() */ + } /* if */ + } else { + /* this return statement contains no expression */ + ldconst(0,sPRI); + if ((rettype & uRETVALUE)!=0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + assert(curfunc!=NULL); + funcdisplayname(symname,curfunc->name); + error(209,symname); /* function should return a value */ + } /* if */ + rettype|=uRETNONE; /* function does not return anything */ + } /* if */ + destructsymbols(&loctab,0); /* call destructor for *all* locals */ + modstk((int)declared*sizeof(cell)); /* end of function, remove *all* + * local variables */ + ffret(strcmp(curfunc->name,uENTRYFUNC)!=0); +} + +static void dobreak(void) +{ + int *ptr; + + ptr=readwhile(); /* readwhile() gives an error if not in loop */ + needtoken(tTERM); + if (ptr==NULL) + return; + destructsymbols(&loctab,nestlevel); + modstk(((int)declared-ptr[wqBRK])*sizeof(cell)); + jumplabel(ptr[wqEXIT]); +} + +static void docont(void) +{ + int *ptr; + + ptr=readwhile(); /* readwhile() gives an error if not in loop */ + needtoken(tTERM); + if (ptr==NULL) + return; + destructsymbols(&loctab,nestlevel); + modstk(((int)declared-ptr[wqCONT])*sizeof(cell)); + jumplabel(ptr[wqLOOP]); +} + +SC_FUNC void exporttag(int tag) +{ + /* find the tag by value in the table, then set the top bit to mark it + * "public" + */ + if (tag!=0 && (tag & PUBLICTAG)==0) { + constvalue *ptr; + for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next) + /* nothing */; + if (ptr!=NULL) + ptr->value |= PUBLICTAG; + } /* if */ +} + +static void doexit(void) +{ + int tag=0; + + if (matchtoken(tTERM)==0){ + doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE); + needtoken(tTERM); + } else { + ldconst(0,sPRI); + } /* if */ + ldconst(tag,sALT); + exporttag(tag); + destructsymbols(&loctab,0); /* call destructor for *all* locals */ + ffabort(xEXIT); +} + +static void dosleep(void) +{ + int tag=0; + + if (matchtoken(tTERM)==0){ + doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE); + needtoken(tTERM); + } else { + ldconst(0,sPRI); + } /* if */ + ldconst(tag,sALT); + exporttag(tag); + ffabort(xSLEEP); + + /* for stack usage checking, mark the use of the sleep instruction */ + pc_memflags |= suSLEEP_INSTR; +} + +static void dostate(void) +{ + char name[sNAMEMAX+1]; + constvalue *automaton; + constvalue *state; + constvalue *stlist; + int flabel; + symbol *sym; + #if !defined SC_LIGHT + int length,index,listid,listindex,stateindex; + char *doc; + #endif + + /* check for an optional condition */ + if (matchtoken('(')) { + flabel=getlabel(); /* get label number for "false" branch */ + pc_docexpr=TRUE; /* attach expression as a documentation string */ + test(flabel,FALSE,FALSE); /* get expression, branch to flabel if false */ + pc_docexpr=FALSE; + needtoken(')'); + } else { + flabel=-1; + } /* if */ + + if (!sc_getstateid(&automaton,&state)) { + delete_autolisttable(); + return; + } /* if */ + needtoken(tTERM); + + /* store the new state id */ + assert(state!=NULL); + ldconst(state->value,sPRI); + assert(automaton!=NULL); + assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0); + storereg(automaton->value,sPRI); + + /* find the optional entry() function for the state */ + sym=findglb(uENTRYFUNC,sGLOBAL); + if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) { + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + assert(strlen(stlist->name)!=0); + if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value)) + break; /* found! */ + } /* for */ + assert(stlist==NULL || state_inlist(stlist->index,state->value)); + if (stlist!=NULL) { + /* the label to jump to is in stlist->name */ + ffcall(sym,stlist->name,0); + } /* if */ + } /* if */ + + if (flabel>=0) + setlabel(flabel); /* condition was false, jump around the state switch */ + + #if !defined SC_LIGHT + /* mark for documentation */ + if (sc_status==statFIRST) { + char *str; + /* get the last list id attached to the function, this contains the source states */ + assert(curfunc!=NULL); + if (curfunc->states!=NULL) { + stlist=curfunc->states->next; + assert(stlist!=NULL); + while (stlist->next!=NULL) + stlist=stlist->next; + listid=stlist->index; + } else { + listid=-1; + } /* if */ + listindex=0; + length=strlen(name)+70; /* +70 for the fixed part "\n" */ + /* see if there are any condition strings to attach */ + for (index=0; (str=get_autolist(index))!=NULL; index++) + length+=strlen(str); + if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) { + do { + sprintf(doc,"=0) { + /* get the source state */ + stateindex=state_listitem(listid,listindex); + state=state_findid(stateindex); + assert(state!=NULL); + sprintf(doc+strlen(doc)," source=\"%s\"",state->name); + } /* if */ + if (get_autolist(0)!=NULL) { + /* add the condition */ + strcat(doc," condition=\""); + for (index=0; (str=get_autolist(index))!=NULL; index++) { + /* remove the ')' token that may be appended before detecting that the expression has ended */ + if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL) + strcat(doc,str); + } /* for */ + strcat(doc,"\""); + } /* if */ + strcat(doc,"/>\n"); + insert_docstring(doc); + } while (listid>=0 && ++listindex=(wq+wqTABSZ-wqSIZE)) + error(102,"loop table"); /* loop table overflow (too many active loops)*/ + k=0; + while (kwq) + wqptr-=wqSIZE; +} + +static int *readwhile(void) +{ + if (wqptr<=wq){ + error(24); /* out of context */ + return NULL; + } else { + return (wqptr-wqSIZE); + } /* if */ +} + diff --git a/compiler-init/sc2.c b/compiler-init/sc2.c new file mode 100644 index 00000000..0e399a70 --- /dev/null +++ b/compiler-init/sc2.c @@ -0,0 +1,2801 @@ +/* Pawn compiler - File input, preprocessing and lexical analysis functions + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc2.c 3590 2006-06-24 14:16:39Z thiadmer $ + */ +#include +#include +#include +#include +#include +#include +#include "lstring.h" +#include "sc.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + +#if defined FORTIFY + #include +#endif + +/* flags for litchar() */ +#define RAWMODE 1 +#define UTF8MODE 2 +static cell litchar(const unsigned char **lptr,int flags); +static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag); + +static void substallpatterns(unsigned char *line,int buffersize); +static int match(char *st,int end); +static int alpha(char c); + +#define SKIPMODE 1 /* bit field in "#if" stack */ +#define PARSEMODE 2 /* bit field in "#if" stack */ +#define HANDLED_ELSE 4 /* bit field in "#if" stack */ +#define SKIPPING (skiplevel>0 && (ifstack[skiplevel-1] & SKIPMODE)==SKIPMODE) + +static short icomment; /* currently in multiline comment? */ +static char ifstack[sCOMP_STACK]; /* "#if" stack */ +static short iflevel; /* nesting level if #if/#else/#endif */ +static short skiplevel; /* level at which we started skipping (including nested #if .. #endif) */ +static unsigned char term_expr[] = ""; +static int listline=-1; /* "current line" for the list file */ + + +/* pushstk & popstk + * + * Uses a LIFO stack to store information. The stack is used by doinclude(), + * doswitch() (to hold the state of "swactive") and some other routines. + * + * Porting note: I made the bold assumption that an integer will not be + * larger than a pointer (it may be smaller). That is, the stack element + * is typedef'ed as a pointer type, but I also store integers on it. See + * SC.H for "stkitem" + * + * Global references: stack,stkidx,stktop (private to pushstk(), popstk() + * and clearstk()) + */ +static stkitem *stack=NULL; +static int stkidx=0,stktop=0; + +SC_FUNC void pushstk(stkitem val) +{ + assert(stkidx<=stktop); + if (stkidx==stktop) { + stkitem *newstack; + int newsize= (stktop==0) ? 16 : 2*stktop; + /* try to resize the stack */ + assert(newsize>stktop); + newstack=(stkitem*)malloc(newsize*sizeof(stkitem)); + if (newstack==NULL) + error(102,"parser stack"); /* stack overflow (recursive include?) */ + /* swap the stacks */ + memcpy(newstack,stack,stkidx*sizeof(stkitem)); + if (stack!=NULL) + free(stack); + stack=newstack; + stktop=newsize; + } /* if */ + assert(stkidx'); /* termination character */ + lptr++; + while (*lptr<=' ' && *lptr!='\0') /* skip whitespace after quote */ + lptr++; + } else { + c='\0'; + } /* if */ + + i=0; + while (*lptr!=c && *lptr!='\0' && i0 && name[i-1]<=' ') + i--; /* strip trailing whitespace */ + assert(i>=0 && i are only read from the list of include directories. + */ + result=plungefile(name,(c!='>'),TRUE); + if (result) + add_constant(symname,1,sGLOBAL,0); + else if (!silent) + error(100,name); /* cannot read from ... (fatal error) */ + } /* if */ +} + +/* readline + * + * Reads in a new line from the input file pointed to by "inpf". readline() + * concatenates lines that end with a \ with the next line. If no more data + * can be read from the file, readline() attempts to pop off the previous file + * from the stack. If that fails too, it sets "freading" to 0. + * + * Global references: inpf,fline,inpfname,freading,icomment (altered) + */ +static void readline(unsigned char *line) +{ + int i,num,cont; + unsigned char *ptr; + + if (lptr==term_expr) + return; + num=sLINEMAX; + cont=FALSE; + do { + if (inpf==NULL || pc_eofsrc(inpf)) { + if (cont) + error(49); /* invalid line continuation */ + if (inpf!=NULL && inpf!=inpf_org) + pc_closesrc(inpf); + i=POPSTK_I(); + if (i==-1) { /* All's done; popstk() returns "stack is empty" */ + freading=FALSE; + *line='\0'; + /* when there is nothing more to read, the #if/#else stack should + * be empty and we should not be in a comment + */ + assert(iflevel>=0); + if (iflevel>0) + error(1,"#endif","-end of file-"); + else if (icomment!=0) + error(1,"*/","-end of file-"); + return; + } /* if */ + fline=i; + fcurrent=(short)POPSTK_I(); + icomment=(short)POPSTK_I(); + sc_is_utf8=(short)POPSTK_I(); + iflevel=(short)POPSTK_I(); + skiplevel=iflevel; /* this condition held before including the file */ + assert(!SKIPPING); /* idem ditto */ + curlibrary=(constvalue *)POPSTK_P(); + free(inpfname); /* return memory allocated for the include file name */ + inpfname=(char *)POPSTK_P(); + inpf=(FILE *)POPSTK_P(); + insert_dbgfile(inpfname); + setfiledirect(inpfname); + listline=-1; /* force a #line directive when changing the file */ + } /* if */ + + if (pc_readsrc(inpf,line,num)==NULL) { + *line='\0'; /* delete line */ + cont=FALSE; + } else { + /* check whether to erase leading spaces */ + if (cont) { + unsigned char *ptr=line; + while (*ptr<=' ' && *ptr!='\0') + ptr++; + if (ptr!=line) + memmove(line,ptr,strlen((char*)ptr)+1); + } /* if */ + cont=FALSE; + /* check whether a full line was read */ + if (strchr((char*)line,'\n')==NULL && !pc_eofsrc(inpf)) + error(75); /* line too long */ + /* check if the next line must be concatenated to this line */ + if ((ptr=(unsigned char*)strchr((char*)line,'\n'))==NULL) + ptr=(unsigned char*)strchr((char*)line,'\r'); + if (ptr!=NULL && ptr>line) { + assert(*(ptr+1)=='\0'); /* '\n' or '\r' should be last in the string */ + while (ptr>line && *ptr<=' ') + ptr--; /* skip trailing whitespace */ + if (*ptr=='\\') { + cont=TRUE; + /* set '\a' at the position of '\\' to make it possible to check + * for a line continuation in a single line comment (error 49) + */ + *ptr++='\a'; + *ptr='\0'; /* erase '\n' (and any trailing whitespace) */ + } /* if */ + } /* if */ + num-=strlen((char*)line); + line+=strlen((char*)line); + } /* if */ + fline+=1; + } while (num>=0 && cont); +} + +/* stripcom + * + * Replaces all comments from the line by space characters. It updates + * a global variable ("icomment") for multiline comments. + * + * This routine also supports the C++ extension for single line comments. + * These comments are started with "//" and end at the end of the line. + * + * The function also detects (and manages) "documentation comments". The + * global variable "icomment" is set to 2 for documentation comments. + * + * Global references: icomment (private to "stripcom") + */ +static void stripcom(unsigned char *line) +{ + char c; + #if !defined SC_LIGHT + #define COMMENT_LIMIT 100 + #define COMMENT_MARGIN 40 /* length of the longest word */ + char comment[COMMENT_LIMIT+COMMENT_MARGIN]; + int commentidx=0; + int skipstar=TRUE; + static int prev_singleline=FALSE; + int singleline=prev_singleline; + + prev_singleline=FALSE; /* preset */ + #endif + + while (*line){ + if (icomment!=0) { + if (*line=='*' && *(line+1)=='/') { + #if !defined SC_LIGHT + if (icomment==2) { + assert(commentidx0) + insert_docstring(comment); + } /* if */ + #endif + icomment=0; /* comment has ended */ + *line=' '; /* replace '*' and '/' characters by spaces */ + *(line+1)=' '; + line+=2; + } else { + if (*line=='/' && *(line+1)=='*') + error(216); /* nested comment */ + #if !defined SC_LIGHT + /* collect the comment characters in a string */ + if (icomment==2) { + if (skipstar && (*line!='\0' && *line<=' ' || *line=='*')) { + /* ignore leading whitespace and '*' characters */ + } else if (commentidxCOMMENT_LIMIT && *line!='\0' && *line<=' ') { + comment[commentidx]='\0'; + insert_docstring(comment); + commentidx=0; + } /* if */ + skipstar=FALSE; + } /* if */ + } /* if */ + #endif + *line=' '; /* replace comments by spaces */ + line+=1; + } /* if */ + } else { + if (*line=='/' && *(line+1)=='*'){ + icomment=1; /* start comment */ + #if !defined SC_LIGHT + /* there must be two "*" behind the slash and then white space */ + if (*(line+2)=='*' && *(line+3)<=' ') { + /* if we are not in a function, we must attach the previous block + * to the global documentation + */ + if (curfunc==NULL && get_docstring(0)!=NULL) + sc_attachdocumentation(NULL); + icomment=2; /* documentation comment */ + } /* if */ + commentidx=0; + skipstar=TRUE; + #endif + *line=' '; /* replace '/' and '*' characters by spaces */ + *(line+1)=' '; + line+=2; + if (icomment==2) + *line++=' '; + } else if (*line=='/' && *(line+1)=='/'){ /* comment to end of line */ + if (strchr((char*)line,'\a')!=NULL) + error(49); /* invalid line continuation */ + #if !defined SC_LIGHT + if (*(line+2)=='/' && *(line+3)<=' ') { + /* documentation comment */ + char *str=(char*)line+3; + char *end; + while (*str<=' ' && *str!='\0') + str++; /* skip leading whitespace */ + if ((end=strrchr(str,'\n'))!=NULL) + *end='\0';/* erase trailing '\n' */ + /* if there is a disjunct block, we may need to attach the previous + * block to the global documentation + */ + if (!singleline && curfunc==NULL && get_docstring(0)!=NULL) + sc_attachdocumentation(NULL); + insert_docstring(str); + prev_singleline=TRUE; + } /* if */ + #endif + *line++='\n'; /* put "newline" at first slash */ + *line='\0'; /* put "zero-terminator" at second slash */ + } else { + if (*line=='\"' || *line=='\''){ /* leave literals unaltered */ + c=*line; /* ending quote, single or double */ + line+=1; + while ((*line!=c || *(line-1)==sc_ctrlchar) && *line!='\0') + line+=1; + line+=1; /* skip final quote */ + } else { + line+=1; + } /* if */ + } /* if */ + } /* if */ + } /* while */ + #if !defined SC_LIGHT + if (icomment==2) { + assert(commentidx0) + insert_docstring(comment); + } /* if */ + #endif +} + +/* btoi + * + * Attempts to interpret a numeric symbol as a boolean value. On success + * it returns the number of characters processed (so the line pointer can be + * adjusted) and the value is stored in "val". Otherwise it returns 0 and + * "val" is garbage. + * + * A boolean value must start with "0b" + */ +static int btoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + + *val=0; + ptr=curptr; + if (*ptr=='0' && *(ptr+1)=='b') { + ptr+=2; + while (*ptr=='0' || *ptr=='1' || *ptr=='_') { + if (*ptr!='_') + *val=(*val<<1) | (*ptr-'0'); + ptr++; + } /* while */ + } else { + return 0; + } /* if */ + if (alphanum(*ptr)) /* number must be delimited by non-alphanumeric char */ + return 0; + else + return (int)(ptr-curptr); +} + +/* dtoi + * + * Attempts to interpret a numeric symbol as a decimal value. On success + * it returns the number of characters processed and the value is stored in + * "val". Otherwise it returns 0 and "val" is garbage. + */ +static int dtoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + + *val=0; + ptr=curptr; + if (!isdigit(*ptr)) /* should start with digit */ + return 0; + while (isdigit(*ptr) || *ptr=='_') { + if (*ptr!='_') + *val=(*val*10)+(*ptr-'0'); + ptr++; + } /* while */ + if (alphanum(*ptr)) /* number must be delimited by non-alphanumerical */ + return 0; + if (*ptr=='.' && isdigit(*(ptr+1))) + return 0; /* but a fractional part must not be present */ + return (int)(ptr-curptr); +} + +/* htoi + * + * Attempts to interpret a numeric symbol as a hexadecimal value. On + * success it returns the number of characters processed and the value is + * stored in "val". Otherwise it return 0 and "val" is garbage. + */ +static int htoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + + *val=0; + ptr=curptr; + if (!isdigit(*ptr)) /* should start with digit */ + return 0; + if (*ptr=='0' && *(ptr+1)=='x') { /* C style hexadecimal notation */ + ptr+=2; + while (ishex(*ptr) || *ptr=='_') { + if (*ptr!='_') { + assert(ishex(*ptr)); + *val= *val<<4; + if (isdigit(*ptr)) + *val+= (*ptr-'0'); + else + *val+= (tolower(*ptr)-'a'+10); + } /* if */ + ptr++; + } /* while */ + } else { + return 0; + } /* if */ + if (alphanum(*ptr)) + return 0; + else + return (int)(ptr-curptr); +} + +#if defined __GNUC__ +static double pow10(int value) +{ + double res=1.0; + while (value>=4) { + res*=10000.0; + value-=5; + } /* while */ + while (value>=2) { + res*=100.0; + value-=2; + } /* while */ + while (value>=1) { + res*=10.0; + value-=1; + } /* while */ + return res; +} +#endif + +/* ftoi + * + * Attempts to interpret a numeric symbol as a rational number, either as + * IEEE 754 single/double precision floating point or as a fixed point integer. + * On success it returns the number of characters processed and the value is + * stored in "val". Otherwise it returns 0 and "val" is unchanged. + * + * Pawn has stricter definition for rational numbers than most: + * o the value must start with a digit; ".5" is not a valid number, you + * should write "0.5" + * o a period must appear in the value, even if an exponent is given; "2e3" + * is not a valid number, you should write "2.0e3" + * o at least one digit must follow the period; "6." is not a valid number, + * you should write "6.0" + */ +static int ftoi(cell *val,const unsigned char *curptr) +{ + const unsigned char *ptr; + double fnum,ffrac,fmult; + unsigned long dnum,dbase; + int i, ignore; + + assert(rational_digits>=0 && rational_digits<9); + for (i=0,dbase=1; i0 && !ignore) { + error(222); /* number of digits exceeds rational number precision */ + ignore=TRUE; + } /* if */ + } /* if */ + ptr++; + } /* while */ + fnum += ffrac*fmult; /* form the number so far */ + if (*ptr=='e') { /* optional fractional part */ + int exp,sign; + ptr++; + if (*ptr=='-') { + sign=-1; + ptr++; + } else { + sign=1; + } /* if */ + if (!isdigit(*ptr)) /* 'e' should be followed by a digit */ + return 0; + exp=0; + while (isdigit(*ptr)) { + exp=(exp*10)+(*ptr-'0'); + ptr++; + } /* while */ + #if defined __GNUC__ + fmult=pow10(exp*sign); + #else + fmult=pow(10,exp*sign); + #endif + fnum *= fmult; + dnum *= (unsigned long)(fmult+0.5); + } /* if */ + + /* decide how to store the number */ + if (sc_rationaltag==0) { + error(70); /* rational number support was not enabled */ + *val=0; + } else if (rational_digits==0) { + /* floating point */ + #if PAWN_CELL_SIZE==32 + float value=(float)fnum; + *val=*((cell *)&value); + #if !defined NDEBUG + /* I assume that the C/C++ compiler stores "float" values in IEEE 754 + * format (as mandated in the ANSI standard). Test this assumption + * anyway. + * Note: problems have been reported with GCC 3.2.x, version 3.3.x works. + */ + { float test1 = 0.0, test2 = 50.0, test3 = -50.0; + uint32_t bit = 1; + /* test 0.0 == all bits 0 */ + assert(*(uint32_t*)&test1==0x00000000L); + /* test sign & magnitude format */ + assert(((*(uint32_t*)&test2) ^ (*(uint32_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1))); + /* test a known value */ + assert(*(uint32_t*)&test2==0x42480000L); + } + #endif + #elif PAWN_CELL_SIZE==64 + *val=*((cell *)&fnum); + #if !defined NDEBUG + /* I assume that the C/C++ compiler stores "double" values in IEEE 754 + * format (as mandated in the ANSI standard). + */ + { float test1 = 0.0, test2 = 50.0, test3 = -50.0; + uint64_t bit = 1; + /* test 0.0 == all bits 0 */ + assert(*(uint64_t*)&test1==0x00000000L); + /* test sign & magnitude format */ + assert(((*(uint64_t*)&test2) ^ (*(uint64_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1))); + } + #endif + #else + #error Unsupported cell size + #endif + } else { + /* fixed point */ + *val=(cell)dnum; + } /* if */ + + return (int)(ptr-curptr); +} + +/* number + * + * Reads in a number (binary, decimal or hexadecimal). It returns the number + * of characters processed or 0 if the symbol couldn't be interpreted as a + * number (in this case the argument "val" remains unchanged). This routine + * relies on the 'early dropout' implementation of the logical or (||) + * operator. + * + * Note: the routine doesn't check for a sign (+ or -). The - is checked + * for at "hier2()" (in fact, it is viewed as an operator, not as a + * sign) and the + is invalid (as in K&R C, and unlike ANSI C). + */ +static int number(cell *val,const unsigned char *curptr) +{ + int i; + cell value; + + if ((i=btoi(&value,curptr))!=0 /* binary? */ + || (i=htoi(&value,curptr))!=0 /* hexadecimal? */ + || (i=dtoi(&value,curptr))!=0) /* decimal? */ + { + *val=value; + return i; + } else { + return 0; /* else not a number */ + } /* if */ +} + +static void chrcat(char *str,char chr) +{ + str=strchr(str,'\0'); + *str++=chr; + *str='\0'; +} + +static int preproc_expr(cell *val,int *tag) +{ + int result; + int index; + cell code_index; + char *term; + + /* Disable staging; it should be disabled already because + * expressions may not be cut off half-way between conditional + * compilations. Reset the staging index, but keep the code + * index. + */ + if (stgget(&index,&code_index)) { + error(57); /* unfinished expression */ + stgdel(0,code_index); + stgset(FALSE); + } /* if */ + assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must point inside the string */ + #if !defined NO_DEFINE + /* preprocess the string */ + substallpatterns(pline,sLINEMAX); + assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must STILL point inside the string */ + #endif + /* append a special symbol to the string, so the expression + * analyzer won't try to read a next line when it encounters + * an end-of-line + */ + assert(strlen((char*)pline)=0); + if (iflevel>=sCOMP_STACK) + error(102,"Conditional compilation stack"); /* table overflow */ + iflevel++; + if (SKIPPING) + break; /* break out of switch */ + skiplevel=iflevel; + preproc_expr(&val,NULL); /* get value (or 0 on error) */ + ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE); + check_empty(lptr); + break; + case tpELSE: + case tpELSEIF: + ret=CMD_IF; + assert(iflevel>=0); + if (iflevel==0) { + error(26); /* no matching #if */ + errorset(sRESET,0); + } else { + /* check for earlier #else */ + if ((ifstack[iflevel-1] & HANDLED_ELSE)==HANDLED_ELSE) { + if (tok==tpELSEIF) + error(61); /* #elseif directive may not follow an #else */ + else + error(60); /* multiple #else directives between #if ... #endif */ + errorset(sRESET,0); + } else { + assert(iflevel>0); + /* if there has been a "parse mode" on this level, set "skip mode", + * otherwise, clear "skip mode" + */ + if ((ifstack[iflevel-1] & PARSEMODE)==PARSEMODE) { + /* there has been a parse mode already on this level, so skip the rest */ + ifstack[iflevel-1] |= (char)SKIPMODE; + } else { + /* previous conditions were all FALSE */ + if (tok==tpELSEIF) { + /* get new expression */ + preproc_expr(&val,NULL); /* get value (or 0 on error) */ + ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE); + } else { + /* a simple #else, clear skip mode */ + ifstack[iflevel-1] &= (char)~SKIPMODE; + } /* if */ + } /* if */ + } /* if */ + } /* if */ + check_empty(lptr); + break; + case tpENDIF: + ret=CMD_IF; + if (iflevel==0){ + error(26); /* no matching "#if" */ + errorset(sRESET,0); + } else { + iflevel--; + if (iflevel0) { + free(inpfname); + inpfname=duplicatestring(pathname); + if (inpfname==NULL) + error(103); /* insufficient memory */ + } /* if */ + } /* if */ + check_empty(lptr); + break; + case tpLINE: + if (!SKIPPING) { + if (lex(&val,&str)!=tNUMBER) + error(8); /* invalid/non-constant expression */ + fline=(int)val; + } /* if */ + check_empty(lptr); + break; + case tpASSERT: + if (!SKIPPING && (sc_debug & sCHKBOUNDS)!=0) { + for (str=(char*)lptr; *str<=' ' && *str!='\0'; str++) + /* nothing */; /* save start of expression */ + preproc_expr(&val,NULL); /* get constant expression (or 0 on error) */ + if (!val) + error(110,str); /* assertion failed */ + check_empty(lptr); + } /* if */ + break; + case tpPRAGMA: + if (!SKIPPING) { + if (lex(&val,&str)==tSYMBOL) { + if (strcmp(str,"amxlimit")==0) { + preproc_expr(&pc_amxlimit,NULL); + } else if (strcmp(str,"amxram")==0) { + preproc_expr(&pc_amxram,NULL); + } else if (strcmp(str,"codepage")==0) { + char name[sNAMEMAX+1]; + while (*lptr<=' ' && *lptr!='\0') + lptr++; + if (*lptr=='"') { + lptr=getstring((unsigned char*)name,sizeof name,lptr); + } else { + int i; + for (i=0; i9) { + error(68); /* invalid rational number precision */ + digits=0; + } /* if */ + if (*lptr==')') + lptr++; + } /* if */ + /* add the tag (make it public) and check the values */ + i=pc_addtag(name); + exporttag(i); + if (sc_rationaltag==0 || (sc_rationaltag==i && rational_digits==(int)digits)) { + sc_rationaltag=i; + rational_digits=(int)digits; + } else { + error(69); /* rational number format already set, can only be set once */ + } /* if */ + } else if (strcmp(str,"semicolon")==0) { + cell val; + preproc_expr(&val,NULL); + sc_needsemicolon=(int)val; + } else if (strcmp(str,"tabsize")==0) { + cell val; + preproc_expr(&val,NULL); + sc_tabsize=(int)val; + } else if (strcmp(str,"align")==0) { + sc_alignnext=TRUE; + } else if (strcmp(str,"unused")==0) { + char name[sNAMEMAX+1]; + int i,comma; + symbol *sym; + do { + /* get the name */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + for (i=0; iusage |= uREAD; + if (sym->ident==iVARIABLE || sym->ident==iREFERENCE + || sym->ident==iARRAY || sym->ident==iREFARRAY) + sym->usage |= uWRITTEN; + } else { + error(17,name); /* undefined symbol */ + } /* if */ + /* see if a comma follows the name */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + comma= (*lptr==','); + if (comma) + lptr++; + } while (comma); + } else { + error(207); /* unknown #pragma */ + } /* if */ + } else { + error(207); /* unknown #pragma */ + } /* if */ + check_empty(lptr); + } /* if */ + break; + case tpENDINPUT: + case tpENDSCRPT: + if (!SKIPPING) { + check_empty(lptr); + assert(inpf!=NULL); + if (inpf!=inpf_org) + pc_closesrc(inpf); + inpf=NULL; + } /* if */ + break; +#if !defined NOEMIT + case tpEMIT: { + /* write opcode to output file */ + char name[40]; + int i; + while (*lptr<=' ' && *lptr!='\0') + lptr++; + for (i=0; i<40 && (isalpha(*lptr) || *lptr=='.'); i++,lptr++) + name[i]=(char)tolower(*lptr); + name[i]='\0'; + stgwrite("\t"); + stgwrite(name); + stgwrite(" "); + code_idx+=opcodes(1); + /* write parameter (if any) */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + if (*lptr!='\0') { + symbol *sym; + tok=lex(&val,&str); + switch (tok) { + case tNUMBER: + case tRATIONAL: + outval(val,FALSE); + code_idx+=opargs(1); + break; + case tSYMBOL: + sym=findloc(str); + if (sym==NULL) + sym=findglb(str,sSTATEVAR); + if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) { + error(17,str); /* undefined symbol */ + } else { + outval(sym->addr,FALSE); + /* mark symbol as "used", unknown whether for read or write */ + markusage(sym,uREAD | uWRITTEN); + code_idx+=opargs(1); + } /* if */ + break; + default: { + char s2[20]; + extern char *sc_tokens[];/* forward declaration */ + if (tok<256) + sprintf(s2,"%c",(char)tok); + else + strcpy(s2,sc_tokens[tok-tFIRST]); + error(1,sc_tokens[tSYMBOL-tFIRST],s2); + break; + } /* case */ + } /* switch */ + } /* if */ + stgwrite("\n"); + check_empty(lptr); + break; + } /* case */ +#endif +#if !defined NO_DEFINE + case tpDEFINE: { + ret=CMD_DEFINE; + if (!SKIPPING) { + char *pattern,*substitution; + const unsigned char *start,*end; + int count,prefixlen; + stringpair *def; + /* find the pattern to match */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + start=lptr; /* save starting point of the match pattern */ + count=0; + while (*lptr>' ' && *lptr!='\0') { + litchar(&lptr,0); /* litchar() advances "lptr" and handles escape characters */ + count++; + } /* while */ + end=lptr; + /* check pattern to match */ + if (!alpha(*start)) { + error(74); /* pattern must start with an alphabetic character */ + break; + } /* if */ + /* store matched pattern */ + pattern=(char*)malloc(count+1); + if (pattern==NULL) + error(103); /* insufficient memory */ + lptr=start; + count=0; + while (lptr!=end) { + assert(lptr=2 && isdigit(pattern[count-1]) && pattern[count-2]=='%') + pattern[count-2]='\0'; + /* find substitution string */ + while (*lptr<=' ' && *lptr!='\0') + lptr++; + start=lptr; /* save starting point of the match pattern */ + count=0; + end=NULL; + while (*lptr!='\0') { + /* keep position of the start of trailing whitespace */ + if (*lptr<=' ') { + if (end==NULL) + end=lptr; + } else { + end=NULL; + } /* if */ + count++; + lptr++; + } /* while */ + if (end==NULL) + end=lptr; + /* store matched substitution */ + substitution=(char*)malloc(count+1); /* +1 for '\0' */ + if (substitution==NULL) + error(103); /* insufficient memory */ + lptr=start; + count=0; + while (lptr!=end) { + assert(lptr0); + if ((def=find_subst(pattern,prefixlen))!=NULL) { + if (strcmp(def->first,pattern)!=0 || strcmp(def->second,substitution)!=0) + error(201,pattern); /* redefinition of macro (non-identical) */ + delete_subst(pattern,prefixlen); + } /* if */ + /* add the pattern/substitution pair to the list */ + assert(strlen(pattern)>0); + insert_subst(pattern,substitution,prefixlen); + free(pattern); + free(substitution); + } /* if */ + break; + } /* case */ + case tpUNDEF: + if (!SKIPPING) { + if (lex(&val,&str)==tSYMBOL) { + ret=delete_subst(str,strlen(str)); + if (!ret) { + /* also undefine normal constants */ + symbol *sym=findconst(str,NULL); + if (sym!=NULL && (sym->usage & (uENUMROOT | uENUMFIELD))==0) { + delete_symbol(&glbtab,sym); + ret=TRUE; + } /* if */ + } /* if */ + if (!ret) + error(17,str); /* undefined symbol */ + } else { + error(20,str); /* invalid symbol name */ + } /* if */ + check_empty(lptr); + } /* if */ + break; +#endif + case tpERROR: + while (*lptr<=' ' && *lptr!='\0') + lptr++; + if (!SKIPPING) + error(111,lptr); /* user error */ + break; + default: + error(31); /* unknown compiler directive */ + ret=SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* process as normal line */ + } /* switch */ + return ret; +} + +#if !defined NO_DEFINE +static int is_startstring(const unsigned char *string) +{ + if (*string=='\"' || *string=='\'') + return TRUE; /* "..." */ + + if (*string=='!') { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* !"..." */ + if (*string==sc_ctrlchar) { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* !\"..." */ + } /* if */ + } else if (*string==sc_ctrlchar) { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* \"..." */ + if (*string=='!') { + string++; + if (*string=='\"' || *string=='\'') + return TRUE; /* \!"..." */ + } /* if */ + } /* if */ + + return FALSE; +} + +static const unsigned char *skipstring(const unsigned char *string) +{ + char endquote; + int flags=0; + + while (*string=='!' || *string==sc_ctrlchar) { + if (*string==sc_ctrlchar) + flags=RAWMODE; + string++; + } /* while */ + + endquote=*string; + assert(endquote=='"' || endquote=='\''); + string++; /* skip open quote */ + while (*string!=endquote && *string!='\0') + litchar(&string,flags); + return string; +} + +static const unsigned char *skippgroup(const unsigned char *string) +{ + int nest=0; + char open=*string; + char close; + + switch (open) { + case '(': + close=')'; + break; + case '{': + close='}'; + break; + case '[': + close=']'; + break; + case '<': + close='>'; + break; + default: + assert(0); + close='\0'; /* only to avoid a compiler warning */ + }/* switch */ + + string++; + while (*string!=close || nest>0) { + if (*string==open) + nest++; + else if (*string==close) + nest--; + else if (is_startstring(string)) + string=skipstring(string); + if (*string=='\0') + break; + string++; + } /* while */ + return string; +} + +static char *strdel(char *str,size_t len) +{ + size_t length=strlen(str); + if (len>length) + len=length; + memmove(str, str+len, length-len+1); /* include EOS byte */ + return str; +} + +static char *strins(char *dest,char *src,size_t srclen) +{ + size_t destlen=strlen(dest); + assert(srclen<=strlen(src)); + memmove(dest+srclen, dest, destlen+1);/* include EOS byte */ + memcpy(dest, src, srclen); + return dest; +} + +static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char *substitution) +{ + int prefixlen; + const unsigned char *p,*s,*e; + unsigned char *args[10]; + int match,arg,len; + + memset(args,0,sizeof args); + + /* check the length of the prefix */ + for (prefixlen=0,s=(unsigned char*)pattern; alphanum(*s); prefixlen++,s++) + /* nothing */; + assert(prefixlen>0); + assert(strncmp((char*)line,pattern,prefixlen)==0); + + /* pattern prefix matches; match the rest of the pattern, gather + * the parameters + */ + s=line+prefixlen; + p=(unsigned char*)pattern+prefixlen; + match=TRUE; /* so far, pattern matches */ + while (match && *s!='\0' && *p!='\0') { + if (*p=='%') { + p++; /* skip '%' */ + if (isdigit(*p)) { + arg=*p-'0'; + assert(arg>=0 && arg<=9); + p++; /* skip parameter id */ + assert(*p!='\0'); + /* match the source string up to the character after the digit + * (skipping strings in the process + */ + e=s; + while (*e!=*p && *e!='\0' && *e!='\n') { + if (is_startstring(e)) /* skip strings */ + e=skipstring(e); + else if (strchr("({[",*e)!=NULL) /* skip parenthized groups */ + e=skippgroup(e); + if (*e!='\0') + e++; /* skip non-alphapetic character (or closing quote of + * a string, or the closing paranthese of a group) */ + } /* while */ + /* store the parameter (overrule any earlier) */ + if (args[arg]!=NULL) + free(args[arg]); + len=(int)(e-s); + args[arg]=(unsigned char*)malloc(len+1); + if (args[arg]==NULL) + error(103); /* insufficient memory */ + strlcpy((char*)args[arg],(char*)s,len+1); + /* character behind the pattern was matched too */ + if (*e==*p) { + s=e+1; + } else if (*e=='\n' && *p==';' && *(p+1)=='\0' && !sc_needsemicolon) { + s=e; /* allow a trailing ; in the pattern match to end of line */ + } else { + assert(*e=='\0' || *e=='\n'); + match=FALSE; + s=e; + } /* if */ + p++; + } else { + match=FALSE; + } /* if */ + } else if (*p==';' && *(p+1)=='\0' && !sc_needsemicolon) { + /* source may be ';' or end of the line */ + while (*s<=' ' && *s!='\0') + s++; /* skip white space */ + if (*s!=';' && *s!='\0') + match=FALSE; + p++; /* skip the semicolon in the pattern */ + } else { + cell ch; + /* skip whitespace between two non-alphanumeric characters, except + * for two identical symbols + */ + assert((char*)p>pattern); + if (!alphanum(*p) && *(p-1)!=*p) + while (*s<=' ' && *s!='\0') + s++; /* skip white space */ + ch=litchar(&p,0); /* this increments "p" */ + if (*s!=ch) + match=FALSE; + else + s++; /* this character matches */ + } /* if */ + } /* while */ + + if (match && *p=='\0') { + /* if the last character to match is an alphanumeric character, the + * current character in the source may not be alphanumeric + */ + assert(p>(unsigned char*)pattern); + if (alphanum(*(p-1)) && alphanum(*s)) + match=FALSE; + } /* if */ + + if (match) { + /* calculate the length of the substituted string */ + for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) { + if (*e=='%' && isdigit(*(e+1))) { + arg=*(e+1)-'0'; + assert(arg>=0 && arg<=9); + if (args[arg]!=NULL) + len+=strlen((char*)args[arg]); + else + len+=2; /* copy '%' plus digit */ + e++; /* skip %, digit is skipped later */ + } else { + len++; + } /* if */ + } /* for */ + /* check length of the string after substitution */ + if (strlen((char*)line) + len - (int)(s-line) > buffersize) { + error(75); /* line too long */ + } else { + /* substitute pattern */ + strdel((char*)line,(int)(s-line)); + for (e=(unsigned char*)substitution,s=line; *e!='\0'; e++) { + if (*e=='%' && isdigit(*(e+1))) { + arg=*(e+1)-'0'; + assert(arg>=0 && arg<=9); + if (args[arg]!=NULL) { + strins((char*)s,(char*)args[arg],strlen((char*)args[arg])); + s+=strlen((char*)args[arg]); + } else { + error(236); /* parameter does not exist, incorrect #define pattern */ + strins((char*)s,(char*)e,2); + s+=2; + } /* if */ + e++; /* skip %, digit is skipped later */ + } else { + strins((char*)s,(char*)e,1); + s++; + } /* if */ + } /* for */ + } /* if */ + } /* if */ + + for (arg=0; arg<10; arg++) + if (args[arg]!=NULL) + free(args[arg]); + + return match; +} + +static void substallpatterns(unsigned char *line,int buffersize) +{ + unsigned char *start, *end; + int prefixlen; + stringpair *subst; + + start=line; + while (*start!='\0') { + /* find the start of a prefix (skip all non-alphabetic characters), + * also skip strings + */ + while (!alpha(*start) && *start!='\0') { + /* skip strings */ + if (is_startstring(start)) { + start=(unsigned char *)skipstring(start); + if (*start=='\0') + break; /* abort loop on error */ + } /* if */ + start++; /* skip non-alphapetic character (or closing quote of a string) */ + } /* while */ + if (*start=='\0') + break; /* abort loop on error */ + /* if matching the operator "defined", skip it plus the symbol behind it */ + if (strncmp((char*)start,"defined",7)==0 && *(start+7)<=' ') { + start+=7; /* skip "defined" */ + /* skip white space & parantheses */ + while (*start<=' ' && *start!='\0' || *start=='(') + start++; + /* skip the symbol behind it */ + while (alphanum(*start)) + start++; + /* drop back into the main loop */ + continue; + } /* if */ + /* get the prefix (length), look for a matching definition */ + prefixlen=0; + end=start; + while (alphanum(*end)) { + prefixlen++; + end++; + } /* while */ + assert(prefixlen>0); + subst=find_subst((char*)start,prefixlen); + if (subst!=NULL) { + /* properly match the pattern and substitute */ + if (!substpattern(start,buffersize-(int)(start-line),subst->first,subst->second)) + start=end; /* match failed, skip this prefix */ + /* match succeeded: do not update "start", because the substitution text + * may be matched by other macros + */ + } else { + start=end; /* no macro with this prefix, skip this prefix */ + } /* if */ + } /* while */ +} +#endif + +/* preprocess + * + * Reads a line by readline() into "pline" and performs basic preprocessing: + * deleting comments, skipping lines with false "#if.." code and recognizing + * other compiler directives. There is an indirect recursion: lex() calls + * preprocess() if a new line must be read, preprocess() calls command(), + * which at his turn calls lex() to identify the token. + * + * Global references: lptr (altered) + * pline (altered) + * freading (referred to only) + */ +SC_FUNC void preprocess(void) +{ + int iscommand; + + if (!freading) + return; + do { + readline(pline); + stripcom(pline); /* ??? no need for this when reading back from list file (in the second pass) */ + lptr=pline; /* set "line pointer" to start of the parsing buffer */ + iscommand=command(); + if (iscommand!=CMD_NONE) + errorset(sRESET,0); /* reset error flag ("panic mode") on empty line or directive */ + #if !defined NO_DEFINE + if (iscommand==CMD_NONE) { + assert(lptr!=term_expr); + substallpatterns(pline,sLINEMAX); + lptr=pline; /* reset "line pointer" to start of the parsing buffer */ + } /* if */ + #endif + if (sc_status==statFIRST && sc_listing && freading + && (iscommand==CMD_NONE || iscommand==CMD_EMPTYLINE || iscommand==CMD_DIRECTIVE)) + { + listline++; + if (fline!=listline) { + listline=fline; + setlinedirect(fline); + } /* if */ + if (iscommand==CMD_EMPTYLINE) + pc_writeasm(outf,"\n"); + else + pc_writeasm(outf,(char*)pline); + } /* if */ + } while (iscommand!=CMD_NONE && iscommand!=CMD_TERM && freading); /* enddo */ +} + +static const unsigned char *unpackedstring(const unsigned char *lptr,int flags) +{ + while (*lptr!='\"' && *lptr!='\0') { + if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ + lptr++; + continue; + } /* if */ + litadd(litchar(&lptr,flags | UTF8MODE)); /* litchar() alters "lptr" */ + } /* while */ + litadd(0); /* terminate string */ + return lptr; +} + +static const unsigned char *packedstring(const unsigned char *lptr,int flags) +{ + int i; + ucell val,c; + + i=sizeof(ucell)-(sCHARBITS/8); /* start at most significant byte */ + val=0; + while (*lptr!='\"' && *lptr!='\0') { + if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ + lptr++; + continue; + } /* if */ + c=litchar(&lptr,flags); /* litchar() alters "lptr" */ + if (c>=(ucell)(1 << sCHARBITS)) + error(43); /* character constant exceeds range */ + val |= (c << 8*i); + if (i==0) { + litadd(val); + val=0; + } /* if */ + i=(i+sizeof(ucell)-(sCHARBITS/8)) % sizeof(ucell); + } /* if */ + /* save last code; make sure there is at least one terminating zero character */ + if (i!=(int)(sizeof(ucell)-(sCHARBITS/8))) + litadd(val); /* at least one zero character in "val" */ + else + litadd(0); /* add full cell of zeros */ + return lptr; +} + +/* lex(lexvalue,lexsym) Lexical Analysis + * + * lex() first deletes leading white space, then checks for multi-character + * operators, keywords (including most compiler directives), numbers, + * labels, symbols and literals (literal characters are converted to a number + * and are returned as such). If every check fails, the line must contain + * a single-character operator. So, lex() returns this character. In the other + * case (something did match), lex() returns the number of the token. All + * these tokens have been assigned numbers above 255. + * + * Some tokens have "attributes": + * tNUMBER the value of the number is return in "lexvalue". + * tRATIONAL the value is in IEEE 754 encoding or in fixed point + * encoding in "lexvalue". + * tSYMBOL the first sNAMEMAX characters of the symbol are + * stored in a buffer, a pointer to this buffer is + * returned in "lexsym". + * tLABEL the first sNAMEMAX characters of the label are + * stored in a buffer, a pointer to this buffer is + * returned in "lexsym". + * tSTRING the string is stored in the literal pool, the index + * in the literal pool to this string is stored in + * "lexvalue". + * + * lex() stores all information (the token found and possibly its attribute) + * in global variables. This allows a token to be examined twice. If "_pushed" + * is true, this information is returned. + * + * Global references: lptr (altered) + * fline (referred to only) + * litidx (referred to only) + * _lextok, _lexval, _lexstr + * _pushed + */ + +static int _pushed; +static int _lextok; +static cell _lexval; +static char _lexstr[sLINEMAX+1]; +static int _lexnewline; + +SC_FUNC void lexinit(void) +{ + stkidx=0; /* index for pushstk() and popstk() */ + iflevel=0; /* preprocessor: nesting of "#if" is currently 0 */ + skiplevel=0; /* preprocessor: not currently skipping */ + icomment=0; /* currently not in a multiline comment */ + _pushed=FALSE; /* no token pushed back into lex */ + _lexnewline=FALSE; +} + +char *sc_tokens[] = { + "*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=", + "||", "&&", "==", "!=", "<=", ">=", "<<", ">>>", ">>", "++", "--", + "...", "..", "::", + "assert", "break", "case", "char", "const", "continue", "default", + "defined", "do", "else", "enum", "exit", "for", "forward", "goto", + "if", "native", "new", "decl", "operator", "public", "return", "sizeof", + "sleep", "state", "static", "stock", "switch", "tagof", "while", + "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput", + "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma", + "#tryinclude", "#undef", + ";", ";", "-integer value-", "-rational value-", "-identifier-", + "-label-", "-string-" + }; + +SC_FUNC int lex(cell *lexvalue,char **lexsym) +{ + int i,toolong,newline,stringflags; + char **tokptr; + const unsigned char *starttoken; + + if (_pushed) { + _pushed=FALSE; /* reset "_pushed" flag */ + *lexvalue=_lexval; + *lexsym=_lexstr; + return _lextok; + } /* if */ + + _lextok=0; /* preset all values */ + _lexval=0; + _lexstr[0]='\0'; + *lexvalue=_lexval; + *lexsym=_lexstr; + _lexnewline=FALSE; + if (!freading) + return 0; + + newline= (lptr==pline); /* does lptr point to start of line buffer */ + while (*lptr<=' ') { /* delete leading white space */ + if (*lptr=='\0') { + preprocess(); /* preprocess resets "lptr" */ + if (!freading) + return 0; + if (lptr==term_expr) /* special sequence to terminate a pending expression */ + return (_lextok=tENDEXPR); + _lexnewline=TRUE; /* set this after preprocess(), because + * preprocess() calls lex() recursively */ + newline=TRUE; + } else { + lptr+=1; + } /* if */ + } /* while */ + if (newline) { + stmtindent=0; + for (i=0; i<(int)(lptr-pline); i++) + if (pline[i]=='\t' && sc_tabsize>0) + stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize); + else + stmtindent++; + } /* if */ + + i=tFIRST; + tokptr=sc_tokens; + while (i<=tMIDDLE) { /* match multi-character operators */ + if (*lptr==**tokptr && match(*tokptr,FALSE)) { + _lextok=i; + if (pc_docexpr) /* optionally concatenate to documentation string */ + insert_autolist(*tokptr); + return _lextok; + } /* if */ + i+=1; + tokptr+=1; + } /* while */ + while (i<=tLAST) { /* match reserved words and compiler directives */ + if (*lptr==**tokptr && match(*tokptr,TRUE)) { + _lextok=i; + errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/ + if (pc_docexpr) /* optionally concatenate to documentation string */ + insert_autolist(*tokptr); + return _lextok; + } /* if */ + i+=1; + tokptr+=1; + } /* while */ + + starttoken=lptr; /* save start pointer (for concatenating to documentation string) */ + if ((i=number(&_lexval,lptr))!=0) { /* number */ + _lextok=tNUMBER; + *lexvalue=_lexval; + lptr+=i; + } else if ((i=ftoi(&_lexval,lptr))!=0) { + _lextok=tRATIONAL; + *lexvalue=_lexval; + lptr+=i; + } else if (alpha(*lptr)) { /* symbol or label */ + /* Note: only sNAMEMAX characters are significant. The compiler + * generates a warning if a symbol exceeds this length. + */ + _lextok=tSYMBOL; + i=0; + toolong=0; + while (alphanum(*lptr)){ + _lexstr[i]=*lptr; + lptr+=1; + if (i=litmax) { + cell *p; + + litmax+=sDEF_LITMAX; + p=(cell *)realloc(litq,litmax*sizeof(cell)); + if (p==NULL) + error(102,"literal table"); /* literal table overflow (fatal error) */ + litq=p; + } /* if */ +} + +/* litadd + * + * Adds a value at the end of the literal queue. The literal queue is used + * for literal strings used in functions and for initializing array variables. + * + * Global references: litidx (altered) + * litq (altered) + */ +SC_FUNC void litadd(cell value) +{ + chk_grow_litq(); + assert(litidx=0 && pos<=litidx); + memmove(litq+(pos+1),litq+pos,(litidx-pos)*sizeof(cell)); + litidx++; + litq[pos]=value; +} + +/* litchar + * + * Return current literal character and increase the pointer to point + * just behind this literal character. + * + * Note: standard "escape sequences" are suported, but the backslash may be + * replaced by another character; the syntax '\ddd' is supported, + * but ddd must be decimal! + */ +static cell litchar(const unsigned char **lptr,int flags) +{ + cell c=0; + const unsigned char *cptr; + + cptr=*lptr; + if ((flags & RAWMODE)!=0 || *cptr!=sc_ctrlchar) { /* no escape character */ + #if !defined NO_UTF8 + if (sc_is_utf8 && (flags & UTF8MODE)!=0) { + c=get_utf8_char(cptr,&cptr); + assert(c>=0); /* file was already scanned for conformance to UTF-8 */ + } else { + #endif + #if !defined NO_CODEPAGE + c=cp_translate(cptr,&cptr); + #else + c=*cptr; + cptr+=1; + #endif + #if !defined NO_UTF8 + } /* if */ + #endif + } else { + cptr+=1; + if (*cptr==sc_ctrlchar) { + c=*cptr; /* \\ == \ (the escape character itself) */ + cptr+=1; + } else { + switch (*cptr) { + case 'a': /* \a == audible alarm */ + c=7; + cptr+=1; + break; + case 'b': /* \b == backspace */ + c=8; + cptr+=1; + break; + case 'e': /* \e == escape */ + c=27; + cptr+=1; + break; + case 'f': /* \f == form feed */ + c=12; + cptr+=1; + break; + case 'n': /* \n == NewLine character */ + c=10; + cptr+=1; + break; + case 'r': /* \r == carriage return */ + c=13; + cptr+=1; + break; + case 't': /* \t == horizontal TAB */ + c=9; + cptr+=1; + break; + case 'v': /* \v == vertical TAB */ + c=11; + cptr+=1; + break; + case 'x': + cptr+=1; + c=0; + while (ishex(*cptr)) { + if (isdigit(*cptr)) + c=(c<<4)+(*cptr-'0'); + else + c=(c<<4)+(tolower(*cptr)-'a'+10); + cptr++; + } /* while */ + if (*cptr==';') + cptr++; /* swallow a trailing ';' */ + break; + case '\'': /* \' == ' (single quote) */ + case '"': /* \" == " (single quote) */ + case '%': /* \% == % (percent) */ + c=*cptr; + cptr+=1; + break; + default: + if (isdigit(*cptr)) { /* \ddd */ + c=0; + while (*cptr>='0' && *cptr<='9') /* decimal! */ + c=c*10 + *cptr++ - '0'; + if (*cptr==';') + cptr++; /* swallow a trailing ';' */ + } else { + error(27); /* invalid character constant */ + } /* if */ + } /* switch */ + } /* if */ + } /* if */ + *lptr=cptr; + assert(c>=0); + return c; +} + +/* alpha + * + * Test if character "c" is alphabetic ("a".."z"), an underscore ("_") + * or an "at" sign ("@"). The "@" is an extension to standard C. + */ +static int alpha(char c) +{ + return (isalpha(c) || c=='_' || c==PUBLIC_CHAR); +} + +/* alphanum + * + * Test if character "c" is alphanumeric ("a".."z", "0".."9", "_" or "@") + */ +SC_FUNC int alphanum(char c) +{ + return (alpha(c) || isdigit(c)); +} + +/* ishex + * + * Test if character "c" is a hexadecimal digit ("0".."9" or "a".."f"). + */ +SC_FUNC int ishex(char c) +{ + return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); +} + +/* The local variable table must be searched backwards, so that the deepest + * nesting of local variables is searched first. The simplest way to do + * this is to insert all new items at the head of the list. + * In the global list, the symbols are kept in sorted order, so that the + * public functions are written in sorted order. + */ +static symbol *add_symbol(symbol *root,symbol *entry,int sort) +{ + symbol *newsym; + + if (sort) + while (root->next!=NULL && strcmp(entry->name,root->next->name)>0) + root=root->next; + + if ((newsym=(symbol *)malloc(sizeof(symbol)))==NULL) { + error(103); + return NULL; + } /* if */ + memcpy(newsym,entry,sizeof(symbol)); + newsym->next=root->next; + root->next=newsym; + return newsym; +} + +static void free_symbol(symbol *sym) +{ + arginfo *arg; + + /* free all sub-symbol allocated memory blocks, depending on the + * kind of the symbol + */ + assert(sym!=NULL); + if (sym->ident==iFUNCTN) { + /* run through the argument list; "default array" arguments + * must be freed explicitly; the tag list must also be freed */ + assert(sym->dim.arglist!=NULL); + for (arg=sym->dim.arglist; arg->ident!=0; arg++) { + if (arg->ident==iREFARRAY && arg->hasdefault) + free(arg->defvalue.array.data); + else if (arg->ident==iVARIABLE + && ((arg->hasdefault & uSIZEOF)!=0 || (arg->hasdefault & uTAGOF)!=0)) + free(arg->defvalue.size.symname); + assert(arg->tags!=NULL); + free(arg->tags); + } /* for */ + free(sym->dim.arglist); + if (sym->states!=NULL) { + delete_consttable(sym->states); + free(sym->states); + } /* if */ + } else if (sym->ident==iVARIABLE || sym->ident==iARRAY) { + if (sym->states!=NULL) { + delete_consttable(sym->states); + free(sym->states); + } /* if */ + } else if (sym->ident==iCONSTEXPR && (sym->usage & uENUMROOT)==uENUMROOT) { + /* free the constant list of an enum root */ + assert(sym->dim.enumlist!=NULL); + delete_consttable(sym->dim.enumlist); + free(sym->dim.enumlist); + } /* if */ + assert(sym->refer!=NULL); + free(sym->refer); + if (sym->documentation!=NULL) + free(sym->documentation); + free(sym); +} + +SC_FUNC void delete_symbol(symbol *root,symbol *sym) +{ + /* find the symbol and its predecessor + * (this function assumes that you will never delete a symbol that is not + * in the table pointed at by "root") + */ + assert(root!=sym); + while (root->next!=sym) { + root=root->next; + assert(root!=NULL); + } /* while */ + + /* unlink it, then free it */ + root->next=sym->next; + free_symbol(sym); +} + +SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_functions) +{ + symbol *sym,*parent_sym; + constvalue *stateptr; + int mustdelete; + + /* erase only the symbols with a deeper nesting level than the + * specified nesting level */ + while (root->next!=NULL) { + sym=root->next; + if (sym->compoundident) { + case iLABEL: + mustdelete=delete_labels; + break; + case iVARIABLE: + case iARRAY: + /* do not delete global variables if functions are preserved */ + mustdelete=delete_functions; + break; + case iREFERENCE: + /* always delete references (only exist as function parameters) */ + mustdelete=TRUE; + break; + case iREFARRAY: + /* a global iREFARRAY symbol is the return value of a function: delete + * this only if "globals" must be deleted; other iREFARRAY instances + * (locals) are also deleted + */ + mustdelete=delete_functions; + for (parent_sym=sym->parent; parent_sym!=NULL && parent_sym->ident!=iFUNCTN; parent_sym=parent_sym->parent) + assert(parent_sym->ident==iREFARRAY); + assert(parent_sym==NULL || (parent_sym->ident==iFUNCTN && parent_sym->parent==NULL)); + if (parent_sym==NULL || parent_sym->ident!=iFUNCTN) + mustdelete=TRUE; + break; + case iCONSTEXPR: + /* delete constants, except predefined constants */ + mustdelete=delete_functions || (sym->usage & uPREDEF)==0; + break; + case iFUNCTN: + /* optionally preserve globals (variables & functions), but + * NOT native functions + */ + mustdelete=delete_functions || (sym->usage & uNATIVE)!=0; + assert(sym->parent==NULL); + break; + case iARRAYCELL: + case iARRAYCHAR: + case iEXPRESSION: + case iVARARGS: + default: + assert(0); + break; + } /* switch */ + if (mustdelete) { + root->next=sym->next; + free_symbol(sym); + } else { + /* if the function was prototyped, but not implemented in this source, + * mark it as such, so that its use can be flagged + */ + if (sym->ident==iFUNCTN && (sym->usage & uDEFINE)==0) + sym->usage |= uMISSING; + if (sym->ident==iFUNCTN || sym->ident==iVARIABLE || sym->ident==iARRAY) + sym->usage &= ~uDEFINE; /* clear "defined" flag */ + /* set all states as "undefined" too */ + if (sym->states!=NULL) + for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next) + stateptr->value=0; + /* for user defined operators, also remove the "prototyped" flag, as + * user-defined operators *must* be declared before use + */ + if (sym->ident==iFUNCTN && !alpha(*sym->name)) + sym->usage &= ~uPROTOTYPED; + root=sym; /* skip the symbol */ + } /* if */ + } /* if */ +} + +/* The purpose of the hash is to reduce the frequency of a "name" + * comparison (which is costly). There is little interest in avoiding + * clusters in similar names, which is why this function is plain simple. + */ +SC_FUNC uint32_t namehash(const char *name) +{ + const unsigned char *ptr=(const unsigned char *)name; + int len=strlen(name); + if (len==0) + return 0L; + assert(len<256); + return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]); +} + +static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag) +{ + symbol *firstmatch=NULL; + symbol *sym=root->next; + int count=0; + unsigned long hash=namehash(name); + while (sym!=NULL) { + if (hash==sym->hash && strcmp(name,sym->name)==0 /* check name */ + && (sym->parent==NULL || sym->ident==iCONSTEXPR) /* sub-types (hierarchical types) are skipped, except for enum fields */ + && (sym->fnumber<0 || sym->fnumber==fnumber)) /* check file number for scope */ + { + assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */ + if (sym->ident==iFUNCTN + || automaton<0 && sym->states==NULL + || automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton) + { + if (cmptag==NULL) + return sym; /* return first match */ + /* return closest match or first match; count number of matches */ + if (firstmatch==NULL) + firstmatch=sym; + assert(cmptag!=NULL); + if (*cmptag==0) + count++; + if (*cmptag==sym->tag) { + *cmptag=1; /* good match found, set number of matches to 1 */ + return sym; + } /* if */ + } /* if */ + } /* */ + sym=sym->next; + } /* while */ + if (cmptag!=NULL && firstmatch!=NULL) + *cmptag=count; + return firstmatch; +} + +static symbol *find_symbol_child(const symbol *root,const symbol *sym) +{ + symbol *ptr=root->next; + while (ptr!=NULL) { + if (ptr->parent==sym) + return ptr; + ptr=ptr->next; + } /* while */ + return NULL; +} + +/* Adds "bywhom" to the list of referrers of "entry". Typically, + * bywhom will be the function that uses a variable or that calls + * the function. + */ +SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom) +{ + int count; + + assert(bywhom!=NULL); /* it makes no sense to add a "void" referrer */ + assert(entry!=NULL); + assert(entry->refer!=NULL); + + /* see if it is already there */ + for (count=0; countnumrefers && entry->refer[count]!=bywhom; count++) + /* nothing */; + if (countnumrefers) { + assert(entry->refer[count]==bywhom); + return TRUE; + } /* if */ + + /* see if there is an empty spot in the referrer list */ + for (count=0; countnumrefers && entry->refer[count]!=NULL; count++) + /* nothing */; + assert(count <= entry->numrefers); + if (count==entry->numrefers) { + symbol **refer; + int newsize=2*entry->numrefers; + assert(newsize>0); + /* grow the referrer list */ + refer=(symbol**)realloc(entry->refer,newsize*sizeof(symbol*)); + if (refer==NULL) + return FALSE; /* insufficient memory */ + /* initialize the new entries */ + entry->refer=refer; + for (count=entry->numrefers; countrefer[count]=NULL; + count=entry->numrefers; /* first empty spot */ + entry->numrefers=newsize; + } /* if */ + + /* add the referrer */ + assert(entry->refer[count]==NULL); + entry->refer[count]=bywhom; + return TRUE; +} + +SC_FUNC void markusage(symbol *sym,int usage) +{ + assert(sym!=NULL); + sym->usage |= (char)usage; + if ((usage & uWRITTEN)!=0) + sym->lnumber=fline; + /* check if (global) reference must be added to the symbol */ + if ((usage & (uREAD | uWRITTEN))!=0) { + /* only do this for global symbols */ + if (sym->vclass==sGLOBAL) { + /* "curfunc" should always be valid, since statements may not occurs + * outside functions; in the case of syntax errors, however, the + * compiler may arrive through this function + */ + if (curfunc!=NULL) + refer_symbol(sym,curfunc); + } /* if */ + } /* if */ +} + + +/* findglb + * + * Returns a pointer to the global symbol (if found) or NULL (if not found) + */ +SC_FUNC symbol *findglb(const char *name,int filter) +{ + /* find a symbol with a matching automaton first */ + symbol *sym=NULL; + + if (filter>sGLOBAL && sc_curstates>0) { + /* find a symbol whose state list matches the current fsa */ + sym=find_symbol(&glbtab,name,fcurrent,state_getfsa(sc_curstates),NULL); + if (sym!=NULL && sym->ident!=iFUNCTN) { + /* if sym!=NULL, we found a variable in the automaton; now we should + * also verify whether there is an intersection between the symbol's + * state list and the current state list + */ + assert(sym->states!=NULL && sym->states->next!=NULL); + if (!state_conflict_id(sc_curstates,sym->states->next->index)) + sym=NULL; + } /* if */ + } /* if */ + + /* if no symbol with a matching automaton exists, find a variable/function + * that has no state(s) attached to it + */ + if (sym==NULL) + sym=find_symbol(&glbtab,name,fcurrent,-1,NULL); + return sym; +} + +/* findloc + * + * Returns a pointer to the local symbol (if found) or NULL (if not found). + * See add_symbol() how the deepest nesting level is searched first. + */ +SC_FUNC symbol *findloc(const char *name) +{ + return find_symbol(&loctab,name,-1,-1,NULL); +} + +SC_FUNC symbol *findconst(const char *name,int *cmptag) +{ + symbol *sym; + + sym=find_symbol(&loctab,name,-1,-1,cmptag); /* try local symbols first */ + if (sym==NULL || sym->ident!=iCONSTEXPR) /* not found, or not a constant */ + sym=find_symbol(&glbtab,name,fcurrent,-1,cmptag); + if (sym==NULL || sym->ident!=iCONSTEXPR) + return NULL; + assert(sym->parent==NULL || (sym->usage & uENUMFIELD)!=0); + /* ^^^ constants have no hierarchy, but enumeration fields may have a parent */ + return sym; +} + +SC_FUNC symbol *finddepend(const symbol *parent) +{ + symbol *sym; + + sym=find_symbol_child(&loctab,parent); /* try local symbols first */ + if (sym==NULL) /* not found */ + sym=find_symbol_child(&glbtab,parent); + return sym; +} + +/* addsym + * + * Adds a symbol to the symbol table (either global or local variables, + * or global and local constants). + */ +SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage) +{ + symbol entry, **refer; + + /* labels may only be defined once */ + assert(ident!=iLABEL || findloc(name)==NULL); + + /* create an empty referrer list */ + if ((refer=(symbol**)malloc(sizeof(symbol*)))==NULL) { + error(103); /* insufficient memory */ + return NULL; + } /* if */ + *refer=NULL; + + /* first fill in the entry */ + memset(&entry,0,sizeof entry); + strcpy(entry.name,name); + entry.hash=namehash(name); + entry.addr=addr; + entry.codeaddr=code_idx; + entry.vclass=(char)vclass; + entry.ident=(char)ident; + entry.tag=tag; + entry.usage=(char)usage; + entry.fnumber=-1; /* assume global visibility (ignored for local symbols) */ + entry.lnumber=fline; + entry.numrefers=1; + entry.refer=refer; + + /* then insert it in the list */ + if (vclass==sGLOBAL) + return add_symbol(&glbtab,&entry,TRUE); + else + return add_symbol(&loctab,&entry,FALSE); +} + +SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag, + int dim[],int numdim,int idxtag[]) +{ + symbol *sym; + + /* global variables may only be defined once + * One complication is that functions returning arrays declare an array + * with the same name as the function, so the assertion must allow for + * this special case. Another complication is that variables may be + * "redeclared" if they are local to an automaton (and findglb() will find + * the symbol without states if no symbol with states exists). + */ + assert(vclass!=sGLOBAL || (sym=findglb(name,sGLOBAL))==NULL || (sym->usage & uDEFINE)==0 + || sym->ident==iFUNCTN && sym==curfunc + || sym->states==NULL && sc_curstates>0); + + if (ident==iARRAY || ident==iREFARRAY) { + symbol *parent=NULL,*top; + int level; + sym=NULL; /* to avoid a compiler warning */ + for (level=0; leveldim.array.length=dim[level]; + top->dim.array.level=(short)(numdim-level-1); + top->x.tags.index=idxtag[level]; + top->parent=parent; + parent=top; + if (level==0) + sym=top; + } /* for */ + } else { + sym=addsym(name,addr,ident,vclass,tag,uDEFINE); + } /* if */ + return sym; +} + +/* getlabel + * + * Returns te next internal label number. The global variable sc_labnum is + * initialized to zero. + */ +SC_FUNC int getlabel(void) +{ + return sc_labnum++; +} + +/* itoh + * + * Converts a number to a hexadecimal string and returns a pointer to that + * string. This function is NOT re-entrant. + */ +SC_FUNC char *itoh(ucell val) +{ +static char itohstr[30]; + char *ptr; + int i,nibble[16]; /* a 64-bit hexadecimal cell has 16 nibbles */ + int max; + + #if PAWN_CELL_SIZE==16 + max=4; + #elif PAWN_CELL_SIZE==32 + max=8; + #elif PAWN_CELL_SIZE==64 + max=16; + #else + #error Unsupported cell size + #endif + ptr=itohstr; + for (i=0; i>=4; + } /* endfor */ + i=max-1; + while (nibble[i]==0 && i>0) /* search for highest non-zero nibble */ + i-=1; + while (i>=0){ + if (nibble[i]>=10) + *ptr++=(char)('a'+(nibble[i]-10)); + else + *ptr++=(char)('0'+nibble[i]); + i-=1; + } /* while */ + *ptr='\0'; /* and a zero-terminator */ + return itohstr; +} + diff --git a/compiler-init/sc3.c b/compiler-init/sc3.c new file mode 100644 index 00000000..65fec149 --- /dev/null +++ b/compiler-init/sc3.c @@ -0,0 +1,2453 @@ +/* Pawn compiler - Recursive descend expresion parser + * + * Copyright (c) ITB CompuPhase, 1997-2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc3.c 3598 2006-07-04 13:44:04Z thiadmer $ + */ +#include +#include +#include /* for _MAX_PATH */ +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval, + int (*hier)(value*),value *lval); +static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval); +static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval, + char *forcetag,int chkbitwise); +static int plnge1(int (*hier)(value *lval),value *lval); +static void plnge2(void (*oper)(void), + int (*hier)(value *lval), + value *lval1,value *lval2); +static cell calc(cell left,void (*oper)(),cell right,char *boolresult); +static int hier14(value *lval); +static int hier13(value *lval); +static int hier12(value *lval); +static int hier11(value *lval); +static int hier10(value *lval); +static int hier9(value *lval); +static int hier8(value *lval); +static int hier7(value *lval); +static int hier6(value *lval); +static int hier5(value *lval); +static int hier4(value *lval); +static int hier3(value *lval); +static int hier2(value *lval); +static int hier1(value *lval1); +static int primary(value *lval); +static void clear_value(value *lval); +static void callfunction(symbol *sym,value *lval_result,int matchparanthesis); +static int dbltest(void (*oper)(),value *lval1,value *lval2); +static int commutative(void (*oper)()); +static int constant(value *lval); + +static char lastsymbol[sNAMEMAX+1]; /* name of last function/variable */ +static int bitwise_opercount; /* count of bitwise operators in an expression */ +static int decl_heap=0; + +/* Function addresses of binary operators for signed operations */ +static void (*op1[17])(void) = { + os_mult,os_div,os_mod, /* hier3, index 0 */ + ob_add,ob_sub, /* hier4, index 3 */ + ob_sal,os_sar,ou_sar, /* hier5, index 5 */ + ob_and, /* hier6, index 8 */ + ob_xor, /* hier7, index 9 */ + ob_or, /* hier8, index 10 */ + os_le,os_ge,os_lt,os_gt, /* hier9, index 11 */ + ob_eq,ob_ne, /* hier10, index 15 */ +}; +/* These two functions are defined because the functions inc() and dec() in + * SC4.C have a different prototype than the other code generation functions. + * The arrays for user-defined functions use the function pointers for + * identifying what kind of operation is requested; these functions must all + * have the same prototype. As inc() and dec() are special cases already, it + * is simplest to add two "do-nothing" functions. + */ +static void user_inc(void) {} +static void user_dec(void) {} + +/* + * Searches for a binary operator a list of operators. The list is stored in + * the array "list". The last entry in the list should be set to 0. + * + * The index of an operator in "list" (if found) is returned in "opidx". If + * no operator is found, nextop() returns 0. + * + * If an operator is found in the expression, it cannot be used in a function + * call with omitted parantheses. Mark this... + * + * Global references: sc_allowproccall (modified) + */ +static int nextop(int *opidx,int *list) +{ + *opidx=0; + while (*list){ + if (matchtoken(*list)){ + sc_allowproccall=FALSE; + return TRUE; /* found! */ + } else { + list+=1; + *opidx+=1; + } /* if */ + } /* while */ + return FALSE; /* entire list scanned, nothing found */ +} + +SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, + value *lval,int *resulttag) +{ +static char *binoperstr[] = { "*", "/", "%", "+", "-", "", "", "", + "", "", "", "<=", ">=", "<", ">", "==", "!=" }; +static int binoper_savepri[] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, + TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }; +static char *unoperstr[] = { "!", "-", "++", "--" }; +static void (*unopers[])(void) = { lneg, neg, user_inc, user_dec }; + char opername[4] = "", symbolname[sNAMEMAX+1]; + int i,swapparams,savepri,savealt; + int paramspassed; + symbol *sym; + + /* since user-defined operators on untagged operands are forbidden, we have + * a quick exit. + */ + assert(numparam==1 || numparam==2); + if (tag1==0 && (numparam==1 || tag2==0)) + return FALSE; + + savepri=savealt=FALSE; + /* find the name with the operator */ + if (numparam==2) { + if (oper==NULL) { + /* assignment operator: a special case */ + strcpy(opername,"="); + if (lval!=NULL && (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR)) + savealt=TRUE; + } else { + assert( (sizeof binoperstr / sizeof binoperstr[0]) == (sizeof op1 / sizeof op1[0]) ); + for (i=0; iusage & uDEFINE)==0*/) { /* ??? should not check uDEFINE; first pass clears these bits */ + /* check for commutative operators */ + if (tag1==tag2 || oper==NULL || !commutative(oper)) + return FALSE; /* not commutative, cannot swap operands */ + /* if arrived here, the operator is commutative and the tags are different, + * swap tags and try again + */ + assert(numparam==2); /* commutative operator must be a binary operator */ + operator_symname(symbolname,opername,tag2,tag1,numparam,tag1); + swapparams=TRUE; + sym=findglb(symbolname,sGLOBAL); + if (sym==NULL /*|| (sym->usage & uDEFINE)==0*/) + return FALSE; + } /* if */ + + /* check existance and the proper declaration of this function */ + if ((sym->usage & uMISSING)!=0 || (sym->usage & uPROTOTYPED)==0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + if ((sym->usage & uMISSING)!=0) + error(4,symname); /* function not defined */ + if ((sym->usage & uPROTOTYPED)==0) + error(71,symname); /* operator must be declared before use */ + } /* if */ + + /* we don't want to use the redefined operator in the function that + * redefines the operator itself, otherwise the snippet below gives + * an unexpected recursion: + * fixed:operator+(fixed:a, fixed:b) + * return a + b + */ + if (sym==curfunc) + return FALSE; + + /* for increment and decrement operators, the symbol must first be loaded + * (and stored back afterwards) + */ + if (oper==user_inc || oper==user_dec) { + assert(!savepri); + assert(lval!=NULL); + if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR) + pushreg(sPRI); /* save current address in PRI */ + rvalue(lval); /* get the symbol's value in PRI */ + } /* if */ + + assert(!savepri || !savealt); /* either one MAY be set, but not both */ + if (savepri) { + /* the chained comparison operators require that the ALT register is + * unmodified, so we save it here; actually, we save PRI because the normal + * instruction sequence (without user operator) swaps PRI and ALT + */ + pushreg(sPRI); /* right-hand operand is in PRI */ + } else if (savealt) { + /* for the assignment operator, ALT may contain an address at which the + * result must be stored; this address must be preserved accross the + * call + */ + assert(lval!=NULL); /* this was checked earlier */ + assert(lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); /* checked earlier */ + pushreg(sALT); + } /* if */ + + /* push parameters, call the function */ + paramspassed= (oper==NULL) ? 1 : numparam; + switch (paramspassed) { + case 1: + pushreg(sPRI); + break; + case 2: + /* note that 1) a function expects that the parameters are pushed + * in reversed order, and 2) the left operand is in the secondary register + * and the right operand is in the primary register */ + if (swapparams) { + pushreg(sALT); + pushreg(sPRI); + } else { + pushreg(sPRI); + pushreg(sALT); + } /* if */ + break; + default: + assert(0); + } /* switch */ + markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ + pushval((cell)paramspassed*sizeof(cell)); + assert(sym->ident==iFUNCTN); + ffcall(sym,NULL,paramspassed); + if (sc_status!=statSKIP) + markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ + if ((sym->usage & uNATIVE)!=0 && sym->x.lib!=NULL) + sym->x.lib->value += 1; /* increment "usage count" of the library */ + sideeffect=TRUE; /* assume functions carry out a side-effect */ + assert(resulttag!=NULL); + *resulttag=sym->tag; /* save tag of the called function */ + + if (savepri || savealt) + popreg(sALT); /* restore the saved PRI/ALT that into ALT */ + if (oper==user_inc || oper==user_dec) { + assert(lval!=NULL); + if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR) + popreg(sALT); /* restore address (in ALT) */ + store(lval); /* store PRI in the symbol */ + moveto1(); /* make sure PRI is restored on exit */ + } /* if */ + return TRUE; +} + +SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce) +{ + if (formaltag!=actualtag) { + /* if the formal tag is zero and the actual tag is not "fixed", the actual + * tag is "coerced" to zero + */ + if (!allowcoerce || formaltag!=0 || (actualtag & FIXEDTAG)!=0) + return FALSE; + } /* if */ + return TRUE; +} + +/* + * The AMX pseudo-processor has no direct support for logical (boolean) + * operations. These have to be done via comparing and jumping. Since we are + * already jumping through the code, we might as well implement an "early + * drop-out" evaluation (also called "short-circuit"). This conforms to + * standard C: + * + * expr1 || expr2 expr2 will only be evaluated if expr1 is false. + * expr1 && expr2 expr2 will only be evaluated if expr1 is true. + * + * expr1 || expr2 && expr3 expr2 will only be evaluated if expr1 is false + * and expr3 will only be evaluated if expr1 is + * false and expr2 is true. + * + * Code generation for the last example proceeds thus: + * + * evaluate expr1 + * operator || found + * jump to "l1" if result of expr1 not equal to 0 + * evaluate expr2 + * -> operator && found; skip to higher level in hierarchy diagram + * jump to "l2" if result of expr2 equal to 0 + * evaluate expr3 + * jump to "l2" if result of expr3 equal to 0 + * set expression result to 1 (true) + * jump to "l3" + * l2: set expression result to 0 (false) + * l3: + * <- drop back to previous hierarchy level + * jump to "l1" if result of expr2 && expr3 not equal to 0 + * set expression result to 0 (false) + * jump to "l4" + * l1: set expression result to 1 (true) + * l4: + * + */ + +/* Skim over terms adjoining || and && operators + * dropval The value of the expression after "dropping out". An "or" drops + * out when the left hand is TRUE, so dropval must be 1 on "or" + * expressions. + * endval The value of the expression when no expression drops out. In an + * "or" expression, this happens when both the left hand and the + * right hand are FALSE, so endval must be 0 for "or" expressions. + */ +static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval, + int (*hier)(value*),value *lval) +{ + int lvalue,hits,droplab,endlab,opidx; + int allconst,foundop; + cell constval; + int index; + cell cidx; + + stgget(&index,&cidx); /* mark position in code generator */ + hits=FALSE; /* no logical operators "hit" yet */ + allconst=TRUE; /* assume all values "const" */ + constval=0; + droplab=0; /* to avoid a compiler warning */ + for ( ;; ) { + lvalue=plnge1(hier,lval); /* evaluate left expression */ + + allconst= allconst && (lval->ident==iCONSTEXPR); + if (allconst) { + if (hits) { + /* one operator was already found */ + if (testfunc==jmp_ne0) + lval->constval= lval->constval || constval; + else + lval->constval= lval->constval && constval; + } /* if */ + constval=lval->constval; /* save result accumulated so far */ + } /* if */ + + foundop=nextop(&opidx,opstr); + if ((foundop || hits) && (lval->ident==iARRAY || lval->ident==iREFARRAY)) + error(33, lval->sym ? (lval->sym->name ? lval->sym->name : "-unknown") : "-unknown-"); /* array was not indexed in an expression */ + if (foundop) { + if (!hits) { + /* this is the first operator in the list */ + hits=TRUE; + droplab=getlabel(); + } /* if */ + dropout(lvalue,testfunc,droplab,lval); + } else if (hits) { /* no (more) identical operators */ + dropout(lvalue,testfunc,droplab,lval); /* found at least one operator! */ + ldconst(endval,sPRI); + jumplabel(endlab=getlabel()); + setlabel(droplab); + ldconst(dropval,sPRI); + setlabel(endlab); + lval->sym=NULL; + lval->tag=pc_addtag("bool"); /* force tag to be "bool" */ + if (allconst) { + lval->ident=iCONSTEXPR; + lval->constval=constval; + stgdel(index,cidx); /* scratch generated code and calculate */ + } else { + lval->ident=iEXPRESSION; + lval->constval=0; + } /* if */ + return FALSE; + } else { + return lvalue; /* none of the operators in "opstr" were found */ + } /* if */ + + } /* while */ +} + +/* + * Reads into the primary register the variable pointed to by lval if + * plunging through the hierarchy levels detected an lvalue. Otherwise + * if a constant was detected, it is loaded. If there is no constant and + * no lvalue, the primary register must already contain the expression + * result. + * + * After that, the compare routines "jmp_ne0" or "jmp_eq0" are called, which + * compare the primary register against 0, and jump to the "early drop-out" + * label "exit1" if the condition is true. + */ +static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval) +{ + if (lvalue) + rvalue(lval); + else if (lval->ident==iCONSTEXPR) + ldconst(lval->constval,sPRI); + (*testfunc)(exit1); +} + +static void checkfunction(value *lval) +{ + symbol *sym=lval->sym; + + if (sym==NULL || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) + return; /* no known symbol, or not a function result */ + + if ((sym->usage & uDEFINE)!=0) { + /* function is defined, can now check the return value (but make an + * exception for directly recursive functions) + */ + if (sym!=curfunc && (sym->usage & uRETVALUE)==0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + error(209,symname); /* function should return a value */ + } /* if */ + } else { + /* function not yet defined, set */ + sym->usage|=uRETVALUE; /* make sure that a future implementation of + * the function uses "return " */ + } /* if */ +} + +/* + * Plunge to a lower level + */ +static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval, + char *forcetag,int chkbitwise) +{ + int lvalue,opidx; + int count; + value lval2 = {0}; + + lvalue=plnge1(hier,lval); + if (nextop(&opidx,opstr)==0) + return lvalue; /* no operator in "opstr" found */ + if (lvalue) + rvalue(lval); + count=0; + do { + if (chkbitwise && count++>0 && bitwise_opercount!=0) + error(212); + opidx+=opoff; /* add offset to index returned by nextop() */ + plnge2(op1[opidx],hier,lval,&lval2); + if (op1[opidx]==ob_and || op1[opidx]==ob_or) + bitwise_opercount++; + if (forcetag!=NULL) + lval->tag=pc_addtag(forcetag); + } while (nextop(&opidx,opstr)); /* do */ + return FALSE; /* result of expression is not an lvalue */ +} + +/* plnge_rel + * + * Binary plunge to lower level; this is very simular to plnge, but + * it has special code generation sequences for chained operations. + */ +static int plnge_rel(int *opstr,int opoff,int (*hier)(value *lval),value *lval) +{ + int lvalue,opidx; + value lval2={0}; + int count; + + /* this function should only be called for relational operators */ + assert(op1[opoff]==os_le); + lvalue=plnge1(hier,lval); + if (nextop(&opidx,opstr)==0) + return lvalue; /* no operator in "opstr" found */ + if (lvalue) + rvalue(lval); + count=0; + lval->boolresult=TRUE; + do { + /* same check as in plnge(), but "chkbitwise" is always TRUE */ + if (count>0 && bitwise_opercount!=0) + error(212); + if (count>0) { + relop_prefix(); + *lval=lval2; /* copy right hand expression of the previous iteration */ + } /* if */ + opidx+=opoff; + plnge2(op1[opidx],hier,lval,&lval2); + if (count++>0) + relop_suffix(); + } while (nextop(&opidx,opstr)); /* enddo */ + lval->constval=lval->boolresult; + lval->tag=pc_addtag("bool"); /* force tag to be "bool" */ + return FALSE; /* result of expression is not an lvalue */ +} + +/* plnge1 + * + * Unary plunge to lower level + * Called by: skim(), plnge(), plnge2(), plnge_rel(), hier14() and hier13() + */ +static int plnge1(int (*hier)(value *lval),value *lval) +{ + int lvalue,index; + cell cidx; + + stgget(&index,&cidx); /* mark position in code generator */ + lvalue=(*hier)(lval); + if (lval->ident==iCONSTEXPR) + stgdel(index,cidx); /* load constant later */ + return lvalue; +} + +/* plnge2 + * + * Binary plunge to lower level + * Called by: plnge(), plnge_rel(), hier14() and hier1() + */ +static void plnge2(void (*oper)(void), + int (*hier)(value *lval), + value *lval1,value *lval2) +{ + int index; + cell cidx; + + stgget(&index,&cidx); /* mark position in code generator */ + if (lval1->ident==iCONSTEXPR) { /* constant on left side; it is not yet loaded */ + if (plnge1(hier,lval2)) + rvalue(lval2); /* load lvalue now */ + else if (lval2->ident==iCONSTEXPR) + ldconst(lval2->constval<constval<ident==iCONSTEXPR) { /* constant on right side */ + if (commutative(oper)) { /* test for commutative operators */ + value lvaltmp = {0}; + stgdel(index,cidx); /* scratch pushreg() and constant fetch (then + * fetch the constant again */ + ldconst(lval2->constval<constval<ident==iARRAY || lval1->ident==iREFARRAY) { + char *ptr=(lval1->sym!=NULL) ? lval1->sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } else if (lval2->ident==iARRAY || lval2->ident==iREFARRAY) { + char *ptr=(lval2->sym!=NULL) ? lval2->sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } /* if */ + /* ??? ^^^ should do same kind of error checking with functions */ + + /* check whether an "operator" function is defined for the tag names + * (a constant expression cannot be optimized in that case) + */ + if (check_userop(oper,lval1->tag,lval2->tag,2,NULL,&lval1->tag)) { + lval1->ident=iEXPRESSION; + lval1->constval=0; + } else if (lval1->ident==iCONSTEXPR && lval2->ident==iCONSTEXPR) { + /* only constant expression if both constant */ + stgdel(index,cidx); /* scratch generated code and calculate */ + if (!matchtag(lval1->tag,lval2->tag,FALSE)) + error(213); /* tagname mismatch */ + lval1->constval=calc(lval1->constval,oper,lval2->constval,&lval1->boolresult); + } else { + if (!matchtag(lval1->tag,lval2->tag,FALSE)) + error(213); /* tagname mismatch */ + (*oper)(); /* do the (signed) operation */ + lval1->ident=iEXPRESSION; + } /* if */ + } /* if */ +} + +static cell truemodulus(cell a,cell b) +{ + return (a % b + b) % b; +} + +static cell calc(cell left,void (*oper)(),cell right,char *boolresult) +{ + if (oper==ob_or) + return (left | right); + else if (oper==ob_xor) + return (left ^ right); + else if (oper==ob_and) + return (left & right); + else if (oper==ob_eq) + return (left == right); + else if (oper==ob_ne) + return (left != right); + else if (oper==os_le) + return *boolresult &= (char)(left <= right), right; + else if (oper==os_ge) + return *boolresult &= (char)(left >= right), right; + else if (oper==os_lt) + return *boolresult &= (char)(left < right), right; + else if (oper==os_gt) + return *boolresult &= (char)(left > right), right; + else if (oper==os_sar) + return (left >> (int)right); + else if (oper==ou_sar) + return ((ucell)left >> (ucell)right); + else if (oper==ob_sal) + return ((ucell)left << (int)right); + else if (oper==ob_add) + return (left + right); + else if (oper==ob_sub) + return (left - right); + else if (oper==os_mult) + return (left * right); + else if (oper==os_div) + return (left - truemodulus(left,right)) / right; + else if (oper==os_mod) + return truemodulus(left,right); + else + error(29); /* invalid expression, assumed 0 (this should never occur) */ + return 0; +} + +SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult) +{ + int locheap=decl_heap; + value lval={0}; + + if (hier14(&lval)) + rvalue(&lval); + /* scrap any arrays left on the heap */ + assert(decl_heap>=locheap); + modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ + decl_heap=locheap; + + if (lval.ident==iCONSTEXPR && val!=NULL) /* constant expression */ + *val=lval.constval; + if (tag!=NULL) + *tag=lval.tag; + if (symptr!=NULL) + *symptr=lval.sym; + if (chkfuncresult) + checkfunction(&lval); + return lval.ident; +} + +SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state) +{ + char name[sNAMEMAX+1]; + cell val; + char *str; + int fsa,islabel; + + assert(automaton!=NULL); + assert(state!=NULL); + if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL)) + return 0; + + tokeninfo(&val,&str); + assert(strlen(str)index>0); + assert(strlen(str)index==0); + } /* if */ + assert(*automaton!=NULL); + fsa=(*automaton)->index; + + assert(*automaton!=NULL); + *state=state_find(name,fsa); + if (*state==NULL) { + char *fsaname=(*automaton)->name; + if (*fsaname=='\0') + fsaname="

"; + error(87,name,fsaname); /* unknown state for automaton */ + return 0; + } /* if */ + + return 1; +} + +SC_FUNC cell array_totalsize(symbol *sym) +{ + cell length; + + assert(sym!=NULL); + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + length=sym->dim.array.length; + if (sym->dim.array.level > 0) { + cell sublength=array_totalsize(finddepend(sym)); + if (sublength>0) + length=length+length*sublength; + else + length=0; + } /* if */ + return length; +} + +static cell array_levelsize(symbol *sym,int level) +{ + assert(sym!=NULL); + assert(sym->ident==iARRAY || sym->ident==iREFARRAY); + assert(level <= sym->dim.array.level); + while (level-- > 0) { + sym=finddepend(sym); + assert(sym!=NULL); + } /* if */ + return sym->dim.array.length; +} + +/* hier14 + * + * Lowest hierarchy level (except for the , operator). + * + * Global references: sc_intest (reffered to only) + * sc_allowproccall (modified) + */ +static int hier14(value *lval1) +{ + int lvalue; + value lval2={0},lval3={0}; + void (*oper)(void); + int tok,level,i; + cell val; + char *st; + int bwcount,leftarray; + cell arrayidx1[sDIMEN_MAX],arrayidx2[sDIMEN_MAX]; /* last used array indices */ + cell *org_arrayidx; + + bwcount=bitwise_opercount; + bitwise_opercount=0; + /* initialize the index arrays with unlikely constant indices; note that + * these indices will only be changed when the array is indexed with a + * constant, and that negative array indices are invalid (so actually, any + * negative value would do). + */ + for (i=0; iarrayidx; /* save current pointer, to reset later */ + if (lval1->arrayidx==NULL) + lval1->arrayidx=arrayidx1; + lvalue=plnge1(hier13,lval1); + if (lval1->ident!=iARRAYCELL && lval1->ident!=iARRAYCHAR) + lval1->arrayidx=NULL; + if (lval1->ident==iCONSTEXPR) /* load constant here */ + ldconst(lval1->constval,sPRI); + tok=lex(&val,&st); + switch (tok) { + case taOR: + oper=ob_or; + break; + case taXOR: + oper=ob_xor; + break; + case taAND: + oper=ob_and; + break; + case taADD: + oper=ob_add; + break; + case taSUB: + oper=ob_sub; + break; + case taMULT: + oper=os_mult; + break; + case taDIV: + oper=os_div; + break; + case taMOD: + oper=os_mod; + break; + case taSHRU: + oper=ou_sar; + break; + case taSHR: + oper=os_sar; + break; + case taSHL: + oper=ob_sal; + break; + case '=': /* simple assignment */ + oper=NULL; + if (sc_intest) + error(211); /* possibly unintended assignment */ + break; + default: + lexpush(); + bitwise_opercount=bwcount; + lval1->arrayidx=org_arrayidx; /* restore array index pointer */ + return lvalue; + } /* switch */ + + /* if we get here, it was an assignment; first check a few special cases + * and then the general */ + if (lval1->ident==iARRAYCHAR) { + /* special case, assignment to packed character in a cell is permitted */ + lvalue=TRUE; + } else if (lval1->ident==iARRAY || lval1->ident==iREFARRAY) { + /* array assignment is permitted too (with restrictions) */ + if (oper) + return error(23); /* array assignment must be simple assigment */ + assert(lval1->sym!=NULL); + if (array_totalsize(lval1->sym)==0) + return error(46,lval1->sym->name); /* unknown array size */ + lvalue=TRUE; + } /* if */ + + /* operand on left side of assignment must be lvalue */ + if (!lvalue) + return error(22); /* must be lvalue */ + /* may not change "constant" parameters */ + assert(lval1->sym!=NULL); + if ((lval1->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + sc_allowproccall=FALSE; /* may no longer use "procedure call" syntax */ + + lval3=*lval1; /* save symbol to enable storage of expresion result */ + lval1->arrayidx=org_arrayidx; /* restore array index pointer */ + if (lval1->ident==iARRAYCELL || lval1->ident==iARRAYCHAR + || lval1->ident==iARRAY || lval1->ident==iREFARRAY) + { + /* if indirect fetch: save PRI (cell address) */ + if (oper) { + pushreg(sPRI); + rvalue(lval1); + } /* if */ + lval2.arrayidx=arrayidx2; + plnge2(oper,hier14,lval1,&lval2); + if (lval2.ident!=iARRAYCELL && lval2.ident!=iARRAYCHAR) + lval2.arrayidx=NULL; + if (oper) + popreg(sALT); + if (!oper && lval3.arrayidx!=NULL && lval2.arrayidx!=NULL + && lval3.ident==lval2.ident && lval3.sym==lval2.sym) + { + int same=TRUE; + assert(lval2.arrayidx==arrayidx2); + for (i=0; iname); /* self-assignment */ + } /* if */ + } else { + if (oper){ + rvalue(lval1); + plnge2(oper,hier14,lval1,&lval2); + } else { + /* if direct fetch and simple assignment: no "push" + * and "pop" needed -> call hier14() directly, */ + if (hier14(&lval2)) + rvalue(&lval2); /* instead of plnge2(). */ + else if (lval2.ident==iVARIABLE) + lval2.ident=iEXPRESSION;/* mark as "rvalue" if it is not an "lvalue" */ + checkfunction(&lval2); + /* check whether lval2 and lval3 (old lval1) refer to the same variable */ + if (lval2.ident==iVARIABLE && lval3.ident==lval2.ident && lval3.sym==lval2.sym) { + assert(lval3.sym!=NULL); + error(226,lval3.sym->name); /* self-assignment */ + } /* if */ + } /* if */ + } /* if */ + /* Array elements are sometimes considered as sub-arrays --when the + * array index is an enumeration field and the enumeration size is greater + * than 1. If the expression on the right side of the assignment is a cell, + * or if an operation is in effect, this does not apply. + */ + leftarray= lval3.ident==iARRAY || lval3.ident==iREFARRAY + || ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR) + && lval3.constval>1 && lval3.sym->dim.array.level==0 + && !oper && (lval2.ident==iARRAY || lval2.ident==iREFARRAY)); + if (leftarray) { + /* Left operand is an array, right operand should be an array variable + * of the same size and the same dimension, an array literal (of the + * same size) or a literal string. For single-dimensional arrays without + * tag for the index, it is permitted to assign a smaller array into a + * larger one (without warning). This is to make it easier to work with + * strings. + */ + int exactmatch=TRUE; + int idxtag=0; + int ltlength=(int)lval3.sym->dim.array.length; + if ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR) + && lval3.constval>0 && lval3.sym->dim.array.level==0) + { + ltlength=(int)lval3.constval; + } /* if */ + if (lval2.ident!=iARRAY && lval2.ident!=iREFARRAY + && (lval2.sym==NULL || lval2.constval<=0)) + error(33,lval3.sym->name); /* array must be indexed */ + if (lval2.sym!=NULL) { + if (lval2.constval==0) { + val=lval2.sym->dim.array.length;/* array variable */ + } else { + val=lval2.constval; + if (lval2.sym->dim.array.level!=0) + error(28,lval2.sym->name); + } /* if */ + level=lval2.sym->dim.array.level; + idxtag=lval2.sym->x.tags.index; + if (level==0 && idxtag==0 && lval3.sym->x.tags.index==0) + exactmatch=FALSE; + } else { + val=lval2.constval; /* literal array */ + level=0; + /* If val is negative, it means that lval2 is a literal string. + * The string array size may be smaller than the destination + * array, provided that the destination array does not have an + * index tag. + */ + if (val<0) { + val=-val; + if (lval3.sym->x.tags.index==0) + exactmatch=FALSE; + } /* if */ + } /* if */ + if (lval3.sym->dim.array.level!=level) + return error(48); /* array dimensions must match */ + else if (ltlengthval || val==0) + return error(47); /* array sizes must match */ + else if (lval3.ident!=iARRAYCELL && !matchtag(lval3.sym->x.tags.index,idxtag,TRUE)) + error(229,(lval2.sym!=NULL) ? lval2.sym->name : lval3.sym->name); /* index tag mismatch */ + if (level>0) { + /* check the sizes of all sublevels too */ + symbol *sym1 = lval3.sym; + symbol *sym2 = lval2.sym; + int i; + assert(sym1!=NULL && sym2!=NULL); + /* ^^^ sym2 must be valid, because only variables can be + * multi-dimensional (there are no multi-dimensional literals), + * sym1 must be valid because it must be an lvalue + */ + assert(exactmatch); + for (i=0; idim.array.length!=sym2->dim.array.length) + error(47); /* array sizes must match */ + else if (!matchtag(sym1->x.tags.index,sym2->x.tags.index,TRUE)) + error(229,sym2->name); /* index tag mismatch */ + } /* for */ + /* get the total size in cells of the multi-dimensional array */ + val=array_totalsize(lval3.sym); + assert(val>0); /* already checked */ + } /* if */ + } else { + /* left operand is not an array, right operand should then not be either */ + if (lval2.ident==iARRAY || lval2.ident==iREFARRAY) + error(6); /* must be assigned to an array */ + } /* if */ + if (leftarray) { + memcopy(val*sizeof(cell)); + } else { + check_userop(NULL,lval2.tag,lval3.tag,2,&lval3,&lval2.tag); + store(&lval3); /* now, store the expression result */ + } /* if */ + if (!oper && !matchtag(lval3.tag,lval2.tag,TRUE)) + error(213); /* tagname mismatch (if "oper", warning already given in plunge2()) */ + if (lval3.sym) + markusage(lval3.sym,uWRITTEN); + sideeffect=TRUE; + bitwise_opercount=bwcount; + lval1->ident=iEXPRESSION; + return FALSE; /* expression result is never an lvalue */ +} + +static int hier13(value *lval) +{ + int lvalue=plnge1(hier12,lval); + if (matchtoken('?')) { + int flab1=getlabel(); + int flab2=getlabel(); + value lval2={0}; + int array1,array2; + + if (lvalue) { + rvalue(lval); + } else if (lval->ident==iCONSTEXPR) { + ldconst(lval->constval,sPRI); + error(lval->constval ? 206 : 205); /* redundant test */ + } /* if */ + jmp_eq0(flab1); /* go to second expression if primary register==0 */ + PUSHSTK_I(sc_allowtags); + sc_allowtags=FALSE; /* do not allow tagnames here (colon is a special token) */ + if (hier13(lval)) + rvalue(lval); + if (lval->ident==iCONSTEXPR) /* load constant here */ + ldconst(lval->constval,sPRI); + sc_allowtags=(short)POPSTK_I(); /* restore */ + jumplabel(flab2); + setlabel(flab1); + needtoken(':'); + if (hier13(&lval2)) + rvalue(&lval2); + if (lval2.ident==iCONSTEXPR) /* load constant here */ + ldconst(lval2.constval,sPRI); + array1= (lval->ident==iARRAY || lval->ident==iREFARRAY); + array2= (lval2.ident==iARRAY || lval2.ident==iREFARRAY); + if (array1 && !array2) { + char *ptr=(lval->sym->name!=NULL) ? lval->sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } else if (!array1 && array2) { + char *ptr=(lval2.sym->name!=NULL) ? lval2.sym->name : "-unknown-"; + error(33,ptr); /* array must be indexed */ + } /* if */ + /* ??? if both are arrays, should check dimensions */ + if (!matchtag(lval->tag,lval2.tag,FALSE)) + error(213); /* tagname mismatch ('true' and 'false' expressions) */ + setlabel(flab2); + if (lval->ident==iARRAY) + lval->ident=iREFARRAY; /* iARRAY becomes iREFARRAY */ + else if (lval->ident!=iREFARRAY) + lval->ident=iEXPRESSION; /* iREFARRAY stays iREFARRAY, rest becomes iEXPRESSION */ + return FALSE; /* conditional expression is no lvalue */ + } else { + return lvalue; + } /* if */ +} + +/* the order of the operators in these lists is important and must be + * the same as the order of the operators in the array "op1" + */ +static int list3[] = {'*','/','%',0}; +static int list4[] = {'+','-',0}; +static int list5[] = {tSHL,tSHR,tSHRU,0}; +static int list6[] = {'&',0}; +static int list7[] = {'^',0}; +static int list8[] = {'|',0}; +static int list9[] = {tlLE,tlGE,'<','>',0}; +static int list10[] = {tlEQ,tlNE,0}; +static int list11[] = {tlAND,0}; +static int list12[] = {tlOR,0}; + +static int hier12(value *lval) +{ + return skim(list12,jmp_ne0,1,0,hier11,lval); +} + +static int hier11(value *lval) +{ + return skim(list11,jmp_eq0,0,1,hier10,lval); +} + +static int hier10(value *lval) +{ /* ==, != */ + return plnge(list10,15,hier9,lval,"bool",TRUE); +} /* ^ this variable is the starting index in the op1[] + * array of the operators of this hierarchy level */ + +static int hier9(value *lval) +{ /* <=, >=, <, > */ + return plnge_rel(list9,11,hier8,lval); +} + +static int hier8(value *lval) +{ /* | */ + return plnge(list8,10,hier7,lval,NULL,FALSE); +} + +static int hier7(value *lval) +{ /* ^ */ + return plnge(list7,9,hier6,lval,NULL,FALSE); +} + +static int hier6(value *lval) +{ /* & */ + return plnge(list6,8,hier5,lval,NULL,FALSE); +} + +static int hier5(value *lval) +{ /* <<, >>, >>> */ + return plnge(list5,5,hier4,lval,NULL,FALSE); +} + +static int hier4(value *lval) +{ /* +, - */ + return plnge(list4,3,hier3,lval,NULL,FALSE); +} + +static int hier3(value *lval) +{ /* *, /, % */ + return plnge(list3,0,hier2,lval,NULL,FALSE); +} + +static int hier2(value *lval) +{ + int lvalue,tok; + int tag,paranthese; + cell val; + char *st; + symbol *sym; + int saveresult; + + tok=lex(&val,&st); + switch (tok) { + case tINC: /* ++lval */ + if (!hier2(lval)) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag)) + inc(lval); /* increase variable first */ + rvalue(lval); /* and read the result into PRI */ + sideeffect=TRUE; + return FALSE; /* result is no longer lvalue */ + case tDEC: /* --lval */ + if (!hier2(lval)) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag)) + dec(lval); /* decrease variable first */ + rvalue(lval); /* and read the result into PRI */ + sideeffect=TRUE; + return FALSE; /* result is no longer lvalue */ + case '~': /* ~ (one's complement) */ + if (hier2(lval)) + rvalue(lval); + invert(); /* bitwise NOT */ + lval->constval=~lval->constval; + return FALSE; + case '!': /* ! (logical negate) */ + if (hier2(lval)) + rvalue(lval); + if (check_userop(lneg,lval->tag,0,1,NULL,&lval->tag)) { + lval->ident=iEXPRESSION; + lval->constval=0; + } else { + lneg(); /* 0 -> 1, !0 -> 0 */ + lval->constval=!lval->constval; + lval->tag=pc_addtag("bool"); + } /* if */ + return FALSE; + case '-': /* unary - (two's complement) */ + if (hier2(lval)) + rvalue(lval); + /* make a special check for a constant expression with the tag of a + * rational number, so that we can simple swap the sign of that constant. + */ + if (lval->ident==iCONSTEXPR && lval->tag==sc_rationaltag && sc_rationaltag!=0) { + if (rational_digits==0) { + #if PAWN_CELL_SIZE==32 + float *f = (float *)&lval->constval; + #elif PAWN_CELL_SIZE==64 + double *f = (double *)&lval->constval; + #else + #error Unsupported cell size + #endif + *f= - *f; /* this modifies lval->constval */ + } else { + /* the negation of a fixed point number is just an integer negation */ + lval->constval=-lval->constval; + } /* if */ + } else if (check_userop(neg,lval->tag,0,1,NULL,&lval->tag)) { + lval->ident=iEXPRESSION; + lval->constval=0; + } else { + neg(); /* arithmic negation */ + lval->constval=-lval->constval; + } /* if */ + return FALSE; + case tLABEL: /* tagname override */ + tag=pc_addtag(st); + lval->cmptag=tag; + lvalue=hier2(lval); + lval->tag=tag; + return lvalue; + case tDEFINED: + paranthese=0; + while (matchtoken('(')) + paranthese++; + tok=lex(&val,&st); + if (tok!=tSYMBOL) + return error(20,st); /* illegal symbol name */ + sym=findloc(st); + if (sym==NULL) + sym=findglb(st,sSTATEVAR); + if (sym!=NULL && sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) + sym=NULL; /* symbol is not a function, it is in the table, but not "defined" */ + val= (sym!=NULL); + if (!val && find_subst(st,strlen(st))!=NULL) + val=1; + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval= val; + lval->tag=pc_addtag("bool"); + ldconst(lval->constval,sPRI); + while (paranthese--) + needtoken(')'); + return FALSE; + case tSIZEOF: + paranthese=0; + while (matchtoken('(')) + paranthese++; + tok=lex(&val,&st); + if (tok!=tSYMBOL) + return error(20,st); /* illegal symbol name */ + sym=findloc(st); + if (sym==NULL) + sym=findglb(st,sSTATEVAR); + if (sym==NULL) + return error(17,st); /* undefined symbol */ + if (sym->ident==iCONSTEXPR) + error(39); /* constant symbol has no size */ + else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) + error(72); /* "function" symbol has no size */ + else if ((sym->usage & uDEFINE)==0) + return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval=1; /* preset */ + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + int level; + symbol *idxsym=NULL; + symbol *subsym=sym; + for (level=0; matchtoken('['); level++) { + idxsym=NULL; + if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) { + char *idxname; + int cmptag=subsym->x.tags.index; + tokeninfo(&val,&idxname); + if ((idxsym=findconst(idxname,&cmptag))==NULL) + error(80,idxname); /* unknown symbol, or non-constant */ + else if (cmptag>1) + error(91,idxname); /* ambiguous constant */ + } /* if */ + needtoken(']'); + if (subsym!=NULL) + subsym=finddepend(subsym); + } /* for */ + if (level>sym->dim.array.level+1) + error(28,sym->name); /* invalid subscript */ + else if (level==sym->dim.array.level+1) + lval->constval= (idxsym!=NULL && idxsym->dim.array.length>0) ? idxsym->dim.array.length : 1; + else + lval->constval=array_levelsize(sym,level); + if (lval->constval==0 && strchr((char *)lptr,PREPROC_TERM)==NULL) + error(224,st); /* indeterminate array size in "sizeof" expression */ + } /* if */ + ldconst(lval->constval,sPRI); + while (paranthese--) + needtoken(')'); + return FALSE; + case tTAGOF: + paranthese=0; + while (matchtoken('(')) + paranthese++; + tok=lex(&val,&st); + if (tok!=tSYMBOL && tok!=tLABEL) + return error(20,st); /* illegal symbol name */ + if (tok==tLABEL) { + constvalue *tagsym=find_constval(&tagname_tab,st,0); + tag=(int)((tagsym!=NULL) ? tagsym->value : 0); + } else { + sym=findloc(st); + if (sym==NULL) + sym=findglb(st,sSTATEVAR); + if (sym==NULL) + return error(17,st); /* undefined symbol */ + if ((sym->usage & uDEFINE)==0) + return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ + tag=sym->tag; + } /* if */ + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + int level; + symbol *idxsym=NULL; + symbol *subsym=sym; + for (level=0; matchtoken('['); level++) { + idxsym=NULL; + if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) { + char *idxname; + int cmptag=subsym->x.tags.index; + tokeninfo(&val,&idxname); + if ((idxsym=findconst(idxname,&cmptag))==NULL) + error(80,idxname); /* unknown symbol, or non-constant */ + else if (cmptag>1) + error(91,idxname); /* ambiguous constant */ + } /* if */ + needtoken(']'); + if (subsym!=NULL) + subsym=finddepend(subsym); + } /* for */ + if (level>sym->dim.array.level+1) + error(28,sym->name); /* invalid subscript */ + else if (level==sym->dim.array.level+1 && idxsym!=NULL) + tag= idxsym->x.tags.index; + } /* if */ + exporttag(tag); + clear_value(lval); + lval->ident=iCONSTEXPR; + lval->constval=tag | PUBLICTAG; + ldconst(lval->constval,sPRI); + while (paranthese--) + needtoken(')'); + return FALSE; + case tSTATE: { + constvalue *automaton; + constvalue *state; + if (sc_getstateid(&automaton,&state)) { + assert(automaton!=NULL); + assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0); + loadreg(automaton->value,sALT); + assert(state!=NULL); + ldconst(state->value,sPRI); + ob_eq(); + clear_value(lval); + lval->ident=iEXPRESSION; + lval->tag=pc_addtag("bool"); + } /* if */ + return FALSE; + } /* case */ + default: + lexpush(); + lvalue=hier1(lval); + /* check for postfix operators */ + if (matchtoken(';')) { + /* Found a ';', do not look further for postfix operators */ + lexpush(); /* push ';' back after successful match */ + return lvalue; + } else if (matchtoken(tTERM)) { + /* Found a newline that ends a statement (this is the case when + * semicolons are optional). Note that an explicit semicolon was + * handled above. This case is similar, except that the token must + * not be pushed back. + */ + return lvalue; + } else { + tok=lex(&val,&st); + switch (tok) { + case tINC: /* lval++ */ + if (!lvalue) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + /* on incrementing array cells, the address in PRI must be saved for + * incremening the value, whereas the current value must be in PRI + * on exit. + */ + saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); + if (saveresult) + pushreg(sPRI); /* save address in PRI */ + rvalue(lval); /* read current value into PRI */ + if (saveresult) + swap1(); /* save PRI on the stack, restore address in PRI */ + if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag)) + inc(lval); /* increase variable afterwards */ + if (saveresult) + popreg(sPRI); /* restore PRI (result of rvalue()) */ + sideeffect=TRUE; + return FALSE; /* result is no longer lvalue */ + case tDEC: /* lval-- */ + if (!lvalue) + return error(22); /* must be lvalue */ + assert(lval->sym!=NULL); + if ((lval->sym->usage & uCONST)!=0) + return error(22); /* assignment to const argument */ + saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); + if (saveresult) + pushreg(sPRI); /* save address in PRI */ + rvalue(lval); /* read current value into PRI */ + if (saveresult) + swap1(); /* save PRI on the stack, restore address in PRI */ + if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag)) + dec(lval); /* decrease variable afterwards */ + if (saveresult) + popreg(sPRI); /* restore PRI (result of rvalue()) */ + sideeffect=TRUE; + return FALSE; + case tCHAR: /* char (compute required # of cells */ + if (lval->ident==iCONSTEXPR) { + lval->constval *= sCHARBITS/8; /* from char to bytes */ + lval->constval = (lval->constval + sizeof(cell)-1) / sizeof(cell); + } else { + if (lvalue) + rvalue(lval); /* fetch value if not already in PRI */ + char2addr(); /* from characters to bytes */ + addconst(sizeof(cell)-1); /* make sure the value is rounded up */ + addr2cell(); /* truncate to number of cells */ + } /* if */ + return FALSE; + default: + lexpush(); + return lvalue; + } /* switch */ + } /* if */ + } /* switch */ +} + +/* hier1 + * + * The highest hierarchy level: it looks for pointer and array indices + * and function calls. + * Generates code to fetch a pointer value if it is indexed and code to + * add to the pointer value or the array address (the address is already + * read at primary()). It also generates code to fetch a function address + * if that hasn't already been done at primary() (check lval[4]) and calls + * callfunction() to call the function. + */ +static int hier1(value *lval1) +{ + int lvalue,index,tok,symtok; + cell val,cidx; + value lval2={0}; + char *st; + char close; + symbol *sym; + symbol dummysymbol,*cursym; /* for changing the index tags in case of enumerated pseudo-arrays */ + + lvalue=primary(lval1); + symtok=tokeninfo(&val,&st); /* get token read by primary() */ + cursym=lval1->sym; +restart: + sym=cursym; + if (matchtoken('[') || matchtoken('{') || matchtoken('(')) { + tok=tokeninfo(&val,&st); /* get token read by matchtoken() */ + if (sym==NULL && symtok!=tSYMBOL) { + /* we do not have a valid symbol and we appear not to have read a valid + * symbol name (so it is unlikely that we would have read a name of an + * undefined symbol) */ + error(29); /* expression error, assumed 0 */ + lexpush(); /* analyse '(', '{' or '[' again later */ + return FALSE; + } /* if */ + if (tok=='[' || tok=='{') { /* subscript */ + close = (char)((tok=='[') ? ']' : '}'); + if (sym==NULL) { /* sym==NULL if lval is a constant or a literal */ + error(28,""); /* cannot subscript */ + needtoken(close); + return FALSE; + } else if (sym->ident!=iARRAY && sym->ident!=iREFARRAY){ + error(28,sym->name); /* cannot subscript, variable is not an array */ + needtoken(close); + return FALSE; + } else if (sym->dim.array.level>0 && close!=']') { + error(51); /* invalid subscript, must use [ ] */ + needtoken(close); + return FALSE; + } /* if */ + /* set the tag to match (enumeration fields as indices) */ + lval2.cmptag=sym->x.tags.index; + stgget(&index,&cidx); /* mark position in code generator */ + pushreg(sPRI); /* save base address of the array */ + if (hier14(&lval2)) /* create expression for the array index */ + rvalue(&lval2); + if (lval2.ident==iARRAY || lval2.ident==iREFARRAY) + error(33,lval2.sym->name); /* array must be indexed */ + needtoken(close); + if (!matchtag(sym->x.tags.index,lval2.tag,TRUE)) + error(213); + if (lval2.ident==iCONSTEXPR) { /* constant expression */ + stgdel(index,cidx); /* scratch generated code */ + if (lval1->arrayidx!=NULL) { /* keep constant index, for checking */ + assert(sym->dim.array.level>=0 && sym->dim.array.levelarrayidx[sym->dim.array.level]=lval2.constval; + } /* if */ + if (close==']') { + /* normal array index */ + if (lval2.constval<0 || sym->dim.array.length!=0 && sym->dim.array.length<=lval2.constval) + error(32,sym->name); /* array index out of bounds */ + if (lval2.constval!=0) { + /* don't add offsets for zero subscripts */ + #if PAWN_CELL_SIZE==16 + ldconst(lval2.constval<<1,sALT); + #elif PAWN_CELL_SIZE==32 + ldconst(lval2.constval<<2,sALT); + #elif PAWN_CELL_SIZE==64 + ldconst(lval2.constval<<3,sALT); + #else + #error Unsupported cell size + #endif + ob_add(); + } /* if */ + } else { + /* character index */ + if (lval2.constval<0 || sym->dim.array.length!=0 + && sym->dim.array.length*((8*sizeof(cell))/sCHARBITS)<=(ucell)lval2.constval) + error(32,sym->name); /* array index out of bounds */ + if (lval2.constval!=0) { + /* don't add offsets for zero subscripts */ + #if sCHARBITS==16 + ldconst(lval2.constval<<1,sALT);/* 16-bit character */ + #else + ldconst(lval2.constval,sALT); /* 8-bit character */ + #endif + ob_add(); + } /* if */ + charalign(); /* align character index into array */ + } /* if */ + /* if the array index is a field from an enumeration, get the tag name + * from the field and save the size of the field too. + */ + assert(lval2.sym==NULL || lval2.sym->dim.array.level==0); + if (lval2.sym!=NULL && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) { + lval1->tag=lval2.sym->x.tags.index; + lval1->constval=lval2.sym->dim.array.length; + } /* if */ + } else { + /* array index is not constant */ + lval1->arrayidx=NULL; /* reset, so won't be checked */ + if (close==']') { + if (sym->dim.array.length!=0) + ffbounds(sym->dim.array.length-1); /* run time check for array bounds */ + cell2addr(); /* normal array index */ + } else { + if (sym->dim.array.length!=0) + ffbounds(sym->dim.array.length*(32/sCHARBITS)-1); + char2addr(); /* character array index */ + } /* if */ + popreg(sALT); + ob_add(); /* base address was popped into secondary register */ + if (close!=']') + charalign(); /* align character index into array */ + } /* if */ + /* the indexed item may be another array (multi-dimensional arrays) */ + assert(cursym==sym && sym!=NULL); /* should still be set */ + if (sym->dim.array.level>0) { + assert(close==']'); /* checked earlier */ + assert(cursym==lval1->sym); + /* read the offset to the subarray and add it to the current address */ + lval1->ident=iARRAYCELL; + pushreg(sPRI); /* the optimizer makes this to a MOVE.alt */ + rvalue(lval1); + popreg(sALT); + ob_add(); + /* adjust the "value" structure and find the referenced array */ + lval1->ident=iREFARRAY; + lval1->sym=finddepend(sym); + assert(lval1->sym!=NULL); + assert(lval1->sym->dim.array.level==sym->dim.array.level-1); + cursym=lval1->sym; + /* try to parse subsequent array indices */ + lvalue=FALSE; /* for now, a iREFARRAY is no lvalue */ + goto restart; + } /* if */ + assert(sym->dim.array.level==0); + /* set type to fetch... INDIRECTLY */ + lval1->ident= (char)((close==']') ? iARRAYCELL : iARRAYCHAR); + /* if the array index is a field from an enumeration, get the tag name + * from the field and save the size of the field too. Otherwise, the + * tag is the one from the array symbol. + */ + if (lval2.ident==iCONSTEXPR && lval2.sym!=NULL + && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) + { + lval1->tag=lval2.sym->x.tags.index; + lval1->constval=lval2.sym->dim.array.length; + if (lval2.tag==sym->x.tags.index && lval1->constval>1 && matchtoken('[')) { + /* an array indexed with an enumeration field may be considered a sub-array */ + lexpush(); + lvalue=FALSE; /* for now, a iREFARRAY is no lvalue */ + lval1->ident=iREFARRAY; + /* initialize a dummy symbol, which is a copy of the current symbol, + * but with an adjusted index tag + */ + assert(sym!=NULL); + dummysymbol=*sym; + /* get the tag of the root of the enumeration */ + assert(lval2.sym!=NULL); + dummysymbol.x.tags.index=lval2.sym->x.tags.field; + dummysymbol.dim.array.length=lval2.sym->dim.array.length; + cursym=&dummysymbol; + /* recurse */ + goto restart; + } /* if */ + } else { + assert(sym!=NULL); + if (cursym!=&dummysymbol) + lval1->tag=sym->tag; + lval1->constval=0; + } /* if */ + /* a cell in an array is an lvalue, a character in an array is not + * always a *valid* lvalue */ + return TRUE; + } else { /* tok=='(' -> function(...) */ + assert(tok=='('); + if (sym==NULL + || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) + { + if (sym==NULL && sc_status==statFIRST) { + /* could be a "use before declaration"; in that case, create a stub + * function so that the usage can be marked. + */ + sym=fetchfunc(lastsymbol,0); + if (sym==NULL) + error(103); /* insufficient memory */ + markusage(sym,uREAD); + } else { + return error(12); /* invalid function call */ + } /* if */ + } else if ((sym->usage & uMISSING)!=0) { + char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ + funcdisplayname(symname,sym->name); + error(4,symname); /* function not defined */ + } /* if */ + callfunction(sym,lval1,TRUE); + return FALSE; /* result of function call is no lvalue */ + } /* if */ + } /* if */ + if (sym!=NULL && lval1->ident==iFUNCTN) { + assert(sym->ident==iFUNCTN); + if (sc_allowproccall) { + callfunction(sym,lval1,FALSE); + } else { + lval1->sym=NULL; + lval1->ident=iEXPRESSION; + lval1->constval=0; + lval1->tag=0; + error(76); /* invalid function call, or syntax error */ + } /* if */ + return FALSE; + } /* if */ + return lvalue; +} + +/* primary + * + * Returns 1 if the operand is an lvalue (everything except arrays, functions + * constants and -of course- errors). + * Generates code to fetch the address of arrays. Code for constants is + * already generated by constant(). + * This routine first clears the entire lval array (all fields are set to 0). + * + * Global references: sc_intest (may be altered, but restored upon termination) + */ +static int primary(value *lval) +{ + char *st; + int lvalue,tok; + cell val; + symbol *sym; + + if (matchtoken('(')){ /* sub-expression - (expression,...) */ + PUSHSTK_I(sc_intest); + PUSHSTK_I(sc_allowtags); + + sc_intest=FALSE; /* no longer in "test" expression */ + sc_allowtags=TRUE; /* allow tagnames to be used in parenthesized expressions */ + sc_allowproccall=FALSE; + do + lvalue=hier14(lval); + while (matchtoken(',')); + needtoken(')'); + lexclr(FALSE); /* clear lex() push-back, it should have been + * cleared already by needtoken() */ + sc_allowtags=(short)POPSTK_I(); + sc_intest=(short)POPSTK_I(); + return lvalue; + } /* if */ + + clear_value(lval); /* clear lval */ + tok=lex(&val,&st); + if (tok==tSYMBOL) { + /* lastsymbol is char[sNAMEMAX+1], lex() should have truncated any symbol + * to sNAMEMAX significant characters */ + assert(strlen(st)ident==iLABEL) { + error(29); /* expression error, assumed 0 */ + ldconst(0,sPRI); /* load 0 */ + return FALSE; /* return 0 for labels (expression error) */ + } /* if */ + lval->sym=sym; + lval->ident=sym->ident; + lval->tag=sym->tag; + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + address(sym,sPRI); /* get starting address in primary register */ + return FALSE; /* return 0 for array (not lvalue) */ + } else { + return TRUE; /* return 1 if lvalue (not label or array) */ + } /* if */ + } /* if */ + /* now try a global variable */ + if ((sym=findglb(st,sSTATEVAR))!=0) { + if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { + /* if the function is only in the table because it was inserted as a + * stub in the first pass (i.e. it was "used" but never declared or + * implemented, issue an error + */ + if ((sym->usage & uPROTOTYPED)==0) + error(17,st); + } else { + if ((sym->usage & uDEFINE)==0) + error(17,st); + lval->sym=sym; + lval->ident=sym->ident; + lval->tag=sym->tag; + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + address(sym,sPRI); /* get starting address in primary register */ + return FALSE; /* return 0 for array (not lvalue) */ + } else { + return TRUE; /* return 1 if lvalue (not function or array) */ + } /* if */ + } /* if */ + } else { + if (!sc_allowproccall) + return error(17,st); /* undefined symbol */ + /* an unknown symbol, but used in a way compatible with the "procedure + * call" syntax. So assume that the symbol refers to a function. + */ + assert(sc_status==statFIRST); + sym=fetchfunc(st,0); + if (sym==NULL) + error(103); /* insufficient memory */ + } /* if */ + assert(sym!=NULL); + assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); + lval->sym=sym; + lval->ident=sym->ident; + lval->tag=sym->tag; + return FALSE; /* return 0 for function (not an lvalue) */ + } /* if */ + lexpush(); /* push the token, it is analyzed by constant() */ + if (constant(lval)==0) { + error(29); /* expression error, assumed 0 */ + ldconst(0,sPRI); /* load 0 */ + } /* if */ + return FALSE; /* return 0 for constants (or errors) */ +} + +static void clear_value(value *lval) +{ + lval->sym=NULL; + lval->constval=0L; + lval->tag=0; + lval->ident=0; + lval->boolresult=FALSE; + /* do not clear lval->arrayidx, it is preset in hier14() */ + /* do not clear lval->cmptag */ +} + +static void setdefarray(cell *string,cell size,cell array_sz,cell *dataaddr,int fconst) +{ + /* The routine must copy the default array data onto the heap, as to avoid + * that a function can change the default value. An optimization is that + * the default array data is "dumped" into the data segment only once (on the + * first use). + */ + assert(string!=NULL); + assert(size>0); + /* check whether to dump the default array */ + assert(dataaddr!=NULL); + if (sc_status==statWRITE && *dataaddr<0) { + int i; + *dataaddr=(litidx+glb_declared)*sizeof(cell); + for (i=0; i=size); + modheap((int)array_sz*sizeof(cell)); + /* ??? should perhaps fill with zeros first */ + memcopy(size*sizeof(cell)); + moveto1(); + } /* if */ +} + +static int findnamedarg(arginfo *arg,char *name) +{ + int i; + + for (i=0; arg[i].ident!=0 && arg[i].ident!=iVARARGS; i++) + if (strcmp(arg[i].name,name)==0) + return i; + return -1; +} + +static int checktag(int tags[],int numtags,int exprtag) +{ + int i; + + assert(tags!=0); + assert(numtags>0); + for (i=0; iident=iEXPRESSION; /* preset, may be changed later */ + lval_result->constval=0; + lval_result->tag=sym->tag; + /* check whether this is a function that returns an array */ + symret=finddepend(sym); + assert(symret==NULL || symret->ident==iREFARRAY); + if (symret!=NULL) { + int retsize; + /* allocate space on the heap for the array, and pass the pointer to the + * reserved memory block as a hidden parameter + */ + retsize=(int)array_totalsize(symret); + assert(retsize>0); + modheap(retsize*sizeof(cell));/* address is in ALT */ + pushreg(sALT); /* pass ALT as the last (hidden) parameter */ + decl_heap+=retsize; + /* also mark the ident of the result as "array" */ + lval_result->ident=iREFARRAY; + lval_result->sym=symret; + } /* if */ + locheap=decl_heap; + + nesting++; + assert(nest_stkusage>=0); + #if !defined NDEBUG + if (nesting==1) + assert(nest_stkusage==0); + #endif + sc_allowproccall=FALSE; /* parameters may not use procedure call syntax */ + + if ((sym->flags & flgDEPRICATED)!=0) { + char *ptr= (sym->documentation!=NULL) ? sym->documentation : ""; + error(234,sym->name,ptr); /* depricated (probably a native function) */ + } /* if */ + + /* run through the arguments */ + arg=sym->dim.arglist; + assert(arg!=NULL); + stgmark(sSTARTREORDER); + memset(arglist,ARG_UNHANDLED,sizeof arglist); + if (matchparanthesis) { + /* Opening brace was already parsed, if closing brace follows, this + * call passes no parameters. + */ + close=matchtoken(')'); + } else { + /* When we find an end of line here, it may be a function call passing + * no parameters, or it may be that the first parameter is on a line + * below. But as a parameter can be anything, this is difficult to check. + * The only simple check that we have is the use of "named parameters". + */ + close=matchtoken(tTERM); + if (close) { + close=!matchtoken('.'); + if (!close) + lexpush(); /* reset the '.' */ + } /* if */ + } /* if */ + if (!close) { + do { + if (matchtoken('.')) { + namedparams=TRUE; + if (needtoken(tSYMBOL)) + tokeninfo(&lexval,&lexstr); + else + lexstr=""; + argpos=findnamedarg(arg,lexstr); + if (argpos<0) { + error(17,lexstr); /* undefined symbol */ + break; /* exit loop, argpos is invalid */ + } /* if */ + needtoken('='); + argidx=argpos; + } else { + if (namedparams) + error(44); /* positional parameters must precede named parameters */ + argpos=nargs; + } /* if */ + /* the number of arguments this was already checked at the declaration + * of the function; check it again for functions with a variable + * argument list + */ + if (argpos>=sMAXARGS) + error(45); /* too many function arguments */ + stgmark((char)(sEXPRSTART+argpos));/* mark beginning of new expression in stage */ + if (arglist[argpos]!=ARG_UNHANDLED) + error(58); /* argument already set */ + if (matchtoken('_')) { + arglist[argpos]=ARG_IGNORED; /* flag argument as "present, but ignored" */ + if (arg[argidx].ident==0 || arg[argidx].ident==iVARARGS) { + error(202); /* argument count mismatch */ + } else if (!arg[argidx].hasdefault) { + error(34,nargs+1); /* argument has no default value */ + } /* if */ + if (arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS) + argidx++; + /* The rest of the code to handle default values is at the bottom + * of this routine where default values for unspecified parameters + * are (also) handled. Note that above, the argument is flagged as + * ARG_IGNORED. + */ + } else { + arglist[argpos]=ARG_DONE; /* flag argument as "present" */ + if (arg[argidx].numtags==1) /* set the expected tag, if any */ + lval.cmptag=arg[argidx].tags[0]; + lvalue=hier14(&lval); + assert(sc_status==statFIRST || arg[argidx].tags!=NULL); + switch (arg[argidx].ident) { + case 0: + error(202); /* argument count mismatch */ + break; + case iVARARGS: + /* always pass by reference */ + if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) { + assert(lval.sym!=NULL); + if ((lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) { + /* treat a "const" variable passed to a function with a non-const + * "variable argument list" as a constant here */ + if (!lvalue) { + error(22); /* need lvalue */ + } else { + rvalue(&lval); /* get value in PRI */ + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + } else if (lvalue) { + address(lval.sym,sPRI); + } else { + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + } else if (lval.ident==iCONSTEXPR || lval.ident==iEXPRESSION + || lval.ident==iARRAYCHAR) + { + /* fetch value if needed */ + if (lval.ident==iARRAYCHAR) + rvalue(&lval); + /* allocate a cell on the heap and store the + * value (already in PRI) there */ + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + /* ??? handle const array passed by reference */ + /* otherwise, the address is already in PRI */ + if (lval.sym!=NULL) + markusage(lval.sym,uWRITTEN); + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + break; + case iVARIABLE: + if (lval.ident==iLABEL || lval.ident==iFUNCTN || lval.ident==iREFFUNC + || lval.ident==iARRAY || lval.ident==iREFARRAY) + error(35,argidx+1); /* argument type mismatch */ + if (lvalue) + rvalue(&lval); /* get value (direct or indirect) */ + /* otherwise, the expression result is already in PRI */ + assert(arg[argidx].numtags>0); + check_userop(NULL,lval.tag,arg[argidx].tags[0],2,NULL,&lval.tag); + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + argidx++; /* argument done */ + break; + case iREFERENCE: + if (!lvalue || lval.ident==iARRAYCHAR) + error(35,argidx+1); /* argument type mismatch */ + if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) + error(35,argidx+1); /* argument type mismatch */ + if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) { + if (lvalue) { + assert(lval.sym!=NULL); + address(lval.sym,sPRI); + } else { + setheap_pri(); /* address of the value on the heap in PRI */ + heapalloc++; + nest_stkusage++; + } /* if */ + } /* if */ + /* otherwise, the address is already in PRI */ + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + argidx++; /* argument done */ + if (lval.sym!=NULL) + markusage(lval.sym,uWRITTEN); + break; + case iREFARRAY: + if (lval.ident!=iARRAY && lval.ident!=iREFARRAY + && lval.ident!=iARRAYCELL) + { + error(35,argidx+1); /* argument type mismatch */ + break; + } /* if */ + if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) + error(35,argidx+1); /* argument type mismatch */ + /* Verify that the dimensions match with those in arg[argidx]. + * A literal array always has a single dimension. + * An iARRAYCELL parameter is also assumed to have a single dimension. + */ + if (lval.sym==NULL || lval.ident==iARRAYCELL) { + if (arg[argidx].numdim!=1) { + error(48); /* array dimensions must match */ + } else if (arg[argidx].dim[0]!=0) { + assert(arg[argidx].dim[0]>0); + if (lval.ident==iARRAYCELL) { + error(47); /* array sizes must match */ + } else { + assert(lval.constval!=0); /* literal array must have a size */ + /* A literal array must have exactly the same size as the + * function argument; a literal string may be smaller than + * the function argument. + */ + if (lval.constval>0 && arg[argidx].dim[0]!=lval.constval + || lval.constval<0 && arg[argidx].dim[0] < -lval.constval) + error(47); /* array sizes must match */ + } /* if */ + } /* if */ + if (lval.ident!=iARRAYCELL) { + /* save array size, for default values with uSIZEOF flag */ + cell array_sz=lval.constval; + assert(array_sz!=0);/* literal array must have a size */ + if (array_sz<0) + array_sz= -array_sz; + append_constval(&arrayszlst,arg[argidx].name,array_sz,0); + } /* if */ + } else { + symbol *sym=lval.sym; + short level=0; + assert(sym!=NULL); + if (sym->dim.array.level+1!=arg[argidx].numdim) + error(48); /* array dimensions must match */ + /* the lengths for all dimensions must match, unless the dimension + * length was defined at zero (which means "undefined") + */ + while (sym->dim.array.level>0) { + assert(leveldim.array.length!=arg[argidx].dim[level]) + error(47); /* array sizes must match */ + else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) + error(229,sym->name); /* index tag mismatch */ + append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); + sym=finddepend(sym); + assert(sym!=NULL); + level++; + } /* if */ + /* the last dimension is checked too, again, unless it is zero */ + assert(leveldim.array.length!=arg[argidx].dim[level]) + error(47); /* array sizes must match */ + else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) + error(229,sym->name); /* index tag mismatch */ + append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); + } /* if */ + /* address already in PRI */ + if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) + error(213); + if (lval.tag!=0) + append_constval(&taglst,arg[argidx].name,lval.tag,0); + // ??? set uWRITTEN? + argidx++; /* argument done */ + break; + } /* switch */ + pushreg(sPRI); /* store the function argument on the stack */ + markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ + nest_stkusage++; + } /* if */ + assert(arglist[argpos]!=ARG_UNHANDLED); + nargs++; + if (matchparanthesis) { + close=matchtoken(')'); + if (!close) /* if not paranthese... */ + if (!needtoken(',')) /* ...should be comma... */ + break; /* ...but abort loop if neither */ + } else { + close=!matchtoken(','); + if (close) { /* if not comma... */ + if (needtoken(tTERM)==1)/* ...must be end of statement */ + lexpush(); /* push again, because end of statement is analised later */ + } /* if */ + } /* if */ + } while (!close && freading && !matchtoken(tENDEXPR)); /* do */ + } /* if */ + /* check remaining function arguments (they may have default values) */ + for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) { + if (arglist[argidx]==ARG_DONE) + continue; /* already seen and handled this argument */ + /* in this first stage, we also skip the arguments with uSIZEOF and uTAGOF; + * these are handled last + */ + if ((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0) { + assert(arg[argidx].ident==iVARIABLE); + continue; + } /* if */ + stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */ + if (arg[argidx].hasdefault) { + if (arg[argidx].ident==iREFARRAY) { + short level; + setdefarray(arg[argidx].defvalue.array.data, + arg[argidx].defvalue.array.size, + arg[argidx].defvalue.array.arraysize, + &arg[argidx].defvalue.array.addr, + (arg[argidx].usage & uCONST)!=0); + if ((arg[argidx].usage & uCONST)==0) { + heapalloc+=arg[argidx].defvalue.array.arraysize; + nest_stkusage+=arg[argidx].defvalue.array.arraysize; + } /* if */ + /* keep the lengths of all dimensions of a multi-dimensional default array */ + assert(arg[argidx].numdim>0); + if (arg[argidx].numdim==1) { + append_constval(&arrayszlst,arg[argidx].name,arg[argidx].defvalue.array.arraysize,0); + } else { + for (level=0; level0); + check_userop(NULL,arg[argidx].defvalue_tag,arg[argidx].tags[0],2,NULL,&dummytag); + assert(dummytag==arg[argidx].tags[0]); + } /* if */ + pushreg(sPRI); /* store the function argument on the stack */ + markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ + nest_stkusage++; + } else { + error(202,argidx); /* argument count mismatch */ + } /* if */ + if (arglist[argidx]==ARG_UNHANDLED) + nargs++; + arglist[argidx]=ARG_DONE; + } /* for */ + /* now a second loop to catch the arguments with default values that are + * the "sizeof" or "tagof" of other arguments + */ + for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) { + constvalue *asz; + cell array_sz; + if (arglist[argidx]==ARG_DONE) + continue; /* already seen and handled this argument */ + stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */ + assert(arg[argidx].ident==iVARIABLE); /* if "sizeof", must be single cell */ + /* if unseen, must be "sizeof" or "tagof" */ + assert((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0); + if ((arg[argidx].hasdefault & uSIZEOF)!=0) { + /* find the argument; if it isn't found, the argument's default value + * was a "sizeof" of a non-array (a warning for this was already given + * when declaring the function) + */ + asz=find_constval(&arrayszlst,arg[argidx].defvalue.size.symname, + arg[argidx].defvalue.size.level); + if (asz!=NULL) { + array_sz=asz->value; + if (array_sz==0) + error(224,arg[argidx].name); /* indeterminate array size in "sizeof" expression */ + } else { + array_sz=1; + } /* if */ + } else { + asz=find_constval(&taglst,arg[argidx].defvalue.size.symname, + arg[argidx].defvalue.size.level); + if (asz != NULL) { + exporttag(asz->value); + array_sz=asz->value | PUBLICTAG; /* must be set, because it just was exported */ + } else { + array_sz=0; + } /* if */ + } /* if */ + ldconst(array_sz,sPRI); + pushreg(sPRI); /* store the function argument on the stack */ + markexpr(sPARM,NULL,0); + nest_stkusage++; + if (arglist[argidx]==ARG_UNHANDLED) + nargs++; + arglist[argidx]=ARG_DONE; + } /* for */ + stgmark(sENDREORDER); /* mark end of reversed evaluation */ + pushval((cell)nargs*sizeof(cell)); + nest_stkusage++; + ffcall(sym,NULL,nargs); + if (sc_status!=statSKIP) + markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ + if ((sym->usage & uNATIVE)!=0 &&sym->x.lib!=NULL) + sym->x.lib->value += 1; /* increment "usage count" of the library */ + modheap(-heapalloc*sizeof(cell)); + if (symret!=NULL) + popreg(sPRI); /* pop hidden parameter as function result */ + sideeffect=TRUE; /* assume functions carry out a side-effect */ + delete_consttable(&arrayszlst); /* clear list of array sizes */ + delete_consttable(&taglst); /* clear list of parameter tags */ + + /* maintain max. amount of memory used */ + { + long totalsize; + totalsize=declared+decl_heap+1; /* local variables & return value size, + * +1 for PROC opcode */ + if (lval_result->ident==iREFARRAY) + totalsize++; /* add hidden parameter (on the stack) */ + if ((sym->usage & uNATIVE)==0) + totalsize++; /* add "call" opcode */ + totalsize+=nest_stkusage; + assert(curfunc!=NULL); + if (curfunc->x.stacksizex.stacksize=totalsize; + nest_stkusage-=nargs+heapalloc+1; /* stack/heap space, +1 for argcount param */ + /* if there is a syntax error in the script, the stack calculation is + * probably incorrect; but we may not allow it to drop below zero + */ + if (nest_stkusage<0) + nest_stkusage=0; + } + + /* scrap any arrays left on the heap, with the exception of the array that + * this function has as a result (in other words, scrap all arrays on the + * heap that caused by expressions in the function arguments) + */ + assert(decl_heap>=locheap); + modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ + decl_heap=locheap; + nesting--; +} + +/* dbltest + * + * Returns a non-zero value if lval1 an array and lval2 is not an array and + * the operation is addition or subtraction. + * + * Returns the "shift" count (1 for 16-bit, 2 for 32-bit) to align a cell + * to an array offset. + */ +static int dbltest(void (*oper)(),value *lval1,value *lval2) +{ + if ((oper!=ob_add) && (oper!=ob_sub)) + return 0; + if (lval1->ident!=iARRAY) + return 0; + if (lval2->ident==iARRAY) + return 0; + return sizeof(cell)/2; /* 1 for 16-bit, 2 for 32-bit */ +} + +/* commutative + * + * Test whether an operator is commutative, i.e. x oper y == y oper x. + * Commutative operators are: + (addition) + * * (multiplication) + * == (equality) + * != (inequality) + * & (bitwise and) + * ^ (bitwise xor) + * | (bitwise or) + * + * If in an expression, code for the left operand has been generated and + * the right operand is a constant and the operator is commutative, the + * precautionary "push" of the primary register is scrapped and the constant + * is read into the secondary register immediately. + */ +static int commutative(void (*oper)()) +{ + return oper==ob_add || oper==os_mult + || oper==ob_eq || oper==ob_ne + || oper==ob_and || oper==ob_xor || oper==ob_or; +} + +/* constant + * + * Generates code to fetch a number, a literal character (which is returned + * by lex() as a number as well) or a literal string (lex() stores the + * strings in the literal queue). If the operand was a number, it is stored + * in lval->constval. + * + * The function returns 1 if the token was a constant or a string, 0 + * otherwise. + */ +static int constant(value *lval) +{ + int tok,index,ident; + cell val,item,cidx; + char *st; + symbol *sym; + int cmptag=lval->cmptag; + + tok=lex(&val,&st); + if (tok==tSYMBOL && (sym=findconst(st,&cmptag))!=0) { + if (cmptag>1) + error(91,sym->name); /* ambiguity: multiple matching constants (different tags) */ + lval->constval=sym->addr; + ldconst(lval->constval,sPRI); + lval->ident=iCONSTEXPR; + lval->tag=sym->tag; + lval->sym=sym; + markusage(sym,uREAD); + } else if (tok==tNUMBER) { + lval->constval=val; + ldconst(lval->constval,sPRI); + lval->ident=iCONSTEXPR; + } else if (tok==tRATIONAL) { + lval->constval=val; + ldconst(lval->constval,sPRI); + lval->ident=iCONSTEXPR; + lval->tag=sc_rationaltag; + } else if (tok==tSTRING) { + /* lex() stores starting index of string in the literal table in 'val' */ + ldconst((val+glb_declared)*sizeof(cell),sPRI); + lval->ident=iARRAY; /* pretend this is a global array */ + lval->constval=val-litidx; /* constval == the negative value of the + * size of the literal array; using a negative + * value distinguishes between literal arrays + * and literal strings (this was done for + * array assignment). */ + } else if (tok=='{') { + int tag,lasttag=-1; + val=litidx; + do { + /* cannot call constexpr() here, because "staging" is already turned + * on at this point */ + assert(staging); + stgget(&index,&cidx); /* mark position in code generator */ + ident=expression(&item,&tag,NULL,FALSE); + stgdel(index,cidx); /* scratch generated code */ + if (ident!=iCONSTEXPR) + error(8); /* must be constant expression */ + if (lasttag<0) + lasttag=tag; + else if (!matchtag(lasttag,tag,FALSE)) + error(213); /* tagname mismatch */ + litadd(item); /* store expression result in literal table */ + } while (matchtoken(',')); + if (!needtoken('}')) + lexclr(FALSE); + ldconst((val+glb_declared)*sizeof(cell),sPRI); + lval->ident=iARRAY; /* pretend this is a global array */ + lval->constval=litidx-val; /* constval == the size of the literal array */ + } else { + return FALSE; /* no, it cannot be interpreted as a constant */ + } /* if */ + return TRUE; /* yes, it was a constant value */ +} + diff --git a/compiler-init/sc4.c b/compiler-init/sc4.c new file mode 100644 index 00000000..70e39dde --- /dev/null +++ b/compiler-init/sc4.c @@ -0,0 +1,1331 @@ +/* Pawn compiler - code generation (unoptimized "assembler" code) + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc4.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include +#include /* for _MAX_PATH */ +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +static int fcurseg; /* the file number (fcurrent) for the active segment */ + + +/* When a subroutine returns to address 0, the AMX must halt. In earlier + * releases, the RET and RETN opcodes checked for the special case 0 address. + * Today, the compiler simply generates a HALT instruction at address 0. So + * a subroutine can savely return to 0, and then encounter a HALT. + */ +SC_FUNC void writeleader(symbol *root) +{ + int lbl_nostate,lbl_table; + int statecount; + symbol *sym; + constvalue *fsa, *state, *stlist; + int fsa_id,listid; + char lbl_default[sNAMEMAX+1]; + + assert(code_idx==0); + + begcseg(); + stgwrite(";program exit point\n"); + stgwrite("\thalt 0\n\n"); + code_idx+=opcodes(1)+opargs(1); /* calculate code length */ + + /* check whether there are any functions that have states */ + for (sym=root->next; sym!=NULL; sym=sym->next) + if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) + break; + if (sym==NULL) + return; /* no function has states, nothing to do next */ + + /* generate an error function that is called for an undefined state */ + stgwrite("\n;exit point for functions called from the wrong state\n"); + lbl_nostate=getlabel(); + setlabel(lbl_nostate); + stgwrite("\thalt "); + outval(AMX_ERR_INVSTATE,TRUE); + code_idx+=opcodes(1)+opargs(1); /* calculate code length */ + + /* write the "state-selectors" table with all automatons (update the + * automatons structure too, as we are now assigning the address to + * each automaton state-selector variable) + */ + assert(glb_declared==0); + begdseg(); + for (fsa=sc_automaton_tab.next; fsa!=NULL; fsa=fsa->next) { + defstorage(); + stgwrite("0\t; automaton "); + if (strlen(fsa->name)==0) + stgwrite("(anonymous)"); + else + stgwrite(fsa->name); + stgwrite("\n"); + fsa->value=glb_declared*sizeof(cell); + glb_declared++; + } /* for */ + + /* write stubs and jump tables for all state functions */ + begcseg(); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) { + stlist=sym->states->next; + assert(stlist!=NULL); /* there should be at least one state item */ + listid=stlist->index; + assert(listid==-1 || listid>0); + if (listid==-1 && stlist->next!=NULL) { + /* first index is the "fallback", take the next one (if available) */ + stlist=stlist->next; + listid=stlist->index; + } /* if */ + if (listid==-1) { + /* first index is the fallback, there is no second... */ + strcpy(stlist->name,"0"); /* insert dummy label number */ + /* this is an error, but we postpone adding the error message until the + * function definition + */ + continue; + } /* if */ + /* generate label numbers for all statelist ids */ + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + assert(strlen(stlist->name)==0); + strcpy(stlist->name,itoh(getlabel())); + } /* for */ + if (strcmp(sym->name,uENTRYFUNC)==0) + continue; /* do not generate stubs for this special function */ + sym->addr=code_idx; /* fix the function address now */ + /* get automaton id for this function */ + assert(listid>0); + fsa_id=state_getfsa(listid); + assert(fsa_id>=0); /* automaton 0 exists */ + fsa=automaton_findid(fsa_id); + /* count the number of states actually used; at the sane time, check + * whether there is a default state function + */ + statecount=0; + strcpy(lbl_default,itoh(lbl_nostate)); + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + if (stlist->index==-1) { + assert(strlen(stlist->name)name); + } else { + statecount+=state_count(stlist->index); + } /* if */ + } /* for */ + /* generate a stub entry for the functions */ + stgwrite("\tload.pri "); + outval(fsa->value,FALSE); + stgwrite("\t; "); + stgwrite(sym->name); + stgwrite("\n"); + code_idx+=opcodes(1)+opargs(1); /* calculate code length */ + lbl_table=getlabel(); + ffswitch(lbl_table); + /* generate the jump table */ + setlabel(lbl_table); + ffcase(statecount,lbl_default,TRUE); + for (state=sc_state_tab.next; state!=NULL; state=state->next) { + if (state->index==fsa_id) { + /* find the label for this list id */ + for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { + if (stlist->index!=-1 && state_inlist(stlist->index,(int)state->value)) { + ffcase(state->value,stlist->name,FALSE); + break; + } /* if */ + } /* for */ + if (stlist==NULL && strtol(lbl_default,NULL,16)==lbl_nostate) + error(230,state->name,sym->name); /* unimplemented state, no fallback */ + } /* if (state belongs to automaton of function) */ + } /* for (state) */ + stgwrite("\n"); + } /* if (is function, used & having states) */ + } /* for (sym) */ +} + +/* writetrailer + * Not much left of this once important function. + * + * Global references: pc_stksize (referred to only) + * sc_dataalign (referred to only) + * code_idx (altered) + * glb_declared (altered) + */ +SC_FUNC void writetrailer(void) +{ + assert(sc_dataalign % opcodes(1) == 0); /* alignment must be a multiple of + * the opcode size */ + assert(sc_dataalign!=0); + + /* pad code to align data segment */ + if ((code_idx % sc_dataalign)!=0) { + begcseg(); + while ((code_idx % sc_dataalign)!=0) + nooperation(); + } /* if */ + + /* pad data segment to align the stack and the heap */ + assert(litidx==0); /* literal queue should have been emptied */ + assert(sc_dataalign % sizeof(cell) == 0); + if (((glb_declared*sizeof(cell)) % sc_dataalign)!=0) { + begdseg(); + defstorage(); + while (((glb_declared*sizeof(cell)) % sc_dataalign)!=0) { + stgwrite("0 "); + glb_declared++; + } /* while */ + } /* if */ + + stgwrite("\nSTKSIZE "); /* write stack size (align stack top) */ + outval(pc_stksize - (pc_stksize % sc_dataalign), TRUE); +} + +/* + * Start (or restart) the CODE segment. + * + * In fact, the code and data segment specifiers are purely informational; + * the "DUMP" instruction itself already specifies that the following values + * should go to the data segment. All other instructions go to the code + * segment. + * + * Global references: curseg + * fcurrent + */ +SC_FUNC void begcseg(void) +{ + if (curseg!=sIN_CSEG || fcurrent!=fcurseg) { + stgwrite("\n"); + stgwrite("CODE "); + outval(fcurrent,FALSE); + stgwrite("\t; "); + outval(code_idx,TRUE); + curseg=sIN_CSEG; + fcurseg=fcurrent; + } /* endif */ +} + +/* + * Start (or restart) the DATA segment. + * + * Global references: curseg + */ +SC_FUNC void begdseg(void) +{ + if (curseg!=sIN_DSEG || fcurrent!=fcurseg) { + stgwrite("\n"); + stgwrite("DATA "); + outval(fcurrent,FALSE); + stgwrite("\t; "); + outval((glb_declared-litidx)*sizeof(cell),TRUE); + curseg=sIN_DSEG; + fcurseg=fcurrent; + } /* if */ +} + +SC_FUNC void setline(int chkbounds) +{ + if ((sc_debug & sSYMBOLIC)!=0 || chkbounds && (sc_debug & sCHKBOUNDS)!=0) { + /* generate a "break" (start statement) opcode rather than a "line" opcode + * because earlier versions of Small/Pawn have an incompatible version of the + * line opcode + */ + stgwrite("\tbreak\t; "); + outval(code_idx,TRUE); + code_idx+=opcodes(1); + } /* if */ +} + +SC_FUNC void setfiledirect(char *name) +{ + if (sc_status==statFIRST && sc_listing) { + assert(name!=NULL); + pc_writeasm(outf,"#file "); + pc_writeasm(outf,name); + pc_writeasm(outf,"\n"); + } /* if */ +} + +SC_FUNC void setlinedirect(int line) +{ + if (sc_status==statFIRST && sc_listing) { + char string[40]; + sprintf(string,"#line %d\n",line); + pc_writeasm(outf,string); + } /* if */ +} + +/* setlabel + * + * Post a code label (specified as a number), on a new line. + */ +SC_FUNC void setlabel(int number) +{ + assert(number>=0); + stgwrite("l."); + stgwrite((char *)itoh(number)); + /* To assist verification of the assembled code, put the address of the + * label as a comment. However, labels that occur inside an expression + * may move (through optimization or through re-ordering). So write the + * address only if it is known to accurate. + */ + if (!staging) { + stgwrite("\t\t; "); + outval(code_idx,FALSE); + } /* if */ + stgwrite("\n"); +} + +/* Write a token that signifies the start or end of an expression or special + * statement. This allows several simple optimizations by the peephole + * optimizer. + */ +SC_FUNC void markexpr(optmark type,const char *name,cell offset) +{ + switch (type) { + case sEXPR: + stgwrite("\t;$exp\n"); + break; + case sPARM: + stgwrite("\t;$par\n"); + break; + case sLDECL: + assert(name!=NULL); + stgwrite("\t;$lcl "); + stgwrite(name); + stgwrite(" "); + outval(offset,TRUE); + break; + default: + assert(0); + } /* switch */ +} + +/* startfunc - declare a CODE entry point (function start) + * + * Global references: funcstatus (referred to only) + */ +SC_FUNC void startfunc(char *fname) +{ + stgwrite("\tproc"); + if (sc_asmfile) { + char symname[2*sNAMEMAX+16]; + funcdisplayname(symname,fname); + stgwrite("\t; "); + stgwrite(symname); + } /* if */ + stgwrite("\n"); + code_idx+=opcodes(1); +} + +/* endfunc + * + * Declare a CODE ending point (function end) + */ +SC_FUNC void endfunc(void) +{ + stgwrite("\n"); /* skip a line */ +} + +/* alignframe + * + * Aligns the frame (and the stack) of the current function to a multiple + * of the specified byte count. Two caveats: the alignment ("numbytes") should + * be a power of 2, and this alignment must be done right after the frame + * is set up (before the first variable is declared) + */ +SC_FUNC void alignframe(int numbytes) +{ + #if !defined NDEBUG + /* "numbytes" should be a power of 2 for this code to work */ + int i,count=0; + for (i=0; isym; + if (lval->ident==iARRAYCELL) { + /* indirect fetch, address already in PRI */ + stgwrite("\tload.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* indirect fetch of a character from a pack, address already in PRI */ + stgwrite("\tlodb.i "); + outval(sCHARBITS/8,TRUE); /* read one or two bytes */ + code_idx+=opcodes(1)+opargs(1); + } else if (lval->ident==iREFERENCE) { + /* indirect fetch, but address not yet in PRI */ + assert(sym!=NULL); + assert(sym->vclass==sLOCAL);/* global references don't exist in Pawn */ + if (sym->vclass==sLOCAL) + stgwrite("\tlref.s.pri "); + else + stgwrite("\tlref.pri "); + outval(sym->addr,TRUE); + markusage(sym,uREAD); + code_idx+=opcodes(1)+opargs(1); + } else { + /* direct or stack relative fetch */ + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tload.s.pri "); + else + stgwrite("\tload.pri "); + outval(sym->addr,TRUE); + markusage(sym,uREAD); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* Get the address of a symbol into the primary or alternate register (used + * for arrays, and for passing arguments by reference). + */ +SC_FUNC void address(symbol *sym,regid reg) +{ + assert(sym!=NULL); + assert(reg==sPRI || reg==sALT); + /* the symbol can be a local array, a global array, or an array + * that is passed by reference. + */ + if (sym->ident==iREFARRAY || sym->ident==iREFERENCE) { + /* reference to a variable or to an array; currently this is + * always a local variable */ + switch (reg) { + case sPRI: + stgwrite("\tload.s.pri "); + break; + case sALT: + stgwrite("\tload.s.alt "); + break; + } /* switch */ + } else { + /* a local array or local variable */ + switch (reg) { + case sPRI: + if (sym->vclass==sLOCAL) + stgwrite("\taddr.pri "); + else + stgwrite("\tconst.pri "); + break; + case sALT: + if (sym->vclass==sLOCAL) + stgwrite("\taddr.alt "); + else + stgwrite("\tconst.alt "); + break; + } /* switch */ + } /* if */ + outval(sym->addr,TRUE); + markusage(sym,uREAD); + code_idx+=opcodes(1)+opargs(1); +} + +/* store + * + * Saves the contents of "primary" into a memory cell, either directly + * or indirectly (at the address given in the alternate register). + */ +SC_FUNC void store(value *lval) +{ + symbol *sym; + + sym=lval->sym; + if (lval->ident==iARRAYCELL) { + /* store at address in ALT */ + stgwrite("\tstor.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* store at address in ALT */ + stgwrite("\tstrb.i "); + outval(sCHARBITS/8,TRUE); /* write one or two bytes */ + code_idx+=opcodes(1)+opargs(1); + } else if (lval->ident==iREFERENCE) { + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tsref.s.pri "); + else + stgwrite("\tsref.pri "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } else { + assert(sym!=NULL); + markusage(sym,uWRITTEN); + if (sym->vclass==sLOCAL) + stgwrite("\tstor.s.pri "); + else + stgwrite("\tstor.pri "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* Get a cell from a fixed address in memory */ +SC_FUNC void loadreg(cell address,regid reg) +{ + assert(reg==sPRI || reg==sALT); + if (reg==sPRI) + stgwrite("\tload.pri "); + else + stgwrite("\tload.alt "); + outval(address,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* Store a cell into a fixed address in memory */ +SC_FUNC void storereg(cell address,regid reg) +{ + assert(reg==sPRI || reg==sALT); + if (reg==sPRI) + stgwrite("\tstor.pri "); + else + stgwrite("\tstor.alt "); + outval(address,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* source must in PRI, destination address in ALT. The "size" + * parameter is in bytes, not cells. + */ +SC_FUNC void memcopy(cell size) +{ + stgwrite("\tmovs "); + outval(size,TRUE); + + code_idx+=opcodes(1)+opargs(1); +} + +/* Address of the source must already have been loaded in PRI + * "size" is the size in bytes (not cells). + */ +SC_FUNC void copyarray(symbol *sym,cell size) +{ + assert(sym!=NULL); + /* the symbol can be a local array, a global array, or an array + * that is passed by reference. + */ + if (sym->ident==iREFARRAY) { + /* reference to an array; currently this is always a local variable */ + assert(sym->vclass==sLOCAL); /* symbol must be stack relative */ + stgwrite("\tload.s.alt "); + } else { + /* a local or global array */ + if (sym->vclass==sLOCAL) + stgwrite("\taddr.alt "); + else + stgwrite("\tconst.alt "); + } /* if */ + outval(sym->addr,TRUE); + markusage(sym,uWRITTEN); + + code_idx+=opcodes(1)+opargs(1); + memcopy(size); +} + +SC_FUNC void fillarray(symbol *sym,cell size,cell value) +{ + ldconst(value,sPRI); /* load value in PRI */ + + assert(sym!=NULL); + /* the symbol can be a local array, a global array, or an array + * that is passed by reference. + */ + if (sym->ident==iREFARRAY) { + /* reference to an array; currently this is always a local variable */ + assert(sym->vclass==sLOCAL); /* symbol must be stack relative */ + stgwrite("\tload.s.alt "); + } else { + /* a local or global array */ + if (sym->vclass==sLOCAL) + stgwrite("\taddr.alt "); + else + stgwrite("\tconst.alt "); + } /* if */ + outval(sym->addr,TRUE); + markusage(sym,uWRITTEN); + + assert(size>0); + stgwrite("\tfill "); + outval(size,TRUE); + + code_idx+=opcodes(2)+opargs(2); +} + +/* Instruction to get an immediate value into the primary or the alternate + * register + */ +SC_FUNC void ldconst(cell val,regid reg) +{ + assert(reg==sPRI || reg==sALT); + switch (reg) { + case sPRI: + if (val==0) { + stgwrite("\tzero.pri\n"); + code_idx+=opcodes(1); + } else { + stgwrite("\tconst.pri "); + outval(val, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ + break; + case sALT: + if (val==0) { + stgwrite("\tzero.alt\n"); + code_idx+=opcodes(1); + } else { + stgwrite("\tconst.alt "); + outval(val, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ + break; + } /* switch */ +} + +/* Copy value in alternate register to the primary register */ +SC_FUNC void moveto1(void) +{ + stgwrite("\tmove.pri\n"); + code_idx+=opcodes(1)+opargs(0); +} + +/* Push primary or the alternate register onto the stack + */ +SC_FUNC void pushreg(regid reg) +{ + assert(reg==sPRI || reg==sALT); + switch (reg) { + case sPRI: + stgwrite("\tpush.pri\n"); + break; + case sALT: + stgwrite("\tpush.alt\n"); + break; + } /* switch */ + code_idx+=opcodes(1); +} + +/* + * Push a constant value onto the stack + */ +SC_FUNC void pushval(cell val) +{ + stgwrite("\tpush.c "); + outval(val, TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* Pop stack into the primary or the alternate register + */ +SC_FUNC void popreg(regid reg) +{ + assert(reg==sPRI || reg==sALT); + switch (reg) { + case sPRI: + stgwrite("\tpop.pri\n"); + break; + case sALT: + stgwrite("\tpop.alt\n"); + break; + } /* switch */ + code_idx+=opcodes(1); +} + +/* + * swap the top-of-stack with the value in primary register + */ +SC_FUNC void swap1(void) +{ + stgwrite("\tswap.pri\n"); + code_idx+=opcodes(1); +} + +/* Switch statements + * The "switch" statement generates a "case" table using the "CASE" opcode. + * The case table contains a list of records, each record holds a comparison + * value and a label to branch to on a match. The very first record is an + * exception: it holds the size of the table (excluding the first record) and + * the label to branch to when none of the values in the case table match. + * The case table is sorted on the comparison value. This allows more advanced + * abstract machines to sift the case table with a binary search. + */ +SC_FUNC void ffswitch(int label) +{ + stgwrite("\tswitch "); + outval(label,TRUE); /* the label is the address of the case table */ + code_idx+=opcodes(1)+opargs(1); +} + +SC_FUNC void ffcase(cell value,char *labelname,int newtable) +{ + if (newtable) { + stgwrite("\tcasetbl\n"); + code_idx+=opcodes(1); + } /* if */ + stgwrite("\tcase "); + outval(value,FALSE); + stgwrite(" "); + stgwrite(labelname); + stgwrite("\n"); + code_idx+=opcodes(0)+opargs(2); +} + +/* + * Call specified function + */ +SC_FUNC void ffcall(symbol *sym,const char *label,int numargs) +{ + char symname[2*sNAMEMAX+16]; + + assert(sym!=NULL); + assert(sym->ident==iFUNCTN); + if (sc_asmfile) + funcdisplayname(symname,sym->name); + if ((sym->usage & uNATIVE)!=0) { + /* reserve a SYSREQ id if called for the first time */ + assert(label==NULL); + if (sc_status==statWRITE && (sym->usage & uREAD)==0 && sym->addr>=0) + sym->addr=ntv_funcid++; + stgwrite("\tsysreq.c "); + outval(sym->addr,FALSE); + if (sc_asmfile) { + stgwrite("\t; "); + stgwrite(symname); + } /* if */ + stgwrite("\n"); /* write on a separate line, to mark a sequence point for the peephole optimizer */ + stgwrite("\tstack "); + outval((numargs+1)*sizeof(cell), TRUE); + code_idx+=opcodes(2)+opargs(2); + } else { + /* normal function */ + stgwrite("\tcall "); + if (label!=NULL) { + stgwrite("l."); + stgwrite(label); + } else { + stgwrite(sym->name); + } /* if */ + if (sc_asmfile + && (label!=NULL || !isalpha(sym->name[0]) && sym->name[0]!='_' && sym->name[0]!=sc_ctrlchar)) + { + stgwrite("\t; "); + stgwrite(symname); + } /* if */ + stgwrite("\n"); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* Return from function + * + * Global references: funcstatus (referred to only) + */ +SC_FUNC void ffret(int remparams) +{ + if (remparams) + stgwrite("\tretn\n"); + else + stgwrite("\tret\n"); + code_idx+=opcodes(1); +} + +SC_FUNC void ffabort(int reason) +{ + stgwrite("\thalt "); + outval(reason,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +SC_FUNC void ffbounds(cell size) +{ + if ((sc_debug & sCHKBOUNDS)!=0) { + stgwrite("\tbounds "); + outval(size,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* + * Jump to local label number (the number is converted to a name) + */ +SC_FUNC void jumplabel(int number) +{ + stgwrite("\tjump "); + outval(number,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Define storage (global and static variables) + */ +SC_FUNC void defstorage(void) +{ + stgwrite("dump "); +} + +/* + * Inclrement/decrement stack pointer. Note that this routine does + * nothing if the delta is zero. + */ +SC_FUNC void modstk(int delta) +{ + if (delta) { + stgwrite("\tstack "); + outval(delta, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* set the stack to a hard offset from the frame */ +SC_FUNC void setstk(cell value) +{ + stgwrite("\tlctrl 5\n"); /* get FRM in PRI */ + assert(value<=0); /* STK should always become <= FRM */ + if (value<0) { + stgwrite("\tadd.c "); + outval(value, TRUE); /* add (negative) offset */ + code_idx+=opcodes(1)+opargs(1); + // ??? write zeros in the space between STK and the value in PRI (the new stk) + // get value of STK in ALT + // zero PRI + // need new FILL opcode that takes a variable size + } /* if */ + stgwrite("\tsctrl 4\n"); /* store in STK */ + code_idx+=opcodes(2)+opargs(2); +} + +SC_FUNC void modheap(int delta) +{ + if (delta) { + stgwrite("\theap "); + outval(delta, TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +SC_FUNC void setheap_pri(void) +{ + stgwrite("\theap "); /* ALT = HEA++ */ + outval(sizeof(cell), TRUE); + stgwrite("\tstor.i\n"); /* store PRI (default value) at address ALT */ + stgwrite("\tmove.pri\n"); /* move ALT to PRI: PRI contains the address */ + code_idx+=opcodes(3)+opargs(1); +} + +SC_FUNC void setheap(cell value) +{ + stgwrite("\tconst.pri "); /* load default value in PRI */ + outval(value, TRUE); + code_idx+=opcodes(1)+opargs(1); + setheap_pri(); +} + +/* + * Convert a cell number to a "byte" address; i.e. double or quadruple + * the primary register. + */ +SC_FUNC void cell2addr(void) +{ + #if PAWN_CELL_SIZE==16 + stgwrite("\tshl.c.pri 1\n"); + #elif PAWN_CELL_SIZE==32 + stgwrite("\tshl.c.pri 2\n"); + #elif PAWN_CELL_SIZE==64 + stgwrite("\tshl.c.pri 3\n"); + #else + #error Unsupported cell size + #endif + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Double or quadruple the alternate register. + */ +SC_FUNC void cell2addr_alt(void) +{ + #if PAWN_CELL_SIZE==16 + stgwrite("\tshl.c.alt 1\n"); + #elif PAWN_CELL_SIZE==32 + stgwrite("\tshl.c.alt 2\n"); + #elif PAWN_CELL_SIZE==64 + stgwrite("\tshl.c.alt 3\n"); + #else + #error Unsupported cell size + #endif + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Convert "distance of addresses" to "number of cells" in between. + * Or convert a number of packed characters to the number of cells (with + * truncation). + */ +SC_FUNC void addr2cell(void) +{ + #if PAWN_CELL_SIZE==16 + stgwrite("\tshr.c.pri 1\n"); + #elif PAWN_CELL_SIZE==32 + stgwrite("\tshr.c.pri 2\n"); + #elif PAWN_CELL_SIZE==64 + stgwrite("\tshr.c.pri 3\n"); + #else + #error Unsupported cell size + #endif + code_idx+=opcodes(1)+opargs(1); +} + +/* Convert from character index to byte address. This routine does + * nothing if a character has the size of a byte. + */ +SC_FUNC void char2addr(void) +{ + #if sCHARBITS==16 + stgwrite("\tshl.c.pri 1\n"); + code_idx+=opcodes(1)+opargs(1); + #endif +} + +/* Align PRI (which should hold a character index) to an address. + * The first character in a "pack" occupies the highest bits of + * the cell. This is at the lower memory address on Big Endian + * computers and on the higher address on Little Endian computers. + * The ALIGN.pri/alt instructions must solve this machine dependence; + * that is, on Big Endian computers, ALIGN.pri/alt shuold do nothing + * and on Little Endian computers they should toggle the address. + */ +SC_FUNC void charalign(void) +{ + stgwrite("\talign.pri "); + outval(sCHARBITS/8,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Add a constant to the primary register. + */ +SC_FUNC void addconst(cell value) +{ + if (value!=0) { + stgwrite("\tadd.c "); + outval(value,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* + * signed multiply of primary and secundairy registers (result in primary) + */ +SC_FUNC void os_mult(void) +{ + stgwrite("\tsmul\n"); + code_idx+=opcodes(1); +} + +/* + * signed divide of alternate register by primary register (quotient in + * primary; remainder in alternate) + */ +SC_FUNC void os_div(void) +{ + stgwrite("\tsdiv.alt\n"); + code_idx+=opcodes(1); +} + +/* + * modulus of (alternate % primary), result in primary (signed) + */ +SC_FUNC void os_mod(void) +{ + stgwrite("\tsdiv.alt\n"); + stgwrite("\tmove.pri\n"); /* move ALT to PRI */ + code_idx+=opcodes(2); +} + +/* + * Add primary and alternate registers (result in primary). + */ +SC_FUNC void ob_add(void) +{ + stgwrite("\tadd\n"); + code_idx+=opcodes(1); +} + +/* + * subtract primary register from alternate register (result in primary) + */ +SC_FUNC void ob_sub(void) +{ + stgwrite("\tsub.alt\n"); + code_idx+=opcodes(1); +} + +/* + * arithmic shift left alternate register the number of bits + * given in the primary register (result in primary). + * There is no need for a "logical shift left" routine, since + * logical shift left is identical to arithmic shift left. + */ +SC_FUNC void ob_sal(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tshl\n"); + code_idx+=opcodes(2); +} + +/* + * arithmic shift right alternate register the number of bits + * given in the primary register (result in primary). + */ +SC_FUNC void os_sar(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tsshr\n"); + code_idx+=opcodes(2); +} + +/* + * logical (unsigned) shift right of the alternate register by the + * number of bits given in the primary register (result in primary). + */ +SC_FUNC void ou_sar(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tshr\n"); + code_idx+=opcodes(2); +} + +/* + * inclusive "or" of primary and alternate registers (result in primary) + */ +SC_FUNC void ob_or(void) +{ + stgwrite("\tor\n"); + code_idx+=opcodes(1); +} + +/* + * "exclusive or" of primary and alternate registers (result in primary) + */ +SC_FUNC void ob_xor(void) +{ + stgwrite("\txor\n"); + code_idx+=opcodes(1); +} + +/* + * "and" of primary and secundairy registers (result in primary) + */ +SC_FUNC void ob_and(void) +{ + stgwrite("\tand\n"); + code_idx+=opcodes(1); +} + +/* + * test ALT==PRI; result in primary register (1 or 0). + */ +SC_FUNC void ob_eq(void) +{ + stgwrite("\teq\n"); + code_idx+=opcodes(1); +} + +/* + * test ALT!=PRI + */ +SC_FUNC void ob_ne(void) +{ + stgwrite("\tneq\n"); + code_idx+=opcodes(1); +} + +/* The abstract machine defines the relational instructions so that PRI is + * on the left side and ALT on the right side of the operator. For example, + * SLESS sets PRI to either 1 or 0 depending on whether the expression + * "PRI < ALT" is true. + * + * The compiler generates comparisons with ALT on the left side of the + * relational operator and PRI on the right side. The XCHG instruction + * prefixing the relational operators resets this. We leave it to the + * peephole optimizer to choose more compact instructions where possible. + */ + +/* Relational operator prefix for chained relational expressions. The + * "suffix" code restores the stack. + * For chained relational operators, the goal is to keep the comparison + * result "so far" in PRI and the value of the most recent operand in + * ALT, ready for a next comparison. + * The "prefix" instruction pushed the comparison result (PRI) onto the + * stack and moves the value of ALT into PRI. If there is a next comparison, + * PRI can now serve as the "left" operand of the relational operator. + */ +SC_FUNC void relop_prefix(void) +{ + stgwrite("\tpush.pri\n"); + stgwrite("\tmove.pri\n"); + code_idx+=opcodes(2); +} + +SC_FUNC void relop_suffix(void) +{ + stgwrite("\tswap.alt\n"); + stgwrite("\tand\n"); + stgwrite("\tpop.alt\n"); + code_idx+=opcodes(3); +} + +/* + * test ALTPRI (signed) + */ +SC_FUNC void os_gt(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tsgrtr\n"); + code_idx+=opcodes(2); +} + +/* + * test ALT>=PRI (signed) + */ +SC_FUNC void os_ge(void) +{ + stgwrite("\txchg\n"); + stgwrite("\tsgeq\n"); + code_idx+=opcodes(2); +} + +/* + * logical negation of primary register + */ +SC_FUNC void lneg(void) +{ + stgwrite("\tnot\n"); + code_idx+=opcodes(1); +} + +/* + * two's complement primary register + */ +SC_FUNC void neg(void) +{ + stgwrite("\tneg\n"); + code_idx+=opcodes(1); +} + +/* + * one's complement of primary register + */ +SC_FUNC void invert(void) +{ + stgwrite("\tinvert\n"); + code_idx+=opcodes(1); +} + +/* + * nop + */ +SC_FUNC void nooperation(void) +{ + stgwrite("\tnop\n"); + code_idx+=opcodes(1); +} + + +/* increment symbol + */ +SC_FUNC void inc(value *lval) +{ + symbol *sym; + + sym=lval->sym; + if (lval->ident==iARRAYCELL) { + /* indirect increment, address already in PRI */ + stgwrite("\tinc.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* indirect increment of single character, address already in PRI */ + stgwrite("\tpush.pri\n"); + stgwrite("\tpush.alt\n"); + stgwrite("\tmove.alt\n"); /* copy address */ + stgwrite("\tlodb.i "); /* read from PRI into PRI */ + outval(sCHARBITS/8,TRUE); /* read one or two bytes */ + stgwrite("\tinc.pri\n"); + stgwrite("\tstrb.i "); /* write PRI to ALT */ + outval(sCHARBITS/8,TRUE); /* write one or two bytes */ + stgwrite("\tpop.alt\n"); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(8)+opargs(2); + } else if (lval->ident==iREFERENCE) { + assert(sym!=NULL); + stgwrite("\tpush.pri\n"); + /* load dereferenced value */ + assert(sym->vclass==sLOCAL); /* global references don't exist in Pawn */ + if (sym->vclass==sLOCAL) + stgwrite("\tlref.s.pri "); + else + stgwrite("\tlref.pri "); + outval(sym->addr,TRUE); + /* increment */ + stgwrite("\tinc.pri\n"); + /* store dereferenced value */ + if (sym->vclass==sLOCAL) + stgwrite("\tsref.s.pri "); + else + stgwrite("\tsref.pri "); + outval(sym->addr,TRUE); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(5)+opargs(2); + } else { + /* local or global variable */ + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tinc.s "); + else + stgwrite("\tinc "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* decrement symbol + * + * in case of an integer pointer, the symbol must be incremented by 2. + */ +SC_FUNC void dec(value *lval) +{ + symbol *sym; + + sym=lval->sym; + if (lval->ident==iARRAYCELL) { + /* indirect decrement, address already in PRI */ + stgwrite("\tdec.i\n"); + code_idx+=opcodes(1); + } else if (lval->ident==iARRAYCHAR) { + /* indirect decrement of single character, address already in PRI */ + stgwrite("\tpush.pri\n"); + stgwrite("\tpush.alt\n"); + stgwrite("\tmove.alt\n"); /* copy address */ + stgwrite("\tlodb.i "); /* read from PRI into PRI */ + outval(sCHARBITS/8,TRUE); /* read one or two bytes */ + stgwrite("\tdec.pri\n"); + stgwrite("\tstrb.i "); /* write PRI to ALT */ + outval(sCHARBITS/8,TRUE); /* write one or two bytes */ + stgwrite("\tpop.alt\n"); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(8)+opargs(2); + } else if (lval->ident==iREFERENCE) { + assert(sym!=NULL); + stgwrite("\tpush.pri\n"); + /* load dereferenced value */ + assert(sym->vclass==sLOCAL); /* global references don't exist in Pawn */ + if (sym->vclass==sLOCAL) + stgwrite("\tlref.s.pri "); + else + stgwrite("\tlref.pri "); + outval(sym->addr,TRUE); + /* decrement */ + stgwrite("\tdec.pri\n"); + /* store dereferenced value */ + if (sym->vclass==sLOCAL) + stgwrite("\tsref.s.pri "); + else + stgwrite("\tsref.pri "); + outval(sym->addr,TRUE); + stgwrite("\tpop.pri\n"); + code_idx+=opcodes(5)+opargs(2); + } else { + /* local or global variable */ + assert(sym!=NULL); + if (sym->vclass==sLOCAL) + stgwrite("\tdec.s "); + else + stgwrite("\tdec "); + outval(sym->addr,TRUE); + code_idx+=opcodes(1)+opargs(1); + } /* if */ +} + +/* + * Jumps to "label" if PRI != 0 + */ +SC_FUNC void jmp_ne0(int number) +{ + stgwrite("\tjnz "); + outval(number,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* + * Jumps to "label" if PRI == 0 + */ +SC_FUNC void jmp_eq0(int number) +{ + stgwrite("\tjzer "); + outval(number,TRUE); + code_idx+=opcodes(1)+opargs(1); +} + +/* write a value in hexadecimal; optionally adds a newline */ +SC_FUNC void outval(cell val,int newline) +{ + stgwrite(itoh(val)); + if (newline) + stgwrite("\n"); +} diff --git a/compiler-init/sc5.c b/compiler-init/sc5.c new file mode 100644 index 00000000..d9d0298a --- /dev/null +++ b/compiler-init/sc5.c @@ -0,0 +1,228 @@ +/* Pawn compiler - Error message system + * In fact a very simple system, using only 'panic mode'. + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc5.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ + #include +#endif +#if defined LINUX || defined __GNUC__ + #include +#endif +#include +#include +#include /* ANSI standardized variable argument list functions */ +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +#if defined _MSC_VER + #pragma warning(push) + #pragma warning(disable:4125) /* decimal digit terminates octal escape sequence */ +#endif + +#include "sc5.scp" + +#if defined _MSC_VER + #pragma warning(pop) +#endif + +#define NUM_WARNINGS (sizeof warnmsg / sizeof warnmsg[0]) +static unsigned char warndisable[(NUM_WARNINGS + 7) / 8]; /* 8 flags in a char */ + +static int errflag; +static int errstart; /* line number at which the instruction started */ +static int errline; /* forced line number for the error message */ + +/* error + * + * Outputs an error message (note: msg is passed optionally). + * If an error is found, the variable "errflag" is set and subsequent + * errors are ignored until lex() finds a semicolumn or a keyword + * (lex() resets "errflag" in that case). + * + * Global references: inpfname (reffered to only) + * fline (reffered to only) + * fcurrent (reffered to only) + * errflag (altered) + */ +SC_FUNC int error(int number,...) +{ +static char *prefix[3]={ "error", "fatal error", "warning" }; +static int lastline,errorcount; +static short lastfile; + char *msg,*pre; + va_list argptr; + char string[128]; + + /* errflag is reset on each semicolon. + * In a two-pass compiler, an error should not be reported twice. Therefore + * the error reporting is enabled only in the second pass (and only when + * actually producing output). Fatal errors may never be ignored. + */ + if ((errflag || sc_status!=statWRITE) && (number<100 || number>=200)) + return 0; + + /* also check for disabled warnings */ + if (number>=200) { + int index=(number-200)/8; + int mask=1 << ((number-200)%8); + if ((warndisable[index] & mask)!=0) + return 0; + } /* if */ + + if (number<100){ + msg=errmsg[number-1]; + pre=prefix[0]; + errflag=TRUE; /* set errflag (skip rest of erroneous expression) */ + errnum++; + } else if (number<200){ + msg=fatalmsg[number-100]; + pre=prefix[1]; + errnum++; /* a fatal error also counts as an error */ + } else { + msg=warnmsg[number-200]; + pre=prefix[2]; + warnnum++; + } /* if */ + + strexpand(string,(unsigned char *)msg,sizeof string,SCPACK_TABLE); + + assert(errstart<=fline); + if (errline>0) + errstart=errline; + else + errline=fline; + assert(errstart<=errline); + va_start(argptr,number); + if (strlen(errfname)==0) { + int start= (errstart==errline) ? -1 : errstart; + if (pc_error(number,string,inpfname,start,errline,argptr)) { + if (outf!=NULL) { + pc_closeasm(outf,TRUE); + outf=NULL; + } /* if */ + longjmp(errbuf,3); /* user abort */ + } /* if */ + } else { + FILE *fp=fopen(errfname,"a"); + if (fp!=NULL) { + if (errstart>=0 && errstart!=errline) + fprintf(fp,"%s(%d -- %d) : %s %03d: ",inpfname,errstart,errline,pre,number); + else + fprintf(fp,"%s(%d) : %s %03d: ",inpfname,errline,pre,number); + vfprintf(fp,string,argptr); + fclose(fp); + } /* if */ + } /* if */ + va_end(argptr); + + if (number>=100 && number<200 || errnum>25){ + if (strlen(errfname)==0) { + va_start(argptr,number); + pc_error(0,"\nCompilation aborted.",NULL,0,0,argptr); + va_end(argptr); + } /* if */ + if (outf!=NULL) { + pc_closeasm(outf,TRUE); + outf=NULL; + } /* if */ + longjmp(errbuf,2); /* fatal error, quit */ + } /* if */ + + errline=-1; + /* check whether we are seeing many errors on the same line */ + if ((errstart<0 && lastline!=fline) || lastlinefline || fcurrent!=lastfile) + errorcount=0; + lastline=fline; + lastfile=fcurrent; + if (number<200) + errorcount++; + if (errorcount>=3) + error(107); /* too many error/warning messages on one line */ + + return 0; +} + +SC_FUNC void errorset(int code,int line) +{ + switch (code) { + case sRESET: + errflag=FALSE; /* start reporting errors */ + break; + case sFORCESET: + errflag=TRUE; /* stop reporting errors */ + break; + case sEXPRMARK: + errstart=fline; /* save start line number */ + break; + case sEXPRRELEASE: + errstart=-1; /* forget start line number */ + errline=-1; + break; + case sSETPOS: + errline=line; + break; + } /* switch */ +} + +/* sc_enablewarning() + * Enables or disables a warning (errors cannot be disabled). + * Initially all warnings are enabled. The compiler does this by setting bits + * for the *disabled* warnings and relying on the array to be zero-initialized. + * + * Parameter enable can be: + * o 0 for disable + * o 1 for enable + * o 2 for toggle + */ +int pc_enablewarning(int number,int enable) +{ + int index; + unsigned char mask; + + if (number<200) + return FALSE; /* errors and fatal errors cannot be disabled */ + number -= 200; + if (number>=NUM_WARNINGS) + return FALSE; + + index=number/8; + mask=(unsigned char)(1 << (number%8)); + switch (enable) { + case 0: + warndisable[index] |= mask; + break; + case 1: + warndisable[index] &= (unsigned char)~mask; + break; + case 2: + warndisable[index] ^= mask; + break; + } /* switch */ + + return TRUE; +} + +#undef SCPACK_TABLE diff --git a/compiler-init/sc5.scp b/compiler-init/sc5.scp new file mode 100644 index 00000000..a08315c2 --- /dev/null +++ b/compiler-init/sc5.scp @@ -0,0 +1,342 @@ +/* Pawn compiler - Error message strings (plain and compressed formats) + * + * Copyright (c) ITB CompuPhase, 2000-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc5.sch 3590 2006-06-24 14:16:39Z thiadmer $ + */ + +SC_FUNC int strexpand(char *dest, unsigned char *source, int maxlen, unsigned char pairtable[128][2]); + +#define SCPACK_TABLE errstr_table +/*-*SCPACK start of pair table, do not change or remove this line */ +unsigned char errstr_table[][2] = { + {101,32}, {111,110}, {116,32}, {105,110}, {97,114}, {116,105}, {100,32}, {115,32}, {101,114}, {97,108}, {101,110}, {37,115}, {133,129}, {34,139}, {141,34}, {117,110}, + {110,111}, {114,101}, {115,105}, {121,32}, {97,116}, {111,114}, {97,110}, {32,142}, {109,98}, {115,116}, {41,10}, {100,101}, {109,138}, {101,134}, {98,108}, {140,32}, + {111,108}, {114,97}, {144,130}, {118,137}, {143,99}, {102,164}, {115,121}, {166,152}, {167,160}, {117,115}, {97,32}, {115,146}, {97,158}, {149,32}, {132,161}, {105,134}, + {103,32}, {163,175}, {103,117}, {178,156}, {136,32}, {132,179}, {131,177}, {111,102}, {116,104}, {101,120}, {105,135}, {165,159}, {101,100}, {99,104}, {118,132}, {168,151}, + {105,172}, {190,192}, {155,102}, {174,147}, {183,32}, {109,97}, {116,111}, {99,129}, {101,135}, {181,130}, {98,128}, {115,10}, {112,145}, {153,148}, {44,32}, {40,191}, + {169,130}, {151,10}, {101,10}, {207,154}, {109,208}, {116,97}, {105,99}, {194,131}, {193,128}, {34,32}, {129,32}, {132,97}, {100,105}, {146,122}, {110,32}, {137,32}, + {104,97}, {101,108}, {117,108}, {99,111}, {108,111}, {109,148}, {199,153}, {58,209}, {111,112}, {97,115}, {108,128}, {232,136}, {230,150}, {150,32}, {204,171}, {131,176}, + {212,202}, {102,105}, {119,105}, {185,238}, {109,112}, {116,136}, {165,140}, {197,147}, {102,149}, {111,32}, {131,32}, {213,176}, {110,117}, {115,117}, {118,128} +}; +/*-*SCPACK end of pair table, do not change or remove this line */ + +static char *errmsg[] = { +#ifdef SCPACK +/*001*/ "expected token: \"%s\", but found \"%s\"\n", +/*002*/ "only a single statement (or expression) can follow each \"case\"\n", +/*003*/ "declaration of a local variable must appear in a compound block\n", +/*004*/ "function \"%s\" is not implemented\n", +/*005*/ "function may not have arguments\n", +/*006*/ "must be assigned to an array\n", +/*007*/ "operator cannot be redefined\n", +/*008*/ "must be a constant expression; assumed zero\n", +/*009*/ "invalid array size (negative, zero or out of bounds)\n", +/*010*/ "invalid function or declaration\n", +/*011*/ "invalid outside functions\n", +/*012*/ "invalid function call, not a valid address\n", +/*013*/ "no entry point (no public functions)\n", +/*014*/ "invalid statement; not in switch\n", +/*015*/ "\"default\" case must be the last case in switch statement\n", +/*016*/ "multiple defaults in \"switch\"\n", +/*017*/ "undefined symbol \"%s\"\n", +/*018*/ "initialization data exceeds declared size\n", +/*019*/ "not a label: \"%s\"\n", +/*020*/ "invalid symbol name \"%s\"\n", +/*021*/ "symbol already defined: \"%s\"\n", +/*022*/ "must be lvalue (non-constant)\n", +/*023*/ "array assignment must be simple assignment\n", +/*024*/ "\"break\" or \"continue\" is out of context\n", +/*025*/ "function heading differs from prototype\n", +/*026*/ "no matching \"#if...\"\n", +/*027*/ "invalid character constant\n", +/*028*/ "invalid subscript (not an array or too many subscripts): \"%s\"\n", +/*029*/ "invalid expression, assumed zero\n", +/*030*/ "compound statement not closed at the end of file (started at line %d)\n", +/*031*/ "unknown directive\n", +/*032*/ "array index out of bounds (variable \"%s\")\n", +/*033*/ "array must be indexed (variable \"%s\")\n", +/*034*/ "argument does not have a default value (argument %d)\n", +/*035*/ "argument type mismatch (argument %d)\n", +/*036*/ "empty statement\n", +/*037*/ "invalid string (possibly non-terminated string)\n", +/*038*/ "extra characters on line\n", +/*039*/ "constant symbol has no size\n", +/*040*/ "duplicate \"case\" label (value %d)\n", +/*041*/ "invalid ellipsis, array size is not known\n", +/*042*/ "invalid combination of class specifiers\n", +/*043*/ "character constant exceeds range for packed string\n", +/*044*/ "positional parameters must precede all named parameters\n", +/*045*/ "too many function arguments\n", +/*046*/ "unknown array size (variable \"%s\")\n", +/*047*/ "array sizes do not match, or destination array is too small\n", +/*048*/ "array dimensions do not match\n", +/*049*/ "invalid line continuation\n", +/*050*/ "invalid range\n", +/*051*/ "invalid subscript, use \"[ ]\" operators on major dimensions\n", +/*052*/ "multi-dimensional arrays must be fully initialized\n", +/*053*/ "exceeding maximum number of dimensions\n", +/*054*/ "unmatched closing brace (\"}\")\n", +/*055*/ "start of function body without function header\n", +/*056*/ "arrays, local variables and function arguments cannot be public (variable \"%s\")\n", +/*057*/ "unfinished expression before compiler directive\n", +/*058*/ "duplicate argument; same argument is passed twice\n", +/*059*/ "function argument may not have a default value (variable \"%s\")\n", +/*060*/ "multiple \"#else\" directives between \"#if ... #endif\"\n", +/*061*/ "\"#elseif\" directive follows an \"#else\" directive\n", +/*062*/ "number of operands does not fit the operator\n", +/*063*/ "function result tag of operator \"%s\" must be \"%s\"\n", +/*064*/ "cannot change predefined operators\n", +/*065*/ "function argument may only have a single tag (argument %d)\n", +/*066*/ "function argument may not be a reference argument or an array (argument \"%s\")\n", +/*067*/ "variable cannot be both a reference and an array (variable \"%s\")\n", +/*068*/ "invalid rational number precision in #pragma\n", +/*069*/ "rational number format already defined\n", +/*070*/ "rational number support was not enabled\n", +/*071*/ "user-defined operator must be declared before use (function \"%s\")\n", +/*072*/ "\"sizeof\" operator is invalid on \"function\" symbols\n", +/*073*/ "function argument must be an array (argument \"%s\")\n", +/*074*/ "#define pattern must start with an alphabetic character\n", +/*075*/ "input line too long (after substitutions)\n", +/*076*/ "syntax error in the expression, or invalid function call\n", +/*077*/ "malformed UTF-8 encoding, or corrupted file: %s\n", +/*078*/ "function uses both \"return\" and \"return \"\n", +/*079*/ "inconsistent return types (array & non-array)\n", +/*080*/ "unknown symbol, or not a constant symbol (symbol \"%s\")\n", +/*081*/ "cannot take a tag as a default value for an indexed array parameter (symbol \"%s\")\n", +/*082*/ "user-defined operators and native functions may not have states\n", +/*083*/ "a function or variable may only belong to a single automaton (symbol \"%s\")\n", +/*084*/ "state conflict: one of the states is already assigned to another implementation (symbol \"%s\")\n", +/*085*/ "no states are defined for symbol \"%s\"\n", +/*086*/ "unknown automaton \"%s\"\n", +/*087*/ "unknown state \"%s\" for automaton \"%s\"\n", +/*088*/ "public variables and local variables may not have states (symbol \"%s\")\n", +/*089*/ "state variables may not be initialized (symbol \"%s\")\n", +/*090*/ "public functions may not return arrays (symbol \"%s\")\n", +/*091*/ "ambiguous constant; tag override is required (symbol \"%s\")\n" +#else + "\271pect\235\306k\212:\227\316bu\202fo\217\206\216\012", + "\201l\223\252s\203g\352\315e\234\202(\255\363\201) c\355f\240\344w ea\275 \042c\351e\042\012", + "\233cl\333\237\304\252\344c\337\330\324appe\204 \372\252\343\364o\217\206\236ock\012", + "\366\227 \272\242i\364le\234t\274\012", + "\273\367\242\340\376\265t\313", + "\360a\253gn\235\306 \355\256y\012", + "\353\224\255c\226\242\312\221\327\274\012", + "\360\252\354\202\363\201; \351\375m\235z\210o\012", + "\266\303\335\200(nega\205ve\316z\210\371\255ou\202\304bo\217ds\232", + "\266\273\255\233cl\333\214\012", + "\266out\222d\200\366\313", + "\266\273c\211l\316\242\252\261add\221s\313", + "\220 \212tr\223po\203\202(\220 pu\236\326 \366s\232", + "\266\315e\234t; \242\372s\362t\275\012", + "\042\302a\342t\331c\351\200\360\270\200l\351\202c\351\200\372s\362t\275 \315e\234t\012", + "m\342\205p\352\302a\342t\207\372\042s\362t\275\042\012", + "\217\327\235\277\012", + "\203i\205\211iza\237d\224\252\271ce\274\207\233cl\204\235\335\322", + "\242\252lab\341\347", + "\266\250 nam\200\216\012", + "\250 \211\221ad\223\327\274\347", + "\360l\243u\200(n\201-\354t\232", + "\303a\253gn\234\202\360\222\364\352a\253gn\234t\012", + "\042b\221ak\331\255\042\307t\203ue\331\272ou\202\304\307t\271t\012", + "\273head\357\334ff\210\207from pro\306typ\322", + "\220 \345\275\357\042#if...\042\012", + "\266\275\333ct\264\354t\012", + "\266\375bscrip\202(\242\355\303\255\306\371m\226\223\375bscripts)\347", + "\266\363\201\316\351\375m\235z\210o\012", + "\343\364o\217\206\315e\234\202\242c\344s\235a\202\270\200\212\206\304\361\352(\231\204t\235a\202l\203\200%d\232", + "\217k\220w\336\334\221c\205v\322", + "\303\203\233x ou\202\304bo\217d\207(\330\216\232", + "\303\360\203\233x\235(\330\216\232", + "\311do\310\242\340\376\252\302a\342\202\243u\200(\311%d\232", + "\311typ\200mis\345\275 (\311%d\232", + "e\364t\223\315e\234t\012", + "\266\231r\357(po\253\236\223n\201-\365m\203\224\235\231r\203g\232", + "\271t\241 \275\333c\365\207\332l\203\322", + "\354\202\250 \340\207\220 \335\322", + "dupl\326\224\200\042c\351e\331lab\341 (\243u\200%d\232", + "\266\341lip\222s\316\303\335\200\272\242k\220wn\012", + "\266\343\230\203a\237\304cl\351\207speci\361\210\313", + "\275\333ct\264\354\202\271ce\274\207r\226g\200f\255pack\235\231r\203g\012", + "po\222\214\337p\333me\365\207\324\314c\274\200\211l nam\235p\333me\365\313", + "\306\371m\226\223\273\265t\313", + "\217k\220w\336\303\335\200(\330\216\232", + "\303\335\310d\371\242\345\275\316\255\233\231\203a\237\303\272\306\371sm\211l\012", + "\303\334\234\222\201\207d\371\242\345\275\012", + "\266l\203\200\307t\203ua\214\012", + "\266r\226g\322", + "\266\375bscript\316\251\200\042[ ]\331\353\224\225\207\332\305j\255\334\234\222\201\313", + "m\342\205-\334\234\222\201\337\256y\207\360f\342l\223\203i\205\211iz\274\012", + "\271ce\274\357\305ximum \374\230\264\304\334\234\222\201\313", + "\217\345\275\235c\344s\357b\241c\200(\042}\042\232", + "\231\204\202\304\273bod\223\362\270ou\202\273head\210\012", + "\256ys\316\344c\337\301\310\226\206\273\265t\207c\226\242\312pu\236\326 (\330\216\232", + "\217f\203ish\235\363\332be\370\200\343\364il\264\334\221c\205v\322", + "dupl\326\224\200\265t; sam\200\311\272p\351s\235tw\326\322", + "\273\311\367\242\340\376\252\302a\342\202\243u\200(\330\216\232", + "m\342\205p\352\042#\341se\331\334\221c\205v\310betwe\212 \042#if ... #\212\334f\042\012", + "\042#\341seif\331\334\221c\205\376f\240\344w\207\355\042#\341se\331\334\221c\205v\322", + "\374\230\264\304\353\226d\207do\310\242\361\202\270\200\353\224\225\012", + "\273\221s\342\202\373\304\353\224\225\227 \360\216\012", + "c\226\242\275\226g\200\314\327\235\353\224\225\313", + "\273\311\367\201l\223\340\376\252s\203g\352\373(\311%d\232", + "\273\311\367\242\312\252\221f\210\212c\200\311\255\355\303(\311\216\232", + "\330c\226\242\312bo\270 \252\221f\210\212c\200\226\206\355\303(\330\216\232", + "\266\241\214\337\374\230\264\314ci\222\332\372#p\241g\305\012", + "\241\214\337\374\230\264\370\305\202\211\221ad\223\327\274\012", + "\241\214\337\374\230\264\375pp\225\202wa\207\242\212\254\274\012", + "\251\210-\327\235\353\224\255\360\233cl\204\235be\370\200\251\200(\366\227\232", + "\042\335e\267\331\353\224\255\272\266\332\042\366\331\250\313", + "\273\311\360\355\303(\311\216\232", + "#\327\200p\224\365\336\324\231\204\202\362\270 \355\211p\340be\205c \275\333c\365\012", + "\203pu\202l\203\200\306\371l\201\260(aft\264\375bs\205tu\214s\232", + "\246n\325x \210r\255\372\270\200\363\201\316\255\266\273c\211l\012", + "m\211\370m\235UTF-8 \212\343d\203g\316\255c\225rupt\235\361le: \213\012", + "\273\251\310bo\270 \042\221turn\331\226\206\042\221tur\336<\243ue>\042\012", + "\203\307\222\231\212\202\221tur\336typ\310(\303& n\201-\256y\232", + "\217k\220w\336\250\316\255\242\252\354\202\250 \323", + "c\226\242\325k\200\252\373a\207\252\302a\342\202\243u\200f\255\355\203\233x\235\303p\333met\264\323", + "\251\210-\327\235\353\224\225\207\226\206na\205\376\366\207\367\242\340\376\315e\313", + "\252\273\255\330\367\201l\223b\341\201\260\306 \252s\203g\352au\306\345\332\323", + "\315\200\307fl\326t: \201\200\304\270\200\315\310\272\211\221ad\223a\253gn\235\306 a\220\270\264i\364le\234\325\237\323", + "\220 \315\310\204\200\327\235f\255\277\012", + "\217k\220w\336au\306\345\201\321", + "\217k\220w\336\315\200\216 f\255au\306\345\201\321", + "pu\236\326 \301\310\226\206\344c\337\301\310\367\242\340\376\315\310\323", + "\315\200\301\310\367\242\312\203i\205\211iz\235\323", + "pu\236\326 \366\207\367\242\221tur\336\256y\207\323", + "a\230i\262ou\207\354t; \373ov\210rid\200\272\221qui\221\206\323" +#endif + }; + +static char *fatalmsg[] = { +#ifdef SCPACK +/*100*/ "cannot read from file: \"%s\"\n", +/*101*/ "cannot write to file: \"%s\"\n", +/*102*/ "table overflow: \"%s\"\n", + /* table can be: loop table + * literal table + * staging buffer + * option table (response file) + * peephole optimizer table + */ +/*103*/ "insufficient memory\n", +/*104*/ "invalid assembler instruction \"%s\"\n", +/*105*/ "numeric overflow, exceeding capacity\n", +/*106*/ "compiled script exceeds the maximum memory size (%ld bytes)\n", +/*107*/ "too many error messages on one line\n", +/*108*/ "codepage mapping file not found\n", +/*109*/ "invalid path: \"%s\"\n", +/*110*/ "assertion failed: %s\n", +/*111*/ "user error: %s\n", +#else + "c\226\242\221a\206from \361le\347", + "c\226\242writ\200\306 \361le\347", + "t\254\200ov\210f\344w\347", + "\203\375ff\326i\212\202mem\225y\012", + "\266\351se\230l\264\203\231ruc\214\321", + "\374m\210\326 ov\210f\344w\316\271ce\274\357capacity\012", + "\343\364il\235scrip\202\271ce\274\207\270\200\305ximum mem\225\223\335\200(%l\206bytes\232", + "\306\371m\226\223\210r\255messag\310\332\201\200l\203\322", + "\343\233pag\200\305pp\357\361\352\242fo\217d\012", + "\266p\224h\347", + "\351s\210\237fail\274: \213\012", + "\251\264\210r\225: \213\012" +#endif + }; + +static char *warnmsg[] = { +#ifdef SCPACK +/*200*/ "symbol \"%s\" is truncated to %d characters\n", +/*201*/ "redefinition of constant/macro (symbol \"%s\")\n", +/*202*/ "number of arguments does not match definition\n", +/*203*/ "symbol is never used: \"%s\"\n", +/*204*/ "symbol is assigned a value that is never used: \"%s\"\n", +/*205*/ "redundant code: constant expression is zero\n", +/*206*/ "redundant test: constant expression is non-zero\n", +/*207*/ "unknown #pragma\n", +/*208*/ "function with tag result used before definition, forcing reparse\n", +/*209*/ "function \"%s\" should return a value\n", +/*210*/ "possible use of symbol before initialization: \"%s\"\n", +/*211*/ "possibly unintended assignment\n", +/*212*/ "possibly unintended bitwise operation\n", +/*213*/ "tag mismatch\n", +/*214*/ "possibly a \"const\" array argument was intended: \"%s\"\n", +/*215*/ "expression has no effect\n", +/*216*/ "nested comment\n", +/*217*/ "loose indentation\n", +/*218*/ "old style prototypes used with optional semicolumns\n", +/*219*/ "local variable \"%s\" shadows a variable at a preceding level\n", +/*220*/ "expression with tag override must appear between parentheses\n", +/*221*/ "label name \"%s\" shadows tag name\n", +/*222*/ "number of digits exceeds rational number precision\n", +/*223*/ "redundant \"sizeof\": argument size is always 1 (symbol \"%s\")\n", +/*224*/ "indeterminate array size in \"sizeof\" expression (symbol \"%s\")\n", +/*225*/ "unreachable code\n", +/*226*/ "a variable is assigned to itself (symbol \"%s\")\n", +/*227*/ "more initiallers than enum fields\n", +/*228*/ "length of initialler exceeds size of the enum field\n", +/*229*/ "index tag mismatch (symbol \"%s\")\n", +/*230*/ "no implementation for state \"%s\" in function \"%s\", no fall-back\n", +/*231*/ "state specification on forward declaration is ignored\n", +/*232*/ "output file is written, but with compact encoding disabled\n", +/*233*/ "state variable \"%s\" shadows a global variable\n", +/*234*/ "function is depricated (symbol \"%s\") %s\n", +/*235*/ "public function lacks forward declaration (symbol \"%s\")\n", +/*236*/ "unknown parameter in substitution (incorrect #define pattern)\n" +#else + "\277 \272tr\244\224\235\306 %\206\275\333c\365\313", + "\221\327i\237\304\354t/\305cr\371\323", + "\374\230\264\304\265t\207do\310\242\345\275 \327i\214\012", + "\250 \272nev\264\251\274\347", + "\250 \272a\253gn\235\252\243u\200\270a\202\272nev\264\251\274\347", + "\221d\217d\226\202\343\233: \354\202\363\332\272z\210o\012", + "\221d\217d\226\202te\231: \354\202\363\332\272n\201-z\210o\012", + "\217k\220w\336#p\241g\305\012", + "\273\362\270 \373\221s\342\202\251\235be\370\200\327i\214\316\370c\357\221p\204s\322", + "\366\227 sho\342\206\221tur\336\252\243u\322", + "po\253\236\200\251\200\304\250 be\370\200\203i\205\211iza\214\347", + "po\253\236\223\217\203t\212\233\206a\253gn\234t\012", + "po\253\236\223\217\203t\212\233\206bit\362s\200\353a\214\012", + "\373mis\345\275\012", + "po\253\236\223\252\042\346\331\303\311wa\207\203t\212\233d\347", + "\363\332\340\207\220 effect\012", + "ne\231\235\343m\234t\012", + "\344os\200\203d\212\325\214\012", + "\240\206\231y\352pro\306typ\310\251\235\362\270 \350\214\337sem\326\240umn\313", + "\344c\337\330\216 s\340dow\207\252\330a\202\252\314c\274\357lev\341\012", + "\363\332\362\270 \373ov\210rid\200\324appe\204 betwe\212 p\204\212\270ese\313", + "lab\341 nam\200\216 s\340dow\207\373nam\322", + "\374\230\264\304\334git\207\271ce\274\207\241\214\337\374\230\264\314ci\222\201\012", + "\221d\217d\226\202\042\335e\267\042: \311\335\200\272\211way\2071 \323", + "\203\233\365m\203\224\200\303\335\200\372\042\335e\267\331\363\332\323", + "\217\221a\275\254\200\343\233\012", + "\252\330\272a\253gn\235\306 its\341f \323", + "m\225\200\203i\205\211l\210\207\270\355\212um \361\341d\313", + "l\212g\270 \304\203i\205\211l\264\271ce\274\207\335\200\304\270\200\212um \361\341d\012", + "\203\233x \373mis\345\275 \323", + "\220 i\364le\234\325\237f\255\315\200\216 \372\366\227\316\220 f\211l-back\012", + "\315\200specif\326a\237\332\370w\204\206\233cl\333\237\272ig\220\221d\012", + "outpu\202\361\352\272writt\212\316bu\202\362\270 \343\364ac\202\212\343d\357\334s\254\274\012", + "\315\200\330\216 s\340dow\207\252g\344b\337\301\322", + "\273\272\233pr\326\224\235\317) \213\012", + "pu\236\326 \273lack\207\370w\204\206\233cl\333\237\323", + "\217k\220w\336p\333met\264\372\375bs\205tu\237(\203c\225\221c\202#\327\200p\224\365n\232" +#endif + }; diff --git a/compiler-init/sc6.c b/compiler-init/sc6.c new file mode 100644 index 00000000..9dd5a169 --- /dev/null +++ b/compiler-init/sc6.c @@ -0,0 +1,1298 @@ +/* Pawn compiler - Binary code generation (the "assembler") + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc6.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include /* for macro max() */ +#include /* for macro offsetof() */ +#include +#include +#if defined FORTIFY + #include +#endif +#include "lstring.h" +#include "sc.h" +#include "amxdbg.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + + +static void append_dbginfo(FILE *fout); + + +typedef cell (*OPCODE_PROC)(FILE *fbin,char *params,cell opcode); + +typedef struct { + cell opcode; + char *name; + int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */ + OPCODE_PROC func; +} OPCODE; + +static cell codeindex; /* similar to "code_idx" */ +static cell *lbltab; /* label table */ +static int writeerror; +static int bytes_in, bytes_out; +static jmp_buf compact_err; + +/* apparently, strtol() does not work correctly on very large (unsigned) + * hexadecimal values */ +static ucell hex2long(const char *s,char **n) +{ + ucell result=0L; + int negate=FALSE; + int digit; + + /* ignore leading whitespace */ + while (*s==' ' || *s=='\t') + s++; + + /* allow a negation sign to create the two's complement of numbers */ + if (*s=='-') { + negate=TRUE; + s++; + } /* if */ + + assert((*s>='0' && *s<='9') || (*s>='a' && *s<='f') || (*s>='a' && *s<='f')); + for ( ;; ) { + if (*s>='0' && *s<='9') + digit=*s-'0'; + else if (*s>='a' && *s<='f') + digit=*s-'a' + 10; + else if (*s>='A' && *s<='F') + digit=*s-'A' + 10; + else + break; /* probably whitespace */ + result=(result<<4) | digit; + s++; + } /* for */ + if (n!=NULL) + *n=(char*)s; + if (negate) + result=(~result)+1; /* take two's complement of the result */ + return (ucell)result; +} + +static ucell getparam(const char *s,char **n) +{ + ucell result=0; + for ( ;; ) { + result+=hex2long(s,(char**)&s); + if (*s!='+') + break; + s++; + } /* for */ + if (n!=NULL) + *n=(char*)s; + return result; +} + +#if BYTE_ORDER==BIG_ENDIAN +static uint16_t *align16(uint16_t *v) +{ + unsigned char *s = (unsigned char *)v; + unsigned char t; + + /* swap two bytes */ + t=s[0]; + s[0]=s[1]; + s[1]=t; + return v; +} + +static uint32_t *align32(uint32_t *v) +{ + unsigned char *s = (unsigned char *)v; + unsigned char t; + + /* swap outer two bytes */ + t=s[0]; + s[0]=s[3]; + s[3]=t; + /* swap inner two bytes */ + t=s[1]; + s[1]=s[2]; + s[2]=t; + return v; +} + +#if PAWN_CELL_SIZE>=64 +static uint64_t *align64(uint64_t *v) +{ + unsigned char *s = (unsigned char *)v; + unsigned char t; + + t=s[0]; + s[0]=s[7]; + s[7]=t; + + t=s[1]; + s[1]=s[6]; + s[6]=t; + + t=s[2]; + s[2]=s[5]; + s[5]=t; + + t=s[3]; + s[3]=s[4]; + s[4]=t; + + return v; +} +#endif + + #if PAWN_CELL_SIZE==16 + #define aligncell(v) align16(v) + #elif PAWN_CELL_SIZE==32 + #define aligncell(v) align32(v) + #elif PAWN_CELL_SIZE==64 + #define aligncell(v) align64(v) + #endif +#else + #define align16(v) (v) + #define align32(v) (v) + #define aligncell(v) (v) +#endif + +static char *skipwhitespace(char *str) +{ + while (isspace(*str)) + str++; + return str; +} + +static char *stripcomment(char *str) +{ + char *ptr=strchr(str,';'); + if (ptr!=NULL) { + *ptr++='\n'; /* terminate the line, but leave the '\n' */ + *ptr='\0'; + } /* if */ + return str; +} + +static void write_encoded(FILE *fbin,ucell *c,int num) +{ + #if PAWN_CELL_SIZE == 16 + #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ + #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ + #elif PAWN_CELL_SIZE == 32 + #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ + #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ + #elif PAWN_CELL_SIZE == 64 + #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ + #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ + #endif + + assert(fbin!=NULL); + while (num-->0) { + if (sc_compress) { + ucell p=(ucell)*c; + unsigned char t[ENC_MAX]; + unsigned char code; + int index; + for (index=0; index>=7; + } /* for */ + /* skip leading zeros */ + while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) + index--; + /* skip leading -1s */ + if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) + index--; + while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) + index--; + /* write high byte first, write continuation bits */ + assert(index>0); + while (index-->0) { + code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); + writeerror |= !pc_writebin(fbin,&code,1); + bytes_out++; + } /* while */ + bytes_in+=sizeof *c; + assert(AMX_COMPACTMARGIN>2); + if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) + longjmp(compact_err,1); + } else { + assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); + writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); + } /* if */ + c++; + } /* while */ +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell noop(FILE *fbin,char *params,cell opcode) +{ + return 0; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell set_currentfile(FILE *fbin,char *params,cell opcode) +{ + fcurrent=(short)getparam(params,NULL); + return 0; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell parm0(FILE *fbin,char *params,cell opcode) +{ + if (fbin!=NULL) + write_encoded(fbin,(ucell*)&opcode,1); + return opcodes(1); +} + +static cell parm1(FILE *fbin,char *params,cell opcode) +{ + ucell p=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p,1); + } /* if */ + return opcodes(1)+opargs(1); +} + +static cell parm2(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + } /* if */ + return opcodes(1)+opargs(2); +} + +static cell parm3(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,¶ms); + ucell p3=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + write_encoded(fbin,&p3,1); + } /* if */ + return opcodes(1)+opargs(3); +} + +static cell parm4(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,¶ms); + ucell p3=getparam(params,¶ms); + ucell p4=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + write_encoded(fbin,&p3,1); + write_encoded(fbin,&p4,1); + } /* if */ + return opcodes(1)+opargs(4); +} + +static cell parm5(FILE *fbin,char *params,cell opcode) +{ + ucell p1=getparam(params,¶ms); + ucell p2=getparam(params,¶ms); + ucell p3=getparam(params,¶ms); + ucell p4=getparam(params,¶ms); + ucell p5=getparam(params,NULL); + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p1,1); + write_encoded(fbin,&p2,1); + write_encoded(fbin,&p3,1); + write_encoded(fbin,&p4,1); + write_encoded(fbin,&p5,1); + } /* if */ + return opcodes(1)+opargs(5); +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell do_dump(FILE *fbin,char *params,cell opcode) +{ + ucell p; + int num = 0; + + while (*params!='\0') { + p=getparam(params,¶ms); + if (fbin!=NULL) + write_encoded(fbin,&p,1); + num++; + while (isspace(*params)) + params++; + } /* while */ + return num*sizeof(cell); +} + +static cell do_call(FILE *fbin,char *params,cell opcode) +{ + char name[sNAMEMAX+1]; + int i; + symbol *sym; + ucell p; + + for (i=0; !isspace(*params); i++,params++) { + assert(*params!='\0'); + assert(i=0 && iident==iFUNCTN || sym->ident==iREFFUNC); + assert(sym->vclass==sGLOBAL); + p=sym->addr; + } /* if */ + + if (fbin!=NULL) { + write_encoded(fbin,(ucell*)&opcode,1); + write_encoded(fbin,&p,1); + } /* if */ + return opcodes(1)+opargs(1); +} + +static cell do_jump(FILE *fbin,char *params,cell opcode) +{ + int i; + ucell p; + + i=(int)hex2long(params,NULL); + assert(i>=0 && i=0 && i=0 && i=MAX_INSTR_LEN) + return 0; + strlcpy(str,instr,maxlen+1); + /* look up the instruction with a binary search + * the assembler is case insensitive to instructions (but case sensitive + * to symbols) + */ + low=1; /* entry 0 is reserved (for "not found") */ + high=(sizeof opcodelist / sizeof opcodelist[0])-1; + while (low0) + low=mid+1; + else + high=mid; + } /* while */ + + assert(low==high); + if (stricmp(str,opcodelist[low].name)==0) + return low; /* found */ + return 0; /* not found, return special index */ +} + +SC_FUNC int assemble(FILE *fout,FILE *fin) +{ + AMX_HEADER hdr; + AMX_FUNCSTUBNT func; + int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; + long nametablesize,nameofs; + #if PAWN_CELL_SIZE > 32 + char line[512]; + #else + char line[256]; + #endif + char *instr,*params; + int i,pass,size; + int16_t count; + symbol *sym, **nativelist; + constvalue *constptr; + cell mainaddr; + char nullchar = 0; + + /* if compression failed, restart the assembly with compaction switched off */ + if (setjmp(compact_err)!=0) { + assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ + sc_compress=FALSE; + pc_resetbin(fout,0); + error(232); /* disabled compact encoding */ + } /* if */ + + #if !defined NDEBUG + /* verify that the opcode list is sorted (skip entry 1; it is reserved + * for a non-existant opcode) + */ + assert(opcodelist[1].name!=NULL); + for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { + assert(opcodelist[i].name!=NULL); + assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); + } /* for */ + #endif + + writeerror=FALSE; + nametablesize=sizeof(int16_t); + numpublics=0; + numnatives=0; + numpubvars=0; + mainaddr=-1; + /* count number of public and native functions and public variables */ + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + int match=0; + if (sym->ident==iFUNCTN) { + if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) + match=++numnatives; + if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) + match=++numpublics; + if (strcmp(sym->name,uMAINFUNC)==0) { + assert(sym->vclass==sGLOBAL); + mainaddr=sym->addr; + } /* if */ + } else if (sym->ident==iVARIABLE) { + if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) + match=++numpubvars; + } /* if */ + if (match) { + char alias[sNAMEMAX+1]; + assert(sym!=NULL); + if ((sym->usage & uNATIVE)==0 || !lookup_alias(alias,sym->name)) { + assert(strlen(sym->name)<=sNAMEMAX); + strcpy(alias,sym->name); + } /* if */ + nametablesize+=strlen(alias)+1; + } /* if */ + } /* for */ + assert(numnatives==ntv_funcid); + + /* count number of libraries */ + numlibraries=0; + if (pc_addlibtable) { + for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { + if (constptr->value>0) { + assert(strlen(constptr->name)>0); + numlibraries++; + nametablesize+=strlen(constptr->name)+1; + } /* if */ + } /* for */ + } /* if */ + + /* count number of public tags */ + numtags=0; + for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + if ((constptr->value & PUBLICTAG)!=0) { + assert(strlen(constptr->name)>0); + numtags++; + nametablesize+=strlen(constptr->name)+1; + } /* if */ + } /* for */ + + /* pad the header to sc_dataalign + * => thereby the code segment is aligned + * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned + * => and thereby the stack top is aligned too + */ + assert(sc_dataalign!=0); + padding= (int)(sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign); + if (padding==sc_dataalign) + padding=0; + + /* write the abstract machine header */ + memset(&hdr, 0, sizeof hdr); + hdr.magic=(unsigned short)AMX_MAGIC; + hdr.file_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MAX_FILE_VER_JIT : CUR_FILE_VERSION); + hdr.amx_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MIN_AMX_VER_JIT : MIN_AMX_VERSION); + hdr.flags=(short)(sc_debug & sSYMBOLIC); + if (sc_compress) + hdr.flags|=AMX_FLAG_COMPACT; + if (sc_debug==0) + hdr.flags|=AMX_FLAG_NOCHECKS; + if (pc_memflags & suSLEEP_INSTR) + hdr.flags|=AMX_FLAG_SLEEP; + hdr.defsize=sizeof(AMX_FUNCSTUBNT); + hdr.publics=sizeof hdr; /* public table starts right after the header */ + hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); + hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); + hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); + hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); + hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); + hdr.cod=hdr.nametable + nametablesize + padding; + hdr.dat=hdr.cod + code_idx; + hdr.hea=hdr.dat + glb_declared*sizeof(cell); + hdr.stp=hdr.hea + pc_stksize*sizeof(cell); + hdr.cip=mainaddr; + hdr.size=hdr.hea; /* preset, this is incorrect in case of compressed output */ + pc_writebin(fout,&hdr,sizeof hdr); + + /* dump zeros up to the rest of the header, so that we can easily "seek" */ + for (nameofs=sizeof hdr; nameofsnext) { + if (sym->ident==iFUNCTN + && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) + { + assert(sym->vclass==sGLOBAL); + func.address=sym->addr; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,sym->name,strlen(sym->name)+1); + nameofs+=strlen(sym->name)+1; + count++; + } /* if */ + } /* for */ + + /* write the natives table */ + /* The native functions must be written in sorted order. (They are + * sorted on their "id", not on their name). A nested loop to find + * each successive function would be an O(n^2) operation. But we + * do not really need to sort, because the native function id's + * are sequential and there are no duplicates. So we first walk + * through the complete symbol list and store a pointer to every + * native function of interest in a temporary table, where its id + * serves as the index in the table. Now we can walk the table and + * have all native functions in sorted order. + */ + if (numnatives>0) { + nativelist=(symbol **)malloc(numnatives*sizeof(symbol *)); + if (nativelist==NULL) + error(103); /* insufficient memory */ + #if !defined NDEBUG + memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ + #endif + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN && (sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { + assert(sym->addr < numnatives); + nativelist[(int)sym->addr]=sym; + } /* if */ + } /* for */ + count=0; + for (i=0; iname)) { + assert(strlen(sym->name)<=sNAMEMAX); + strcpy(alias,sym->name); + } /* if */ + assert(sym->vclass==sGLOBAL); + func.address=0; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,alias,strlen(alias)+1); + nameofs+=strlen(alias)+1; + count++; + } /* for */ + free(nativelist); + } /* if */ + + /* write the libraries table */ + if (pc_addlibtable) { + count=0; + for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { + if (constptr->value>0) { + assert(strlen(constptr->name)>0); + func.address=0; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + nameofs+=strlen(constptr->name)+1; + count++; + } /* if */ + } /* for */ + } /* if */ + + /* write the public variables table */ + count=0; + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iVARIABLE && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { + assert((sym->usage & uDEFINE)!=0); + assert(sym->vclass==sGLOBAL); + func.address=sym->addr; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,sym->name,strlen(sym->name)+1); + nameofs+=strlen(sym->name)+1; + count++; + } /* if */ + } /* for */ + + /* write the public tagnames table */ + count=0; + for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + if ((constptr->value & PUBLICTAG)!=0) { + assert(strlen(constptr->name)>0); + func.address=constptr->value & TAGMASK; + func.nameofs=nameofs; + #if BYTE_ORDER==BIG_ENDIAN + align32(&func.address); + align32(&func.nameofs); + #endif + pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); + pc_writebin(fout,&func,sizeof func); + pc_resetbin(fout,nameofs); + pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + nameofs+=strlen(constptr->name)+1; + count++; + } /* if */ + } /* for */ + + /* write the "maximum name length" field in the name table */ + assert(nameofs==hdr.nametable+nametablesize); + pc_resetbin(fout,hdr.nametable); + count=sNAMEMAX; + #if BYTE_ORDER==BIG_ENDIAN + align16(&count); + #endif + pc_writebin(fout,&count,sizeof count); + pc_resetbin(fout,hdr.cod); + + /* First pass: relocate all labels */ + /* This pass is necessary because the code addresses of labels is only known + * after the peephole optimization flag. Labels can occur inside expressions + * (e.g. the conditional operator), which are optimized. + */ + lbltab=NULL; + if (sc_labnum>0) { + /* only very short programs have zero labels; no first pass is needed + * if there are no labels */ + lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); + if (lbltab==NULL) + error(103); /* insufficient memory */ + codeindex=0; + pc_resetasm(fin); + while (pc_readasm(fin,line,sizeof line)!=NULL) { + stripcomment(line); + instr=skipwhitespace(line); + /* ignore empty lines */ + if (*instr=='\0') + continue; + if (tolower(*instr)=='l' && *(instr+1)=='.') { + int lindex=(int)hex2long(instr+2,NULL); + assert(lindex>=0 && lindexinstr); + i=findopcode(instr,(int)(params-instr)); + if (opcodelist[i].name==NULL) { + *params='\0'; + error(104,instr); /* invalid assembler instruction */ + } /* if */ + if (opcodelist[i].segment==sIN_CSEG) + codeindex+=opcodelist[i].func(NULL,skipwhitespace(params),opcodelist[i].opcode); + } /* if */ + } /* while */ + } /* if */ + + /* Second pass (actually 2 more passes, one for all code and one for all data) */ + bytes_in=0; + bytes_out=0; + for (pass=sIN_CSEG; pass<=sIN_DSEG; pass++) { + pc_resetasm(fin); + while (pc_readasm(fin,line,sizeof line)!=NULL) { + stripcomment(line); + instr=skipwhitespace(line); + /* ignore empty lines and labels (labels have a special syntax, so these + * must be parsed separately) */ + if (*instr=='\0' || tolower(*instr)=='l' && *(instr+1)=='.') + continue; + /* get to the end of the instruction (make use of the '\n' that fgets() + * added at the end of the line; this way we will *always* drop on a + * whitespace character) */ + for (params=instr; *params!='\0' && !isspace(*params); params++) + /* nothing */; + assert(params>instr); + i=findopcode(instr,(int)(params-instr)); + assert(opcodelist[i].name!=NULL); + if (opcodelist[i].segment==pass) + opcodelist[i].func(fout,skipwhitespace(params),opcodelist[i].opcode); + } /* while */ + } /* for */ + if (bytes_out-bytes_in>0) + error(106); /* compression buffer overflow */ + + if (lbltab!=NULL) { + free(lbltab); + #if !defined NDEBUG + lbltab=NULL; + #endif + } /* if */ + + if (sc_compress) + hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ + if (!writeerror && (sc_debug & sSYMBOLIC)!=0) + append_dbginfo(fout); /* optionally append debug file */ + + if (writeerror) + error(101,"disk full"); + + /* adjust the header */ + size=(int)hdr.cod; /* save, the value in the header may be swapped */ + #if BYTE_ORDER==BIG_ENDIAN + align32(&hdr.size); + align16(&hdr.magic); + align16(&hdr.flags); + align16(&hdr.defsize); + align32(&hdr.publics); + align32(&hdr.natives); + align32(&hdr.libraries); + align32(&hdr.pubvars); + align32(&hdr.tags); + align32(&hdr.nametable); + align32(&hdr.cod); + align32(&hdr.dat); + align32(&hdr.hea); + align32(&hdr.stp); + align32(&hdr.cip); + #endif + pc_resetbin(fout,0); + pc_writebin(fout,&hdr,sizeof hdr); + + /* return the size of the header (including name tables, but excluding code + * or data sections) + */ + return size; +} + +static void append_dbginfo(FILE *fout) +{ + AMX_DBG_HDR dbghdr; + AMX_DBG_LINE dbgline; + AMX_DBG_SYMBOL dbgsym; + AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; + int index,dim,dbgsymdim; + char *str,*prevstr,*name,*prevname; + ucell codeidx,previdx; + constvalue *constptr; + char symname[2*sNAMEMAX+16]; + int16_t id1,id2; + ucell address; + + /* header with general information */ + memset(&dbghdr, 0, sizeof dbghdr); + dbghdr.size=sizeof dbghdr; + dbghdr.magic=AMX_DBG_MAGIC; + dbghdr.file_version=CUR_FILE_VERSION; + dbghdr.amx_version=MIN_AMX_VERSION; + + /* first pass: collect the number of items in various tables */ + + /* file table */ + previdx=0; + prevstr=NULL; + prevname=NULL; + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='F') { + codeidx=hex2long(str+2,&name); + if (codeidx!=previdx) { + if (prevstr!=NULL) { + assert(prevname!=NULL); + dbghdr.files++; + dbghdr.size+=sizeof(cell)+strlen(prevname)+1; + } /* if */ + previdx=codeidx; + } /* if */ + prevstr=str; + prevname=skipwhitespace(name); + } /* if */ + } /* for */ + if (prevstr!=NULL) { + assert(prevname!=NULL); + dbghdr.files++; + dbghdr.size+=sizeof(cell)+strlen(prevname)+1; + } /* if */ + + /* line number table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='L') { + dbghdr.lines++; + dbghdr.size+=sizeof(AMX_DBG_LINE); + } /* if */ + } /* for */ + + /* symbol table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='S') { + dbghdr.symbols++; + name=strchr(str+2,':'); + assert(name!=NULL); + dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); + if ((prevstr=strchr(name,'['))!=NULL) + while ((prevstr=strchr(prevstr+1,':'))!=NULL) + dbghdr.size+=sizeof(AMX_DBG_SYMDIM); + } /* if */ + } /* for */ + + /* tag table */ + for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(strlen(constptr->name)>0); + dbghdr.tags++; + dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); + } /* for */ + + /* automaton table */ + for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); + dbghdr.automatons++; + dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); + } /* for */ + + /* state table */ + for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(strlen(constptr->name)>0); + dbghdr.states++; + dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); + } /* for */ + + + /* pass 2: generate the tables */ + #if BYTE_ORDER==BIG_ENDIAN + align32((uint32_t*)&dbghdr.size); + align16(&dbghdr.magic); + align16(&dbghdr.flags); + align16(&dbghdr.files); + align16(&dbghdr.lines); + align16(&dbghdr.symbols); + align16(&dbghdr.tags); + align16(&dbghdr.automatons); + align16(&dbghdr.states); + #endif + writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); + + /* file table */ + previdx=0; + prevstr=NULL; + prevname=NULL; + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='F') { + codeidx=hex2long(str+2,&name); + if (codeidx!=previdx) { + if (prevstr!=NULL) { + assert(prevname!=NULL); + #if BYTE_ORDER==BIG_ENDIAN + aligncell(&previdx); + #endif + writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); + writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); + } /* if */ + previdx=codeidx; + } /* if */ + prevstr=str; + prevname=skipwhitespace(name); + } /* if */ + } /* for */ + if (prevstr!=NULL) { + assert(prevname!=NULL); + #if BYTE_ORDER==BIG_ENDIAN + aligncell(&previdx); + #endif + writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); + writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); + } /* if */ + + /* line number table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='L') { + dbgline.address=hex2long(str+2,&str); + dbgline.line=(int32_t)hex2long(str,NULL); + #if BYTE_ORDER==BIG_ENDIAN + aligncell(&dbgline.address); + align32(&dbgline.line); + #endif + writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); + } /* if */ + } /* for */ + + /* symbol table */ + for (index=0; (str=get_dbgstring(index))!=NULL; index++) { + assert(str!=NULL); + assert(str[0]!='\0' && str[1]==':'); + if (str[0]=='S') { + dbgsym.address=hex2long(str+2,&str); + dbgsym.tag=(int16_t)hex2long(str,&str); + str=skipwhitespace(str); + assert(*str==':'); + name=skipwhitespace(str+1); + str=strchr(name,' '); + assert(str!=NULL); + assert((int)(str-name)next) { + assert(strlen(constptr->name)>0); + id1=(int16_t)(constptr->value & TAGMASK); + #if BYTE_ORDER==BIG_ENDIAN + align16(&id1); + #endif + writeerror |= !pc_writebin(fout,&id1,sizeof id1); + writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + } /* for */ + + /* automaton table */ + for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); + id1=(int16_t)constptr->index; + address=(ucell)constptr->value; + #if BYTE_ORDER==BIG_ENDIAN + align16(&id1); + aligncell(&address); + #endif + writeerror |= !pc_writebin(fout,&id1,sizeof id1); + writeerror |= !pc_writebin(fout,&address,sizeof address); + writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + } /* for */ + + /* state table */ + for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { + assert(strlen(constptr->name)>0); + id1=(int16_t)constptr->value; + id2=(int16_t)constptr->index; + address=(ucell)constptr->value; + #if BYTE_ORDER==BIG_ENDIAN + align16(&id1); + align16(&id2); + #endif + writeerror |= !pc_writebin(fout,&id1,sizeof id1); + writeerror |= !pc_writebin(fout,&id2,sizeof id2); + writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); + } /* for */ + + delete_dbgstringtable(); +} diff --git a/compiler-init/sc7.c b/compiler-init/sc7.c new file mode 100644 index 00000000..f5a52d5a --- /dev/null +++ b/compiler-init/sc7.c @@ -0,0 +1,703 @@ +/* Pawn compiler - Staging buffer and optimizer + * + * The staging buffer + * ------------------ + * The staging buffer allows buffered output of generated code, deletion + * of redundant code, optimization by a tinkering process and reversing + * the ouput of evaluated expressions (which is used for the reversed + * evaluation of arguments in functions). + * Initially, stgwrite() writes to the file directly, but after a call to + * stgset(TRUE), output is redirected to the buffer. After a call to + * stgset(FALSE), stgwrite()'s output is directed to the file again. Thus + * only one routine is used for writing to the output, which can be + * buffered output or direct output. + * + * staging buffer variables: stgbuf - the buffer + * stgidx - current index in the staging buffer + * staging - if true, write to the staging buffer; + * if false, write to file directly. + * + * The peephole optimizer uses a dual "pipeline". The staging buffer (described + * above) gets optimized for each expression or sub-expression in a function + * call. The peephole optimizer is recursive, but it does not span multiple + * sub-expressions. However, the data gets written to a second buffer that + * behaves much like the staging buffer. This second buffer gathers all + * optimized strings from the staging buffer for a complete expression. The + * peephole optmizer then runs over this second buffer to find optimzations + * across function parameter boundaries. + * + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sc7.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include /* for atoi() */ +#include +#include +#if defined FORTIFY + #include +#endif +#include "sc.h" + +#if defined _MSC_VER + #pragma warning(push) + #pragma warning(disable:4125) /* decimal digit terminates octal escape sequence */ +#endif + +#include "sc7.scp" + +#if defined _MSC_VER + #pragma warning(pop) +#endif + +static int stgstring(char *start,char *end); +static void stgopt(char *start,char *end,int (*outputfunc)(char *str)); + + +#define sSTG_GROW 512 +#define sSTG_MAX 20480 + +static char *stgbuf=NULL; +static int stgmax=0; /* current size of the staging buffer */ + +static char *stgpipe=NULL; +static int pipemax=0; /* current size of the stage pipe, a second staging buffer */ +static int pipeidx=0; + +#define CHECK_STGBUFFER(index) if ((int)(index)>=stgmax) grow_stgbuffer(&stgbuf, stgmax, (index)+1) +#define CHECK_STGPIPE(index) if ((int)(index)>=pipemax) grow_stgbuffer(&stgpipe, pipemax, (index)+1) + +static void grow_stgbuffer(char **buffer, int curmax, int requiredsize) +{ + char *p; + int clear= (*buffer==NULL); /* if previously none, empty buffer explicitly */ + + assert(curmaxsSTG_MAX) + error(102,"staging buffer"); /* staging buffer overflow (fatal error) */ + curmax=requiredsize+sSTG_GROW; + if (*buffer!=NULL) + p=(char *)realloc(*buffer,curmax*sizeof(char)); + else + p=(char *)malloc(curmax*sizeof(char)); + if (p==NULL) + error(102,"staging buffer"); /* staging buffer overflow (fatal error) */ + *buffer=p; + if (clear) + **buffer='\0'; +} + +SC_FUNC void stgbuffer_cleanup(void) +{ + if (stgbuf!=NULL) { + free(stgbuf); + stgbuf=NULL; + stgmax=0; + } /* if */ + if (stgpipe!=NULL) { + free(stgpipe); + stgpipe=NULL; + pipemax=0; + pipeidx=0; + } /* if */ +} + +/* the variables "stgidx" and "staging" are declared in "scvars.c" */ + +/* stgmark + * + * Copies a mark into the staging buffer. At this moment there are three + * possible marks: + * sSTARTREORDER identifies the beginning of a series of expression + * strings that must be written to the output file in + * reordered order + * sENDREORDER identifies the end of 'reverse evaluation' + * sEXPRSTART + idx only valid within a block that is evaluated in + * reordered order, it identifies the start of an + * expression; the "idx" value is the argument position + * + * Global references: stgidx (altered) + * stgbuf (altered) + * staging (referred to only) + */ +SC_FUNC void stgmark(char mark) +{ + if (staging) { + CHECK_STGBUFFER(stgidx); + stgbuf[stgidx++]=mark; + } /* if */ +} + +static int rebuffer(char *str) +{ + if (sc_status==statWRITE) { + if (pipeidx>=2 && stgpipe[pipeidx-1]=='\0' && stgpipe[pipeidx-2]!='\n') + pipeidx-=1; /* overwrite last '\0' */ + while (*str!='\0') { /* copy to staging buffer */ + CHECK_STGPIPE(pipeidx); + stgpipe[pipeidx++]=*str++; + } /* while */ + CHECK_STGPIPE(pipeidx); + stgpipe[pipeidx++]='\0'; + } /* if */ + return TRUE; +} + +static int filewrite(char *str) +{ + if (sc_status==statWRITE) + return pc_writeasm(outf,str); + return TRUE; +} + +/* stgwrite + * + * Writes the string "st" to the staging buffer or to the output file. In the + * case of writing to the staging buffer, the terminating byte of zero is + * copied too, but... the optimizer can only work on complete lines (not on + * fractions of it. Therefore if the string is staged, if the last character + * written to the buffer is a '\0' and the previous-to-last is not a '\n', + * the string is concatenated to the last string in the buffer (the '\0' is + * overwritten). This also means an '\n' used in the middle of a string isn't + * recognized and could give wrong results with the optimizer. + * Even when writing to the output file directly, all strings are buffered + * until a whole line is complete. + * + * Global references: stgidx (altered) + * stgbuf (altered) + * staging (referred to only) + */ +SC_FUNC void stgwrite(const char *st) +{ + int len; + + if (staging) { + assert(stgidx==0 || stgbuf!=NULL); /* staging buffer must be valid if there is (apparently) something in it */ + if (stgidx>=2 && stgbuf[stgidx-1]=='\0' && stgbuf[stgidx-2]!='\n') + stgidx-=1; /* overwrite last '\0' */ + while (*st!='\0') { /* copy to staging buffer */ + CHECK_STGBUFFER(stgidx); + stgbuf[stgidx++]=*st++; + } /* while */ + CHECK_STGBUFFER(stgidx); + stgbuf[stgidx++]='\0'; + } else { + len=(stgbuf!=NULL) ? strlen(stgbuf) : 0; + CHECK_STGBUFFER(len+strlen(st)+1); + strcat(stgbuf,st); + len=strlen(stgbuf); + if (len>0 && stgbuf[len-1]=='\n') { + filewrite(stgbuf); + stgbuf[0]='\0'; + } /* if */ + } /* if */ +} + +/* stgout + * + * Writes the staging buffer to the output file via stgstring() (for + * reversing expressions in the buffer) and stgopt() (for optimizing). It + * resets "stgidx". + * + * Global references: stgidx (altered) + * stgbuf (referred to only) + * staging (referred to only) + */ +SC_FUNC void stgout(int index) +{ + int reordered=0; + int idx; + + if (!staging) + return; + assert(pipeidx==0); + + /* first pass: sub-expressions */ + if (sc_status==statWRITE) + reordered=stgstring(&stgbuf[index],&stgbuf[stgidx]); + stgidx=index; + + /* second pass: optimize the buffer created in the first pass */ + if (sc_status==statWRITE) { + if (reordered) { + stgopt(stgpipe,stgpipe+pipeidx,filewrite); + } else { + /* there is no sense in re-optimizing if the order of the sub-expressions + * did not change; so output directly + */ + for (idx=0; idx=0) + stack[arg].end=start-1; /* finish previous argument */ + arg=(unsigned char)*start - sEXPRSTART; + stack[arg].start=start+1; + if (arg>=argc) + argc=arg+1; + } /* if */ + start++; + } else { + start+=strlen(start)+1; + } /* if */ + } /* switch */ + } while (nest); /* enddo */ + if (arg>=0) + stack[arg].end=start-1; /* finish previous argument */ + while (argc>0) { + argc--; + stgstring(stack[argc].start,stack[argc].end); + } /* while */ + free(stack); + } else { + ptr=start; + while (ptr0) + filewrite(stgbuf); + } /* if */ + stgbuf[0]='\0'; +} + +/* phopt_init + * Initialize all sequence strings of the peehole optimizer. The strings + * are embedded in the .EXE file in compressed format, here we expand + * them (and allocate memory for the sequences). + */ +static SEQUENCE *sequences; + +SC_FUNC int phopt_init(void) +{ + int number, i, len; + char str[160]; + + /* count number of sequences */ + for (number=0; sequences_cmp[number].find!=NULL; number++) + /* nothing */; + number++; /* include an item for the NULL terminator */ + + if ((sequences=(SEQUENCE*)malloc(number * sizeof(SEQUENCE)))==NULL) + return FALSE; + + /* pre-initialize all to NULL (in case of failure) */ + for (i=0; i (PAWN_CELL_SIZE/4) * MAX_OPT_CAT + #define MAX_ALIAS sNAMEMAX +#else + #define MAX_ALIAS (PAWN_CELL_SIZE/4) * MAX_OPT_CAT +#endif + +static int matchsequence(char *start,char *end,char *pattern, + char symbols[MAX_OPT_VARS][MAX_ALIAS+1], + int *match_length) +{ + int var,i; + char str[MAX_ALIAS+1]; + char *start_org=start; + cell value; + char *ptr; + + *match_length=0; + for (var=0; var=end) + return FALSE; + switch (*pattern) { + case '%': /* new "symbol" */ + pattern++; + assert(isdigit(*pattern)); + var=atoi(pattern) - 1; + assert(var>=0 && var=0 && var=0 && var0) { /* delete a section */ + memmove(dest,dest+offset,dest_length-offset); + memset(dest+dest_length-offset,0xcc,offset); /* not needed, but for cleanlyness */ + } else if (offset<0) { /* insert a section */ + memmove(dest-offset, dest, dest_length); + } /* if */ + memcpy(dest, replace, repl_length); +} + +/* stgopt + * + * Optimizes the staging buffer by checking for series of instructions that + * can be coded more compact. The routine expects the lines in the staging + * buffer to be separated with '\n' and '\0' characters. + * + * The longest sequences should probably be checked first. + */ + +static void stgopt(char *start,char *end,int (*outputfunc)(char *str)) +{ + char symbols[MAX_OPT_VARS][MAX_ALIAS+1]; + int seq,match_length,repl_length; + int matches; + char *debut=start; /* save original start of the buffer */ + + assert(sequences!=NULL); + /* do not match anything if debug-level is maximum */ + if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) { + do { + matches=0; + start=debut; + while (start=0); + if (*sequences[seq].find=='\0') { + if (pc_optimize==sOPTIMIZE_NOMACRO) { + break; /* don't look further */ + } else { + seq++; /* continue with next string */ + continue; + } /* if */ + } /* if */ + if (matchsequence(start,end,sequences[seq].find,symbols,&match_length)) { + char *replace=replacesequence(sequences[seq].replace,symbols,&repl_length); + /* If the replacement is bigger than the original section, we may need + * to "grow" the staging buffer. This is quite complex, due to the + * re-ordering of expressions that can also happen in the staging + * buffer. In addition, it should not happen: the peephole optimizer + * must replace sequences with *shorter* sequences, not longer ones. + * So, I simply forbid sequences that are longer than the ones they + * are meant to replace. + */ + assert(match_length>=repl_length); + if (match_length>=repl_length) { + strreplace(start,replace,match_length,repl_length,(int)(end-start)); + end-=match_length-repl_length; + free(replace); + code_idx-=sequences[seq].savesize; + seq=0; /* restart search for matches */ + matches++; + } else { + /* actually, we should never get here (match_length0); + } /* if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) */ + + for (start=debut; start ;$lcl + * stack -4 push.c + * const.pri ;$exp + * stor.s.pri - + * ;$exp - + */ + { + #ifdef SCPACK + ";$lcl %1 %2!stack -4!const.pri %3!stor.s.pri %2!;$exp!", + ";$lcl %1 %2!push.c %3!;$exp!", + #else + "\224lcl\332\227ack -4!\233\206\256\227or\222\230\252", + "\224lcl\332\331\256\252", + #endif + seqsize(3,3) - seqsize(1,1) + }, + { + #ifdef SCPACK + ";$lcl %1 %2!stack -4!zero.pri!stor.s.pri %2!;$exp!", + ";$lcl %1 %2!push.c 0!;$exp!", + #else + "\224lcl\332\227ack -4!\373\227or\222\230\252", + "\224lcl\332\331 0!\252", + #endif + seqsize(3,2) - seqsize(1,1) + }, + /* During a calculation, the intermediate result must sometimes + * be moved from PRI to ALT, like in: + * push.pri move.alt + * load.s.pri n1 load.s.pri n1 + * pop.alt - + * + * The above also accurs for "load.pri" and for "const.pri", + * so add another two cases. + */ + { + #ifdef SCPACK + "push.pri!load.s.pri %1!pop.alt!", + "move.alt!load.s.pri %1!", + #else + "\244\333\241", + "\306\333", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "push.pri!load.pri %1!pop.alt!", + "move.alt!load.pri %1!", + #else + "\244\311\241", + "\306\311", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "push.pri!const.pri %1!pop.alt!", + "move.alt!const.pri %1!", + #else + "\244\330\241", + "\306\330", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "push.pri!zero.pri!pop.alt!", + "move.alt!zero.pri!", + #else + "\244\373\241", + "\306\373", + #endif + seqsize(3,0) - seqsize(2,0) + }, + /* saving PRI and then loading from its address + * occurs when indexing a multi-dimensional array + */ + { + #ifdef SCPACK + "push.pri!load.i!pop.alt!", + "move.alt!load.i!", + #else + "\244\342i\266", + "\306\342\355", + #endif + seqsize(3,0) - seqsize(2,0) + }, + /* An even simpler PUSH/POP optimization (occurs in + * switch statements): + * push.pri move.alt + * pop.alt - + */ + { + #ifdef SCPACK + "push.pri!pop.alt!", + "move.alt!", + #else + "\244\241", + "\306", + #endif + seqsize(2,0) - seqsize(1,0) + }, + /* Some simple arithmetic sequences + */ + { + #ifdef SCPACK + "move.alt!load.s.pri %1!add!", + "load.s.alt %1!add!", + #else + "\306\333\271", + "\375\271", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!load.pri %1!add!", + "load.alt %1!add!", + #else + "\306\311\271", + "\374\271", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!const.pri %1!add!", + "const.alt %1!add!", + #else + "\306\330\271", + "\356\271", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!load.s.pri %1!sub.alt!", + "load.s.alt %1!sub!", + #else + "\306\333sub\223", + "\375sub!", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!load.pri %1!sub.alt!", + "load.alt %1!sub!", + #else + "\306\311sub\223", + "\374sub!", + #endif + seqsize(3,1) - seqsize(2,1) + }, + { + #ifdef SCPACK + "move.alt!const.pri %1!sub.alt!", + "const.alt %1!sub!", + #else + "\306\330sub\223", + "\356sub!", + #endif + seqsize(3,1) - seqsize(2,1) + }, + /* User-defined operators first load the operands into registers and + * then have them pushed onto the stack. This can give rise to sequences + * like: + * const.pri n1 push.c n1 + * const.alt n2 push.c n2 + * push.pri - + * push.alt - + * A similar sequence occurs with the two PUSH.pri/alt instructions inverted. + * The first, second, or both CONST.pri/alt instructions can also be + * LOAD.pri/alt. + * This gives 2 x 4 cases. + */ + { + #ifdef SCPACK + "const.pri %1!const.alt %2!push.pri!push.alt!", + "push.c %1!push.c %2!", + #else + "\330\233\304\244\354", + "\331\202\331\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!const.alt %2!push.alt!push.pri!", + "push.c %2!push.c %1!", + #else + "\330\233\304\354\244", + "\331\221\331\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!load.alt %2!push.pri!push.alt!", + "push.c %1!push %2!", + #else + "\330\362\244\354", + "\331\202\216\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!load.alt %2!push.alt!push.pri!", + "push %2!push.c %1!", + #else + "\330\362\354\244", + "\216\221\331\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!const.alt %2!push.pri!push.alt!", + "push %1!push.c %2!", + #else + "\311\233\304\244\354", + "\216\202\331\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!const.alt %2!push.alt!push.pri!", + "push.c %2!push %1!", + #else + "\311\233\304\354\244", + "\331\221\216\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!load.alt %2!push.pri!push.alt!", + "push %1!push %2!", + #else + "\311\362\244\354", + "\216\202\216\221", + #endif + seqsize(4,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "load.pri %1!load.alt %2!push.alt!push.pri!", + "push %2!push %1!", + #else + "\311\362\354\244", + "\216\221\216\202", + #endif + seqsize(4,2) - seqsize(2,2) + }, + /* Function calls (parameters are passed on the stack) + * load.s.pri n1 push.s n1 + * push.pri - + * -------------------------------------- + * load.pri n1 push n1 + * push.pri - + * -------------------------------------- + * const.pri n1 push.c n1 + * push.pri - + * -------------------------------------- + * zero.pri push.c 0 + * push.pri - + * -------------------------------------- + * addr.pri n1 push.adr n1 + * push.pri - + * + * However, PRI must not be needed after this instruction + * if this shortcut is used. Check for the ;$par comment. + */ + { + #ifdef SCPACK + "load.s.pri %1!push.pri!;$par!", + "push.s %1!;$par!", + #else + "\226\264\255", + "\216\222\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.pri %1!push.pri!;$par!", + "push %1!;$par!", + #else + "\217\264\255", + "\216\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.pri %1!push.pri!;$par!", + "push.c %1!;$par!", + #else + "\233\264\255", + "\331\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.pri!push.pri!;$par!", + "push.c 0!;$par!", + #else + "\373\244\255", + "\331 0!\255", + #endif + seqsize(2,0) - seqsize(1,1) + }, + { + #ifdef SCPACK + "addr.pri %1!push.pri!;$par!", + "push.adr %1!;$par!", + #else + "\265\264\255", + "\216\326\202\255", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* References with a default value generate new cells on the heap + * dynamically. That code often ends with: + * move.pri push.alt + * push.pri - + */ + { + #ifdef SCPACK + "move.pri!push.pri!", + "push.alt!", + #else + "\300\236\244", + "\354", + #endif + seqsize(2,0) - seqsize(1,0) + }, + /* Simple arithmetic operations on constants. Noteworthy is the + * subtraction of a constant, since it is converted to the addition + * of the inverse value. + * const.alt n1 add.c n1 + * add - + * -------------------------------------- + * const.alt n1 add.c -n1 + * sub - + * -------------------------------------- + * const.alt n1 smul.c n1 + * smul - + * -------------------------------------- + * const.alt n1 eq.c.pri n1 + * eq - + */ + { + #ifdef SCPACK + "const.alt %1!add!", + "add.c %1!", + #else + "\356\271", + "\235\242\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.alt %1!sub!", + "add.c -%1!", + #else + "\356sub!", + "\235\242 -\201", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.alt %1!smul!", + "smul.c %1!", + #else + "\356smul!", + "smu\307\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.alt %1!eq!", + "eq.c.pri %1!", + #else + "\356\325", + "\262\242\225", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* Some operations use the alternative subtraction operation --these + * can also be optimized. + * const.pri n1 load.s.pri n2 + * load.s.alt n2 add.c -n1 + * sub.alt - + * -------------------------------------- + * const.pri n1 load.pri n2 + * load.alt n2 add.c -n1 + * sub.alt - + */ + { + #ifdef SCPACK + "const.pri %1!load.s.alt %2!sub.alt!", + "load.s.pri %2!add.c -%1!", + #else + "\330\226\304sub\223", + "\246\235\242 -\201", + #endif + seqsize(3,2) - seqsize(2,2) + }, + { + #ifdef SCPACK + "const.pri %1!load.alt %2!sub.alt!", + "load.pri %2!add.c -%1!", + #else + "\330\362sub\223", + "\334\235\242 -\201", + #endif + seqsize(3,2) - seqsize(2,2) + }, + /* With arrays indexed with constants that come from enumerations, it happens + * multiple add.c opcodes follow in sequence. + * add.c n1 add.c n1+n2 + * add.c n2 - + */ + { + #ifdef SCPACK + "add.c %1!add.c %2!", + "add.c %1+%2!", + #else + "\235\242\202\235\242\221", + "\235\242\267+%2!", + #endif + seqsize(2,2) - seqsize(1,1) + }, + /* Compare and jump + * eq jneq n1 + * jzer n1 - + * -------------------------------------- + * eq jeq n1 + * jnz n1 - + * -------------------------------------- + * neq jeq n1 + * jzer n1 - + * -------------------------------------- + * neq jneq n1 + * jnz n1 - + * Compares followed by jzer occur much more + * often than compares followed with jnz. So we + * take the easy route here. + * less jgeq n1 + * jzer n1 - + * -------------------------------------- + * leq jgrtr n1 + * jzer n1 - + * -------------------------------------- + * grtr jleq n1 + * jzer n1 - + * -------------------------------------- + * geq jless n1 + * jzer n1 - + * -------------------------------------- + * sless jsgeq n1 + * jzer n1 - + * -------------------------------------- + * sleq jsgrtr n1 + * jzer n1 - + * -------------------------------------- + * sgrtr jsleq n1 + * jzer n1 - + * -------------------------------------- + * sgeq jsless n1 + * jzer n1 - + */ + { + #ifdef SCPACK + "eq!jzer %1!", + "jneq %1!", + #else + "\325\323", + "jn\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "eq!jnz %1!", + "jeq %1!", + #else + "\325jnz\202", + "j\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "neq!jzer %1!", + "jeq %1!", + #else + "n\325\323", + "j\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "neq!jnz %1!", + "jneq %1!", + #else + "n\325jnz\202", + "jn\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "less!jzer %1!", + "jgeq %1!", + #else + "l\352!\323", + "jg\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "leq!jzer %1!", + "jgrtr %1!", + #else + "l\325\323", + "jg\353r\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "grtr!jzer %1!", + "jleq %1!", + #else + "g\353\237\323", + "jl\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "geq!jzer %1!", + "jless %1!", + #else + "g\325\323", + "jl\352\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sless!jzer %1!", + "jsgeq %1!", + #else + "\372!\323", + "j\320\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sleq!jzer %1!", + "jsgrtr %1!", + #else + "\321\325\323", + "j\371r\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sgrtr!jzer %1!", + "jsleq %1!", + #else + "\371\237\323", + "j\321\357", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "sgeq!jzer %1!", + "jsless %1!", + #else + "\320\325\323", + "j\372\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* Test for zero (common case, especially for strings) + * E.g. the test expression of: "for (i=0; str{i}!=0; ++i)" + * + * zero.alt jzer n1 + * jeq n1 - + * -------------------------------------- + * zero.alt jnz n1 + * jneq n1 - + */ + { + #ifdef SCPACK + "zero.alt!jeq %1!", + "jzer %1!", + #else + "\340\223j\357", + "\323", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.alt!jneq %1!", + "jnz %1!", + #else + "\340\223jn\357", + "jnz\202", + #endif + seqsize(2,1) - seqsize(1,1) + }, + /* Incrementing and decrementing leaves a value in + * in PRI which may not be used (for example, as the + * third expression in a "for" loop). + * inc n1 inc n1 ; ++n + * load.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * load.pri n1 inc n1 ; n++, e.g. "for (n=0; n<10; n++)" + * inc n1 ;$exp + * ;$exp - + * Plus the varieties for stack relative increments + * and decrements. + */ + { + #ifdef SCPACK + "inc %1!load.pri %1!;$exp!", + "inc %1!;$exp!", + #else + "inc\202\311\252", + "inc\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.pri %1!inc %1!;$exp!", + "inc %1!;$exp!", + #else + "\311inc\301", + "inc\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "inc.s %1!load.s.pri %1!;$exp!", + "inc.s %1!;$exp!", + #else + "inc\222\202\333\252", + "inc\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.s.pri %1!inc.s %1!;$exp!", + "inc.s %1!;$exp!", + #else + "\333inc\222\301", + "inc\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "dec %1!load.pri %1!;$exp!", + "dec %1!;$exp!", + #else + "dec\202\311\252", + "dec\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.pri %1!dec %1!;$exp!", + "dec %1!;$exp!", + #else + "\311dec\301", + "dec\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "dec.s %1!load.s.pri %1!;$exp!", + "dec.s %1!;$exp!", + #else + "dec\222\202\333\252", + "dec\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "load.s.pri %1!dec.s %1!;$exp!", + "dec.s %1!;$exp!", + #else + "\333dec\222\301", + "dec\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + /* ??? the same (increments and decrements) for references */ + /* Loading the constant zero has a special opcode. + * When storing zero in memory, the value of PRI must not be later on. + * const.pri 0 zero n1 + * stor.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * const.pri 0 zero.s n1 + * stor.s.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * zero.pri zero n1 + * stor.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * zero.pri zero.s n1 + * stor.s.pri n1 ;$exp + * ;$exp - + * -------------------------------------- + * const.pri 0 zero.pri + * -------------------------------------- + * const.alt 0 zero.alt + * The last two alternatives save more memory than they save + * time, but anyway... + */ + { + #ifdef SCPACK + "const.pri 0!stor.pri %1!;$exp!", + "zero %1!;$exp!", + #else + "\233\206 0!\227or\225\252", + "\340\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.pri 0!stor.s.pri %1!;$exp!", + "zero.s %1!;$exp!", + #else + "\233\206 0!\227or\222\225\252", + "\340\222\301", + #endif + seqsize(2,2) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.pri!stor.pri %1!;$exp!", + "zero %1!;$exp!", + #else + "\373\227or\225\252", + "\340\301", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "zero.pri!stor.s.pri %1!;$exp!", + "zero.s %1!;$exp!", + #else + "\373\227or\222\225\252", + "\340\222\301", + #endif + seqsize(2,1) - seqsize(1,1) + }, + { + #ifdef SCPACK + "const.pri 0!", + "zero.pri!", + #else + "\233\206 0!", + "\373", + #endif + seqsize(1,1) - seqsize(1,0) + }, + { + #ifdef SCPACK + "const.alt 0!", + "zero.alt!", + #else + "\233\212 0!", + "\340\223", + #endif + seqsize(1,1) - seqsize(1,0) + }, + + /* ------------------ */ + /* Macro instructions */ + /* ------------------ */ + + { "", "", 0 }, /* separator, so optimizer can stop before generating macro opcodes */ + + /* optimizing the calling of native functions (which always have a parameter + * count pushed before, and the stack pointer restored afterwards + */ + { + #ifdef SCPACK + "push.c %1!sysreq.c %2!stack %3!", //note: %3 == %1 + 4 + "sysreq.n %2 %1!", + #else + "\331\202sysr\262\242\221\227ack\256", + "sysr\262.n\220\202", + #endif + seqsize(3,3) - seqsize(1,2) + }, + /* ----- */ + /* Functions with many parameters with the same "type" have sequences like: + * push.c n1 push3.c n1 n2 n3 + * ;$par ;$par + * push.c n2 - + * ;$par - + * push.c n3 - + * ;$par - + * etc. etc. + * + * Similar sequences occur with PUSH, PUSH.s and PUSHADDR + */ + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!;$par!push.c %3!;$par!push.c %4!;$par!push.c %5!", + "push5.c %1 %2 %3 %4 %5!", + #else + "\331\336\242\345\242\256\257\242\335\257\242\2035!", + "\2165\242\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!;$par!push.c %3!;$par!push.c %4!", + "push4.c %1 %2 %3 %4!", + #else + "\331\336\242\345\242\256\257\242\335", + "\2164\242\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!;$par!push.c %3!", + "push3.c %1 %2 %3!", + #else + "\331\336\242\345\242\256", + "\2163\242\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push.c %1!;$par!push.c %2!", + "push2.c %1 %2!", + #else + "\331\336\242\221", + "\2162\242\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { + #ifdef SCPACK + "push %1!;$par!push %2!;$par!push %3!;$par!push %4!;$par!push %5!", + "push5 %1 %2 %3 %4 %5!", + #else + "\216\336\345\256\257\335\257\2035!", + "\2165\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push %1!;$par!push %2!;$par!push %3!;$par!push %4!", + "push4 %1 %2 %3 %4!", + #else + "\216\336\345\256\257\335", + "\2164\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push %1!;$par!push %2!;$par!push %3!", + "push3 %1 %2 %3!", + #else + "\216\336\345\256", + "\2163\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push %1!;$par!push %2!", + "push2 %1 %2!", + #else + "\216\336\221", + "\2162\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!;$par!push.s %3!;$par!push.s %4!;$par!push.s %5!", + "push5.s %1 %2 %3 %4 %5!", + #else + "\216\222\336\222\345\222\256\257\222\335\257\222\2035!", + "\2165\222\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!;$par!push.s %3!;$par!push.s %4!", + "push4.s %1 %2 %3 %4!", + #else + "\216\222\336\222\345\222\256\257\222\335", + "\2164\222\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!;$par!push.s %3!", + "push3.s %1 %2 %3!", + #else + "\216\222\336\222\345\222\256", + "\2163\222\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push.s %1!;$par!push.s %2!", + "push2.s %1 %2!", + #else + "\216\222\336\222\221", + "\2162\222\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!;$par!push.adr %4!;$par!push.adr %5!", + "push5.adr %1 %2 %3 %4 %5!", + #else + "\216\326\336\326\345\326\256\257\326\335\257\326\2035!", + "\2165\326\343\245\302\2035!", + #endif + seqsize(5,5) - seqsize(1,5) + }, + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!;$par!push.adr %4!", + "push4.adr %1 %2 %3 %4!", + #else + "\216\326\336\326\345\326\256\257\326\335", + "\2164\326\343\245\335", + #endif + seqsize(4,4) - seqsize(1,4) + }, + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!", + "push3.adr %1 %2 %3!", + #else + "\216\326\336\326\345\326\256", + "\2163\326\343\256", + #endif + seqsize(3,3) - seqsize(1,3) + }, + { + #ifdef SCPACK + "push.adr %1!;$par!push.adr %2!", + "push2.adr %1 %2!", + #else + "\216\326\336\326\221", + "\2162\326\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* Loading two registers at a time + * load.pri n1 load.both n1 n2 + * load.alt n2 - + * -------------------------------------- + * load.alt n2 load.both n1 n2 + * load.pri n1 - + * -------------------------------------- + * load.s.pri n1 load.s.both n1 n2 + * load.s.alt n2 - + * -------------------------------------- + * load.s.alt n2 load.s.both n1 n2 + * load.s.pri n1 - + */ + { + #ifdef SCPACK + "load.pri %1!load.alt %2!", + "load.both %1 %2!", + #else + "\311\362", + "\342\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.alt %2!load.pri %1!", + "load.both %1 %2!", + #else + "\362\311", + "\342\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.s.pri %1!load.s.alt %2!", + "load.s.both %1 %2!", + #else + "\333\226\304", + "\226.\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.s.alt %2!load.s.pri %1!", + "load.s.both %1 %2!", + #else + "\226\304\333", + "\226.\275th\332", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* Loading two registers and then pushing them occurs with user operators + * load.both n1 n2 push2 n1 n2 + * push.pri - + * push.alt - + * -------------------------------------- + * load.s.both n1 n2 push2.s n1 n2 + * push.pri - + * push.alt - + */ + { + #ifdef SCPACK + "load.both %1 %2!push.pri!push.alt!", + "push2 %1 %2!", + #else + "\342\275th\332\244\354", + "\2162\332", + #endif + seqsize(3,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "load.s.both %1 %2!push.pri!push.alt!", + "push2.s %1 %2!", + #else + "\226.\275th\332\244\354", + "\2162\222\332", + #endif + seqsize(3,2) - seqsize(1,2) + }, + /* Load a constant in a variable + * const.pri n1 const n2 n1 + * stor.pri n2 - + * -------------------------------------- + * const.pri n1 const.s n2 n1 + * stor.s.pri n2 - + */ + { + #ifdef SCPACK + "const.pri %1!stor.pri %2!", + "const %2 %1!", + #else + "\330\227or\230", + "\233\220\202", + #endif + seqsize(2,2) - seqsize(1,2) + }, + { + #ifdef SCPACK + "const.pri %1!stor.s.pri %2!", + "const.s %2 %1!", + #else + "\330\227or\222\230", + "\233\222\220\202", + #endif + seqsize(2,2) - seqsize(1,2) + }, + /* ----- */ + { NULL, NULL, 0 } +}; diff --git a/compiler-init/scexpand.c b/compiler-init/scexpand.c new file mode 100644 index 00000000..977c0121 --- /dev/null +++ b/compiler-init/scexpand.c @@ -0,0 +1,68 @@ +/* expand.c -- Byte Pair Encoding decompression */ +/* Copyright 1996 Philip Gage */ + +/* Byte Pair Compression appeared in the September 1997 + * issue of C/C++ Users Journal. The original source code + * may still be found at the web site of the magazine + * (www.cuj.com). + * + * The decompressor has been modified by me (Thiadmer + * Riemersma) to accept a string as input, instead of a + * complete file. + */ +#include +#include +#include "sc.h" + +#define STACKSIZE 16 + +SC_FUNC int strexpand(char *dest, unsigned char *source, int maxlen, unsigned char pairtable[128][2]) +{ + unsigned char stack[STACKSIZE]; + short c, top = 0; + int len; + + assert(maxlen > 0); + len = 1; /* already 1 byte for '\0' */ + for (;;) { + + /* Pop byte from stack or read byte from the input string */ + if (top) + c = stack[--top]; + else if ((c = *(unsigned char *)source++) == '\0') + break; + + /* Push pair on stack or output byte to the output string */ + if (c > 127) { + assert(top+2 <= STACKSIZE); + stack[top++] = pairtable[c-128][1]; + stack[top++] = pairtable[c-128][0]; + } + else { + len++; + if (maxlen > 1) { /* reserve one byte for the '\0' */ + *dest++ = (char)c; + maxlen--; + } + } + } + *dest = '\0'; + return len; /* return number of bytes decoded */ +} + +#if 0 /*for testing*/ +#include "sc5.scp" + +int main (int argc, char **argv) +{ + int i; + char str[128]; + + for (i=0; i<58; i++) { + strexpand(str, errmsg[i], sizeof str, SCPACK_TABLE); + printf("%s", str); + } /* for */ + return 0; +} +#endif + diff --git a/compiler-init/sci18n.c b/compiler-init/sci18n.c new file mode 100644 index 00000000..7b58e1f4 --- /dev/null +++ b/compiler-init/sci18n.c @@ -0,0 +1,428 @@ +/* Codepage translation to Unicode, and UTF-8 support + * + * The translation is based on codepage mapping files that are distributed + * by the Unicode consortium, see ftp://ftp.unicode.org/Public/MAPPINGS/. + * + * Character sets with a maximum of 256 codes are translated via a lookup + * table (these are Single-Byte Character Sets). Character sets like Shift-JIS + * with single-byte characters and multi-byte characters (introduced by a + * leader byte) are split into two tables: the 256-entry lookup table for + * the single-byte characters and an extended table for the multi-byte + * characters. The extended table is allocated dynamically; the lookup table + * is allocated statically, so loading SBCS tables cannot fail (if the tables + * themselves are valid, of course). + * + * Copyright (c) ITB CompuPhase, 2004-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: CODEPAGE.C,v 1.0 2004-02-18 12:13:04+01 thiadmer Exp thiadmer $ + */ +#include +#include +#include +#include +#include +#include "sc.h" + +#if !defined TRUE + #define FALSE 0 + #define TRUE 1 +#endif +#if !defined _MAX_PATH + #define _MAX_PATH 250 +#endif +#if !defined DIRSEP_CHAR + #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #define DIRSEP_CHAR '/' + #elif defined macintosh + #define DIRSEP_CHAR ':' + #else + #define DIRSEP_CHAR '\\' + #endif +#endif + +#if !defined ELEMENTS + #define ELEMENTS(array) (sizeof(array) / sizeof(array[0])) +#endif + +#if !defined NO_CODEPAGE + +#if !defined MAXCODEPAGE + #define MAXCODEPAGE 12 /* typically "cp" + 4 digits + ".txt" */ +#endif +#define INVALID 0xffffu /* 0xffff and 0xfffe are invalid Unicode characters */ +#define LEADBYTE 0xfffeu + +struct wordpair { + unsigned short index; + wchar_t code; +}; +static char cprootpath[_MAX_PATH] = { DIRSEP_CHAR, '\0' }; +static wchar_t bytetable[256]; +static struct wordpair *wordtable = NULL; +static unsigned wordtablesize = 0; +static unsigned wordtabletop = 0; + + +/* read in a line delimited by '\r' or '\n'; do NOT store the '\r' or '\n' into + * the string and ignore empty lines + * returns 1 for success and 0 for failure + */ +static int cp_readline(FILE *fp,char *string,size_t size) +{ + size_t count=0; + int c; + assert(size>1); + while ((c=fgetc(fp))!=EOF && count0) /* '\r' or '\n' ends a string */ + break; + /* if count==0, the line started with a '\r' or '\n', or perhaps line + * ends in the file are '\r\n' and we read and stopped on the '\r' of + * the preceding line + */ + } else { + string[count++]=(char)c; + } /* if */ + } /* while */ + string[count]='\0'; + return count>0; +} + +/* cp_path() sets the directory where all codepage files must be found (if + * the parameter to cp_set() specifies a full path, that is used instead). + * The path is specified into two parts: root and directory; the full path + * for the codepage direcory is just the concatenation of the two, with a + * directory separator in between. The directory is given in two parts, + * because often a program already retrieves its "home" directory and the + * codepages are most conveniently stored in a subdirectory of this home + * directory. + */ +SC_FUNC int cp_path(const char *root, const char *directory) +{ + size_t len1,len2; + int add_slash1,add_slash2; + + len1= (root!=NULL) ? strlen(root) : 0; + add_slash1= (len1==0 || root[len1-1]!=DIRSEP_CHAR); + len2= (directory!=NULL) ? strlen(directory) : 0; + add_slash2= (len2>0 && root[len2-1]!=DIRSEP_CHAR); + if (len1+add_slash1+len2+add_slash2>=(_MAX_PATH-MAXCODEPAGE)) + return FALSE; /* full filename may not fit */ + if (root!=NULL) + strcpy(cprootpath,root); + if (add_slash1) { + assert(len1==0 || cprootpath[len1]=='\0'); + cprootpath[len1]=DIRSEP_CHAR; + cprootpath[len1+1]='\0'; + } /* if */ + if (directory!=NULL) + strcat(cprootpath,directory); + if (add_slash2) { + assert(cprootpath[len1+add_slash1+len2]=='\0'); + cprootpath[len1+add_slash1+len2]=DIRSEP_CHAR; + cprootpath[len1+add_slash1+len2+1]='\0'; + } /* if */ + cp_set(NULL); /* start with a "linear" table (no translation) */ + return TRUE; +} + +/* cp_set() loads a codepage from a file. The name parameter may be a + * filename (including a full path) or it may be a partial codepage name. + * If the name parameter is NULL, the codepage is cleared to be a "linear" + * table (no translation). + * The following files are attempted to open (where specifies the + * value of the parameter): + * + * / + * /.txt + * /cp + * /cp.txt + */ +SC_FUNC int cp_set(const char *name) +{ + char filename[_MAX_PATH]; + FILE *fp=NULL; + unsigned index; + + /* for name==NULL, set up an identity table */ + if (name==NULL || *name=='\0') { + if (wordtable!=NULL) { + free(wordtable); + wordtable=NULL; + wordtablesize=0; + wordtabletop=0; + } /* if */ + for (index=0; indexMAXCODEPAGE) + return 0; + assert(strlen(name)+strlen(cprootpath)<_MAX_PATH); + strcpy(filename,cprootpath); + strcat(filename,name); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) { + /* try opening the file in the "root path" for codepages, with a ".txt" extension */ + if (strlen(name)+4>=MAXCODEPAGE) + return 0; + assert(strlen(filename)+4<_MAX_PATH); + strcat(filename,".txt"); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) { + /* try opening the file in the "root path" for codepages, with "cp" prefixed before the name */ + if (strlen(name)+2>MAXCODEPAGE) + return 0; + assert(2+strlen(name)+strlen(cprootpath)<_MAX_PATH); + strcpy(filename,cprootpath); + strcat(filename,"cp"); + strcat(filename,name); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) { + /* try opening the file in the "root path" for codepages, with "cp" prefixed an ".txt" appended */ + if (strlen(name)+2+4>MAXCODEPAGE) + return 0; + assert(strlen(filename)+4<_MAX_PATH); + strcat(filename,".txt"); + fp=fopen(filename,"rt"); + } /* if */ + if (fp==NULL) + return FALSE; /* all failed */ + + /* clear the tables */ + for (index=0; index0 && wordtable!=NULL); + if (wordtable!=NULL) { + free(wordtable); + wordtable=NULL; + wordtablesize=0; + wordtabletop=0; + } /* if */ + + /* read in the table */ + while (cp_readline(fp,filename,sizeof filename)) { + char *ptr; + if ((ptr=strchr(filename,'#'))!=NULL) + *ptr='\0'; /* strip of comment */ + for (ptr=filename; *ptr>0 && *ptr<' '; ptr++) + /* nothing */; /* skip leading whitespace */ + if (*ptr!='\0') { + /* content on line */ + unsigned code=LEADBYTE; + int num=sscanf(ptr,"%i %i",&index,&code); + /* if sscanf() returns 1 and the index is in range 0..255, then the + * code is a DBCS lead byte; if sscanf() returns 2 and index>=256, this + * is a double byte pair (lead byte + follower) + */ + if (num>=1 && index<256) { + bytetable[index]=(wchar_t)code; + } else if (num==2 && index>=256 && index=wordtablesize) { + /* grow the list */ + int newsize; + struct wordpair *newblock; + newsize= (wordtablesize==0) ? 128 : 2*wordtablesize; + newblock=(struct wordpair *)malloc(newsize*sizeof(*wordtable)); + if (newblock!=NULL) { + memcpy(newblock,wordtable,wordtabletop*sizeof(*wordtable)); + free(wordtable); + wordtable=newblock; + wordtablesize=newsize; + } /* if */ + } /* if */ + if (wordtabletop0 && (unsigned)wordtable[pos-1].index>index) { + wordtable[pos]=wordtable[pos-1]; + pos--; + } /* while */ + wordtable[pos].index=(unsigned short)index; + wordtable[pos].code=(wchar_t)code; + } /* if */ + } /* if */ + } /* if */ + } /* while */ + + fclose(fp); + return TRUE; +} + +SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr) +{ + wchar_t result; + + result=bytetable[*string++]; + /* check whether this is a leader code */ + if ((unsigned)result==LEADBYTE && wordtable!=NULL) { + /* look up the code via binary search */ + int low,high,mid; + unsigned short index=(unsigned short)(((*(string-1)) << 8) | *string); + string++; + assert(wordtabletop>0); + low=0; + high=wordtabletop-1; + while (lowwordtable[mid].index) + low=mid+1; + else + high=mid; + } /* while */ + assert(low==high); + if (wordtable[low].index==index) + result=wordtable[low].code; + } /* if */ + + if (endptr!=NULL) + *endptr=string; + return (cell)result; +} + +#endif /* NO_CODEPAGE */ + +#if !defined NO_UTF8 +SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr) +{ + int follow=0; + long lowmark=0; + unsigned char ch; + cell result=0; + + if (endptr!=NULL) + *endptr=string; + + for ( ;; ) { + ch=*string++; + + if (follow>0 && (ch & 0xc0)==0x80) { + /* leader code is active, combine with earlier code */ + result=(result << 6) | (ch & 0x3f); + if (--follow==0) { + /* encoding a character in more bytes than is strictly needed, + * is not really valid UTF-8; we are strict here to increase + * the chance of heuristic dectection of non-UTF-8 text + * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) + */ + if (result=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) + return -1; + } /* if */ + break; + } else if (follow==0 && (ch & 0x80)==0x80) { + /* UTF-8 leader code */ + if ((ch & 0xe0)==0xc0) { + /* 110xxxxx 10xxxxxx */ + follow=1; + lowmark=0x80L; + result=ch & 0x1f; + } else if ((ch & 0xf0)==0xe0) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + follow=2; + lowmark=0x800L; + result=ch & 0x0f; + } else if ((ch & 0xf8)==0xf0) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=3; + lowmark=0x10000L; + result=ch & 0x07; + } else if ((ch & 0xfc)==0xf8) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=4; + lowmark=0x200000L; + result=ch & 0x03; + } else if ((ch & 0xfe)==0xfc) { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (32 bits) */ + follow=5; + lowmark=0x4000000L; + result=ch & 0x01; + } else { + /* this is invalid UTF-8 */ + return -1; + } /* if */ + } else if (follow==0 && (ch & 0x80)==0x00) { + /* 0xxxxxxx (US-ASCII) */ + result=ch; + break; + } else { + /* this is invalid UTF-8 */ + return -1; + } /* if */ + + } /* for */ + + if (endptr!=NULL) + *endptr=string; + return result; +} +#endif + +SC_FUNC int scan_utf8(FILE *fp,const char *filename) +{ + #if defined NO_UTF8 + return 0; + #else + void *resetpos=pc_getpossrc(fp); + int utf8=TRUE; + int firstchar=TRUE,bom_found=FALSE; + const unsigned char *ptr; + + while (utf8 && pc_readsrc(fp,pline,sLINEMAX)!=NULL) { + ptr=pline; + if (firstchar) { + /* check whether the very first character on the very first line + * starts with a BYTE order mark + */ + cell c=get_utf8_char(ptr,&ptr); + bom_found= (c==0xfeff); + utf8= (c>=0); + firstchar=FALSE; + } /* if */ + while (utf8 && *ptr!='\0') + utf8= (get_utf8_char(ptr,&ptr)>=0); + } /* while */ + pc_resetsrc(fp,resetpos); + if (bom_found) { + unsigned char bom[3]; + if (!utf8) + error(77,filename); /* malformed UTF-8 encoding */ + pc_readsrc(fp,bom,3); + assert(bom[0]==0xef && bom[1]==0xbb && bom[2]==0xbf); + } /* if */ + return utf8; + #endif /* NO_UTF8 */ +} diff --git a/compiler-init/sclist.c b/compiler-init/sclist.c new file mode 100644 index 00000000..18c961b0 --- /dev/null +++ b/compiler-init/sclist.c @@ -0,0 +1,486 @@ +/* Pawn compiler - maintenance of various lists + * + * o Name list (aliases) + * o Include path list + * o Macro defintions (text substitutions) + * + * Copyright (c) ITB CompuPhase, 2001-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: sclist.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ +#include +#include +#include +#include +#include "sc.h" + +#if defined FORTIFY + #include +#endif + +/* a "private" implementation of strdup(), so that porting + * to other memory allocators becomes easier. + * By Søren Hannibal. + */ +SC_FUNC char* duplicatestring(const char* sourcestring) +{ + char* result=(char*)malloc(strlen(sourcestring)+1); + strcpy(result,sourcestring); + return result; +} + + +static stringpair *insert_stringpair(stringpair *root,char *first,char *second,int matchlength) +{ + stringpair *cur,*pred; + + assert(root!=NULL); + assert(first!=NULL); + assert(second!=NULL); + /* create a new node, and check whether all is okay */ + if ((cur=(stringpair*)malloc(sizeof(stringpair)))==NULL) + return NULL; + cur->first=duplicatestring(first); + cur->second=duplicatestring(second); + cur->matchlength=matchlength; + if (cur->first==NULL || cur->second==NULL) { + if (cur->first!=NULL) + free(cur->first); + if (cur->second!=NULL) + free(cur->second); + free(cur); + return NULL; + } /* if */ + /* link the node to the tree, find the position */ + for (pred=root; pred->next!=NULL && strcmp(pred->next->first,first)<0; pred=pred->next) + /* nothing */; + cur->next=pred->next; + pred->next=cur; + return cur; +} + +static void delete_stringpairtable(stringpair *root) +{ + stringpair *cur, *next; + + assert(root!=NULL); + cur=root->next; + while (cur!=NULL) { + next=cur->next; + assert(cur->first!=NULL); + assert(cur->second!=NULL); + free(cur->first); + free(cur->second); + free(cur); + cur=next; + } /* while */ + memset(root,0,sizeof(stringpair)); +} + +static stringpair *find_stringpair(stringpair *cur,char *first,int matchlength) +{ + int result=0; + + assert(matchlength>0); /* the function cannot handle zero-length comparison */ + assert(first!=NULL); + while (cur!=NULL && result<=0) { + result=(int)*cur->first - (int)*first; + if (result==0 && matchlength==cur->matchlength) { + result=strncmp(cur->first,first,matchlength); + if (result==0) + return cur; + } /* if */ + cur=cur->next; + } /* while */ + return NULL; +} + +static int delete_stringpair(stringpair *root,stringpair *item) +{ + stringpair *cur; + + assert(root!=NULL); + cur=root; + while (cur->next!=NULL) { + if (cur->next==item) { + cur->next=item->next; /* unlink from list */ + assert(item->first!=NULL); + assert(item->second!=NULL); + free(item->first); + free(item->second); + free(item); + return TRUE; + } /* if */ + cur=cur->next; + } /* while */ + return FALSE; +} + +/* ----- string list functions ----------------------------------- */ +static stringlist *insert_string(stringlist *root,char *string) +{ + stringlist *cur; + + assert(string!=NULL); + if ((cur=(stringlist*)malloc(sizeof(stringlist)))==NULL) + error(103); /* insufficient memory (fatal error) */ + if ((cur->line=duplicatestring(string))==NULL) + error(103); /* insufficient memory (fatal error) */ + /* insert as "last" */ + assert(root!=NULL); + while (root->next!=NULL) + root=root->next; + cur->next=root->next; + root->next=cur; + return cur; +} + +static char *get_string(stringlist *root,int index) +{ + stringlist *cur; + + assert(root!=NULL); + cur=root->next; + while (cur!=NULL && index-->0) + cur=cur->next; + if (cur!=NULL) { + assert(cur->line!=NULL); + return cur->line; + } /* if */ + return NULL; +} + +static int delete_string(stringlist *root,int index) +{ + stringlist *cur,*item; + + assert(root!=NULL); + for (cur=root; cur->next!=NULL && index>0; cur=cur->next,index--) + /* nothing */; + if (cur->next!=NULL) { + item=cur->next; + cur->next=item->next; /* unlink from list */ + assert(item->line!=NULL); + free(item->line); + free(item); + return TRUE; + } /* if */ + return FALSE; +} + +SC_FUNC void delete_stringtable(stringlist *root) +{ + stringlist *cur,*next; + + assert(root!=NULL); + cur=root->next; + while (cur!=NULL) { + next=cur->next; + assert(cur->line!=NULL); + free(cur->line); + free(cur); + cur=next; + } /* while */ + memset(root,0,sizeof(stringlist)); +} + + +/* ----- alias table --------------------------------------------- */ +static stringpair alias_tab = {NULL, NULL, NULL}; /* alias table */ + +SC_FUNC stringpair *insert_alias(char *name,char *alias) +{ + stringpair *cur; + + assert(name!=NULL); + assert(strlen(name)<=sNAMEMAX); + assert(alias!=NULL); + assert(strlen(alias)<=sNAMEMAX); + if ((cur=insert_stringpair(&alias_tab,name,alias,strlen(name)))==NULL) + error(103); /* insufficient memory (fatal error) */ + return cur; +} + +SC_FUNC int lookup_alias(char *target,char *name) +{ + stringpair *cur=find_stringpair(alias_tab.next,name,strlen(name)); + if (cur!=NULL) { + assert(strlen(cur->second)<=sNAMEMAX); + strcpy(target,cur->second); + } /* if */ + return cur!=NULL; +} + +SC_FUNC void delete_aliastable(void) +{ + delete_stringpairtable(&alias_tab); +} + +/* ----- include paths list -------------------------------------- */ +static stringlist includepaths = {NULL, NULL}; /* directory list for include files */ + +SC_FUNC stringlist *insert_path(char *path) +{ + return insert_string(&includepaths,path); +} + +SC_FUNC char *get_path(int index) +{ + return get_string(&includepaths,index); +} + +SC_FUNC void delete_pathtable(void) +{ + delete_stringtable(&includepaths); + assert(includepaths.next==NULL); +} + + +/* ----- text substitution patterns ------------------------------ */ +#if !defined NO_DEFINE + +static stringpair substpair = { NULL, NULL, NULL}; /* list of substitution pairs */ + +static stringpair *substindex['z'-PUBLIC_CHAR+1]; /* quick index to first character */ +static void adjustindex(char c) +{ + stringpair *cur; + assert(c>='A' && c<='Z' || c>='a' && c<='z' || c=='_' || c==PUBLIC_CHAR); + assert(PUBLIC_CHAR<'A' && 'A'<'_' && '_'<'z'); + + for (cur=substpair.next; cur!=NULL && cur->first[0]!=c; cur=cur->next) + /* nothing */; + substindex[(int)c-PUBLIC_CHAR]=cur; +} + +SC_FUNC stringpair *insert_subst(char *pattern,char *substitution,int prefixlen) +{ + stringpair *cur; + + assert(pattern!=NULL); + assert(substitution!=NULL); + if ((cur=insert_stringpair(&substpair,pattern,substitution,prefixlen))==NULL) + error(103); /* insufficient memory (fatal error) */ + adjustindex(*pattern); + return cur; +} + +SC_FUNC stringpair *find_subst(char *name,int length) +{ + stringpair *item; + assert(name!=NULL); + assert(length>0); + assert(*name>='A' && *name<='Z' || *name>='a' && *name<='z' || *name=='_' || *name==PUBLIC_CHAR); + item=substindex[(int)*name-PUBLIC_CHAR]; + if (item!=NULL) + item=find_stringpair(item,name,length); + return item; +} + +SC_FUNC int delete_subst(char *name,int length) +{ + stringpair *item; + assert(name!=NULL); + assert(length>0); + assert(*name>='A' && *name<='Z' || *name>='a' && *name<='z' || *name=='_' || *name==PUBLIC_CHAR); + item=substindex[(int)*name-PUBLIC_CHAR]; + if (item!=NULL) + item=find_stringpair(item,name,length); + if (item==NULL) + return FALSE; + delete_stringpair(&substpair,item); + adjustindex(*name); + return TRUE; +} + +SC_FUNC void delete_substtable(void) +{ + int i; + delete_stringpairtable(&substpair); + for (i=0; i=199901L + #define __STDC_FORMAT_MACROS + #define __STDC_CONSTANT_MACROS + #include /* automatically includes stdint.h */ +#elif (defined _MSC_VER || defined __BORLANDC__) && (defined _I64_MAX || defined HAVE_I64) + #define PRId64 "I64d" + #define PRIx64 "I64x" +#else + #define PRId64 "lld" + #define PRIx64 "llx" +#endif +#if PAWN_CELL_SIZE==64 + #define PRIdC PRId64 + #define PRIxC PRIx64 +#elif PAWN_CELL_SIZE==32 + #define PRIdC "ld" + #define PRIxC "lx" +#else + #define PRIdC "d" + #define PRIxC "x" +#endif + +static stringlist dbgstrings = {NULL, NULL}; + +SC_FUNC stringlist *insert_dbgfile(const char *filename) +{ + + if (sc_status==statWRITE && (sc_debug & sSYMBOLIC)!=0) { + char string[_MAX_PATH+40]; + assert(filename!=NULL); + assert(strlen(filename)+400) + linenr--; /* line numbers are zero-based in the debug information */ + sprintf(string,"L:%" PRIxC " %x",code_idx,linenr); + return insert_string(&dbgstrings,string); + } /* if */ + return NULL; +} + +SC_FUNC stringlist *insert_dbgsymbol(symbol *sym) +{ + if (sc_status==statWRITE && (sc_debug & sSYMBOLIC)!=0) { + char string[2*sNAMEMAX+128]; + char symname[2*sNAMEMAX+16]; + + funcdisplayname(symname,sym->name); + /* address tag:name codestart codeend ident vclass [tag:dim ...] */ + if (sym->ident==iFUNCTN) { + sprintf(string,"S:%" PRIxC " %x:%s %" PRIxC " %" PRIxC " %x %x", + sym->addr,sym->tag,symname,sym->addr,sym->codeaddr,sym->ident,sym->vclass); + } else { + sprintf(string,"S:%" PRIxC " %x:%s %" PRIxC " %" PRIxC " %x %x", + sym->addr,sym->tag,symname,sym->codeaddr,code_idx,sym->ident,sym->vclass); + } /* if */ + if (sym->ident==iARRAY || sym->ident==iREFARRAY) { + #if !defined NDEBUG + int count=sym->dim.array.level; + #endif + symbol *sub; + strcat(string," [ "); + for (sub=sym; sub!=NULL; sub=finddepend(sub)) { + assert(sub->dim.array.level==count--); + sprintf(string+strlen(string),"%x:%x ",sub->x.tags.index,sub->dim.array.length); + } /* for */ + strcat(string,"]"); + } /* if */ + + return insert_string(&dbgstrings,string); + } /* if */ + return NULL; +} + +SC_FUNC char *get_dbgstring(int index) +{ + return get_string(&dbgstrings,index); +} + +SC_FUNC void delete_dbgstringtable(void) +{ + delete_stringtable(&dbgstrings); + assert(dbgstrings.next==NULL); +} diff --git a/compiler-init/scmemfil.c b/compiler-init/scmemfil.c new file mode 100644 index 00000000..73a76c63 --- /dev/null +++ b/compiler-init/scmemfil.c @@ -0,0 +1,179 @@ +/* Pawn compiler + * + * Routines to maintain a "text file" in memory. + * + * Copyright (c) ITB CompuPhase, 2003-2006 + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from the + * use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: scmemfil.c 3579 2006-06-06 13:35:29Z thiadmer $ + */ + +#include +#include +#include +#include +#include "memfile.h" + +#if defined FORTIFY + #include +#endif + + +#define BUFFERSIZE 512u + +/* For every block, except the first: + * buffer points to a block that is BUFFERSIZE long that holds the data + * bufpos is the "used size" of the block + * For the first block: + * buffer points to the "file name" + * bufpos is the current "file pointer" + */ +typedef memfile_t MEMFILE; +#define tMEMFILE 1 + +#include "sc.h" + + +MEMFILE *mfcreate(char *filename) +{ + return memfile_creat(filename, 4096); +} + +void mfclose(MEMFILE *mf) +{ + memfile_destroy(mf); +} + +int mfdump(MEMFILE *mf) +{ + FILE *fp; + int okay; + + assert(mf!=NULL); + /* create the file */ + fp=fopen(mf->name, "wb"); + if (fp==NULL) + return 0; + + okay=1; + okay = okay & (fwrite(mf->base, mf->usedoffs, 1, fp)==(size_t)mf->usedoffs); + + fclose(fp); + return okay; +} + +long mflength(MEMFILE *mf) +{ + return mf->usedoffs; +} + +long mfseek(MEMFILE *mf,long offset,int whence) +{ + long length; + + assert(mf!=NULL); + if (mf->usedoffs == 0) + return 0L; /* early exit: not a single byte in the file */ + + /* find the size of the memory file */ + length=mflength(mf); + + /* convert the offset to an absolute position */ + switch (whence) { + case SEEK_SET: + break; + case SEEK_CUR: + offset+=mf->offs; + break; + case SEEK_END: + assert(offset<=0); + offset+=length; + break; + } /* switch */ + + /* clamp to the file length limit */ + if (offset<0) + offset=0; + else if (offset>length) + offset=length; + + /* set new position and return it */ + memfile_seek(mf, offset); + + return offset; +} + +unsigned int mfwrite(MEMFILE *mf,unsigned char *buffer,unsigned int size) +{ + return (memfile_write(mf, buffer, size) ? size : 0); +} + +unsigned int mfread(MEMFILE *mf,unsigned char *buffer,unsigned int size) +{ + return memfile_read(mf, buffer, size); +} + +char *mfgets(MEMFILE *mf,char *string,unsigned int size) +{ + char *ptr; + unsigned int read; + long seek; + + assert(mf!=NULL); + + read=mfread(mf,(unsigned char *)string,size); + if (read==0) + return NULL; + seek=0L; + + /* make sure that the string is zero-terminated */ + assert(read<=size); + if (read +#include +#include +#include +#include +#include "sc.h" +#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ + #include +#endif + +#if defined FORTIFY + #include +#endif + +typedef struct s_statelist { + struct s_statelist *next; + int *states; /* list of states in this combination */ + int numstates; /* number of items in the above list */ + int fsa; /* automaton id */ + int listid; /* unique id for this combination list */ +} statelist; + +static statelist statelist_tab = { NULL, NULL, 0, 0, 0}; /* state combinations table */ + + +static constvalue *find_automaton(const char *name,int *last) +{ + constvalue *ptr; + + assert(last!=NULL); + *last=0; + ptr=sc_automaton_tab.next; + while (ptr!=NULL) { + if (strcmp(name,ptr->name)==0) + return ptr; + if (ptr->index>*last) + *last=ptr->index; + ptr=ptr->next; + } /* while */ + return NULL; +} + +SC_FUNC constvalue *automaton_add(const char *name) +{ + constvalue *ptr; + int last; + + assert(strlen(name)name)); + ptr=find_automaton(name,&last); + if (ptr==NULL) { + assert(last+1 <= SHRT_MAX); + ptr=append_constval(&sc_automaton_tab,name,(cell)0,(short)(last+1)); + } /* if */ + return ptr; +} + +SC_FUNC constvalue *automaton_find(const char *name) +{ + int last; + return find_automaton(name,&last); +} + +SC_FUNC constvalue *automaton_findid(int id) +{ + constvalue *ptr; + for (ptr=sc_automaton_tab.next; ptr!=NULL && ptr->index!=id; ptr=ptr->next) + /* nothing */; + return ptr; +} + + +static constvalue *find_state(const char *name,int fsa,int *last) +{ + constvalue *ptr; + + assert(last!=NULL); + *last=0; + ptr=sc_state_tab.next; + while (ptr!=NULL) { + if (ptr->index==fsa) { + if (strcmp(name,ptr->name)==0) + return ptr; + if ((int)ptr->value>*last) + *last=(int)ptr->value; + } /* if */ + ptr=ptr->next; + } /* while */ + return NULL; +} + +SC_FUNC constvalue *state_add(const char *name,int fsa) +{ + constvalue *ptr; + int last; + + assert(strlen(name)name)); + ptr=find_state(name,fsa,&last); + if (ptr==NULL) { + assert(fsa <= SHRT_MAX); + ptr=append_constval(&sc_state_tab,name,(cell)(last+1),(short)fsa); + } /* if */ + return ptr; +} + +SC_FUNC constvalue *state_find(const char *name,int fsa_id) +{ + int last; /* dummy */ + return find_state(name,fsa_id,&last); +} + +SC_FUNC constvalue *state_findid(int id) +{ + constvalue *ptr; + for (ptr=sc_state_tab.next; ptr!=NULL && ptr->value!=id; ptr=ptr->next) + /* nothing */; + return ptr; +} + +SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid) +{ + int idx; + + assert(list!=NULL); + assert(listsize!=NULL); + assert(*listsize>=0); + assert(count!=NULL); + assert(*count>=0); + assert(*count<=*listsize); + + if (*count==*listsize) { + /* To avoid constantly calling malloc(), the list is grown by 4 states at + * a time. + */ + *listsize+=4; + *list=(int*)realloc(*list,*listsize*sizeof(int)); + if (*list==NULL) + error(103); /* insufficient memory */ + } /* if */ + + /* find the insertion point (the list has to stay sorted) */ + for (idx=0; idx<*count && *list[idx]0); + assert(last!=NULL); + *last=0; + ptr=statelist_tab.next; + while (ptr!=NULL) { + if (ptr->listid>*last) + *last=ptr->listid; + if (ptr->fsa==fsa && ptr->numstates==count) { + /* compare all states */ + for (i=0; istates[i]==list[i]; i++) + /* nothing */; + if (i==count) + return ptr; + } /* if */ + ptr=ptr->next; + } /* while */ + return NULL; +} + +static statelist *state_getlist_ptr(int listid) +{ + statelist *ptr; + + assert(listid>0); + for (ptr=statelist_tab.next; ptr!=NULL && ptr->listid!=listid; ptr=ptr->next) + /* nothing */; + return ptr; +} + +SC_FUNC int state_addlist(int *list,int count,int fsa) +{ + statelist *ptr; + int last; + + assert(list!=NULL); + assert(count>0); + ptr=state_findlist(list,count,fsa,&last); + if (ptr==NULL) { + if ((ptr=(statelist*)malloc(sizeof(statelist)))==NULL) + error(103); /* insufficient memory */ + if ((ptr->states=(int*)malloc(count*sizeof(int)))==NULL) { + free(ptr); + error(103); /* insufficient memory */ + } /* if */ + memcpy(ptr->states,list,count*sizeof(int)); + ptr->numstates=count; + ptr->fsa=fsa; + ptr->listid=last+1; + ptr->next=statelist_tab.next; + statelist_tab.next=ptr; + } /* if */ + assert(ptr!=NULL); + return ptr->listid; +} + +SC_FUNC void state_deletetable(void) +{ + statelist *ptr; + + while (statelist_tab.next!=NULL) { + ptr=statelist_tab.next; + /* unlink first */ + statelist_tab.next=ptr->next; + /* then delete */ + assert(ptr->states!=NULL); + free(ptr->states); + free(ptr); + } /* while */ +} + +SC_FUNC int state_getfsa(int listid) +{ + statelist *ptr; + + assert(listid>=0); + if (listid==0) + return -1; + + ptr=state_getlist_ptr(listid); + return (ptr!=NULL) ? ptr->fsa : -1; /* fsa 0 exists */ +} + +SC_FUNC int state_count(int listid) +{ + statelist *ptr=state_getlist_ptr(listid); + if (ptr==NULL) + return 0; /* unknown list, no states in it */ + return ptr->numstates; +} + +SC_FUNC int state_inlist(int listid,int state) +{ + statelist *ptr; + int i; + + ptr=state_getlist_ptr(listid); + if (ptr==NULL) + return FALSE; /* unknown list, state not in it */ + for (i=0; inumstates; i++) + if (ptr->states[i]==state) + return TRUE; + return FALSE; +} + +SC_FUNC int state_listitem(int listid,int index) +{ + statelist *ptr; + + ptr=state_getlist_ptr(listid); + assert(ptr!=NULL); + assert(index>=0 && indexnumstates); + return ptr->states[index]; +} + +static int checkconflict(statelist *psrc,statelist *ptgt) +{ + int s,t; + + assert(psrc!=NULL); + assert(ptgt!=NULL); + for (s=0; snumstates; s++) + for (t=0; tnumstates; t++) + if (psrc->states[s]==ptgt->states[t]) + return 1; /* state conflict */ + return 0; +} + +/* This function searches whether one of the states in the list of statelist id's + * of a symbol exists in any other statelist id's of the same function; it also + * verifies that all definitions of the symbol are in the same automaton. + */ +SC_FUNC void state_conflict(symbol *root) +{ + statelist *psrc,*ptgt; + constvalue *srcptr,*tgtptr; + symbol *sym; + + assert(root!=NULL); + for (sym=root->next; sym!=NULL; sym=sym->next) { + if (sym->parent!=NULL || sym->ident!=iFUNCTN) + continue; /* hierarchical data type or no function */ + if (sym->states==NULL) + continue; /* this function has no states */ + for (srcptr=sym->states->next; srcptr!=NULL; srcptr=srcptr->next) { + if (srcptr->index==-1) + continue; /* state list id -1 is a special case */ + psrc=state_getlist_ptr(srcptr->index); + assert(psrc!=NULL); + for (tgtptr=srcptr->next; tgtptr!=NULL; tgtptr=tgtptr->next) { + if (tgtptr->index==-1) + continue; /* state list id -1 is a special case */ + ptgt=state_getlist_ptr(tgtptr->index); + assert(ptgt!=NULL); + if (psrc->fsa!=ptgt->fsa && strcmp(sym->name,uENTRYFUNC)!=0) + error(83,sym->name); /* this function is part of another machine */ + if (checkconflict(psrc,ptgt)) + error(84,sym->name); /* state conflict */ + } /* for (tgtptr) */ + } /* for (srcptr) */ + } /* for (sym) */ +} + +/* check whether the two state lists (whose ids are passed in) share any + * states + */ +SC_FUNC int state_conflict_id(int listid1,int listid2) +{ + statelist *psrc,*ptgt; + + psrc=state_getlist_ptr(listid1); + assert(psrc!=NULL); + ptgt=state_getlist_ptr(listid2); + assert(ptgt!=NULL); + return checkconflict(psrc,ptgt); +} diff --git a/compiler-init/scvars.c b/compiler-init/scvars.c new file mode 100644 index 00000000..6a1365f8 --- /dev/null +++ b/compiler-init/scvars.c @@ -0,0 +1,113 @@ +/* Pawn compiler + * + * Global (cross-module) variables. + * + * Copyright (c) ITB CompuPhase, 1997-2006 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: scvars.c 3577 2006-06-02 16:22:52Z thiadmer $ + */ +#include +#include /* for _MAX_PATH */ +#include "sc.h" + +/* global variables + * + * All global variables that are shared amongst the compiler files are + * declared here. + */ +SC_VDEFINE symbol loctab; /* local symbol table */ +SC_VDEFINE symbol glbtab; /* global symbol table */ +SC_VDEFINE cell *litq; /* the literal queue */ +SC_VDEFINE unsigned char pline[sLINEMAX+1]; /* the line read from the input file */ +SC_VDEFINE const unsigned char *lptr; /* points to the current position in "pline" */ +SC_VDEFINE constvalue tagname_tab = { NULL, "", 0, 0}; /* tagname table */ +SC_VDEFINE constvalue libname_tab = { NULL, "", 0, 0}; /* library table (#pragma library "..." syntax) */ +SC_VDEFINE constvalue *curlibrary = NULL; /* current library */ +SC_VDEFINE int pc_addlibtable = TRUE; /* is the library table added to the AMX file? */ +SC_VDEFINE symbol *curfunc; /* pointer to current function */ +SC_VDEFINE char *inpfname; /* pointer to name of the file currently read from */ +SC_VDEFINE char outfname[_MAX_PATH]; /* intermediate (assembler) file name */ +SC_VDEFINE char binfname[_MAX_PATH]; /* binary file name */ +SC_VDEFINE char errfname[_MAX_PATH]; /* error file name */ +SC_VDEFINE char sc_ctrlchar = CTRL_CHAR; /* the control character (or escape character)*/ +SC_VDEFINE char sc_ctrlchar_org = CTRL_CHAR;/* the default control character */ +SC_VDEFINE int litidx = 0; /* index to literal table */ +SC_VDEFINE int litmax = sDEF_LITMAX; /* current size of the literal table */ +SC_VDEFINE int stgidx = 0; /* index to the staging buffer */ +SC_VDEFINE int sc_labnum = 0; /* number of (internal) labels */ +SC_VDEFINE int staging = 0; /* true if staging output */ +SC_VDEFINE cell declared = 0; /* number of local cells declared */ +SC_VDEFINE cell glb_declared=0; /* number of global cells declared */ +SC_VDEFINE cell code_idx = 0; /* number of bytes with generated code */ +SC_VDEFINE int ntv_funcid= 0; /* incremental number of native function */ +SC_VDEFINE int errnum = 0; /* number of errors */ +SC_VDEFINE int warnnum = 0; /* number of warnings */ +SC_VDEFINE int sc_debug = sCHKBOUNDS; /* by default: bounds checking+assertions */ +SC_VDEFINE int sc_packstr= FALSE; /* strings are packed by default? */ +SC_VDEFINE int sc_asmfile= FALSE; /* create .ASM file? */ +SC_VDEFINE int sc_listing= FALSE; /* create .LST file? */ +SC_VDEFINE int sc_compress=TRUE; /* compress bytecode? */ +SC_VDEFINE int sc_needsemicolon=TRUE;/* semicolon required to terminate expressions? */ +SC_VDEFINE int sc_dataalign=sizeof(cell);/* data alignment value */ +SC_VDEFINE int sc_alignnext=FALSE; /* must frame of the next function be aligned? */ +SC_VDEFINE int pc_docexpr=FALSE; /* must expression be attached to documentation comment? */ +SC_VDEFINE int curseg = 0; /* 1 if currently parsing CODE, 2 if parsing DATA */ +SC_VDEFINE cell pc_stksize=sDEF_AMXSTACK;/* default stack size */ +SC_VDEFINE cell pc_amxlimit=0; /* default abstract machine size limit = none */ +SC_VDEFINE cell pc_amxram=0; /* default abstract machine data size limit = none */ +SC_VDEFINE int freading = FALSE; /* Is there an input file ready for reading? */ +SC_VDEFINE int fline = 0; /* the line number in the current file */ +SC_VDEFINE short fnumber = 0; /* the file number in the file table (debugging) */ +SC_VDEFINE short fcurrent= 0; /* current file being processed (debugging) */ +SC_VDEFINE short sc_intest=FALSE; /* true if inside a test */ +SC_VDEFINE int sideeffect= 0; /* true if an expression causes a side-effect */ +SC_VDEFINE int stmtindent= 0; /* current indent of the statement */ +SC_VDEFINE int indent_nowarn=FALSE;/* skip warning "217 loose indentation" */ +SC_VDEFINE int sc_tabsize=8; /* number of spaces that a TAB represents */ +SC_VDEFINE short sc_allowtags=TRUE; /* allow/detect tagnames in lex() */ +SC_VDEFINE int sc_status; /* read/write status */ +SC_VDEFINE int sc_rationaltag=0; /* tag for rational numbers */ +SC_VDEFINE int rational_digits=0; /* number of fractional digits */ +SC_VDEFINE int sc_allowproccall=0; /* allow/detect tagnames in lex() */ +SC_VDEFINE short sc_is_utf8=FALSE; /* is this source file in UTF-8 encoding */ +SC_VDEFINE char *pc_depricate=NULL;/* if non-null, mark next declaration as depricated */ +SC_VDEFINE int sc_curstates=0; /* ID of the current state list */ +SC_VDEFINE int pc_optimize=sOPTIMIZE_NOMACRO; /* (peephole) optimization level */ +SC_VDEFINE int pc_memflags=0; /* special flags for the stack/heap usage */ + +SC_VDEFINE constvalue sc_automaton_tab = { NULL, "", 0, 0}; /* automaton table */ +SC_VDEFINE constvalue sc_state_tab = { NULL, "", 0, 0}; /* state table */ + +SC_VDEFINE FILE *inpf = NULL; /* file read from (source or include) */ +SC_VDEFINE FILE *inpf_org= NULL; /* main source file */ +SC_VDEFINE FILE *outf = NULL; /* (intermediate) text file written to */ + +SC_VDEFINE jmp_buf errbuf; + +#if !defined SC_LIGHT + SC_VDEFINE int sc_makereport=FALSE; /* generate a cross-reference report */ +#endif + +#if defined __WATCOMC__ && !defined NDEBUG + /* Watcom's CVPACK dislikes .OBJ files without functions */ + static int dummyfunc(void) + { + return 0; + } +#endif diff --git a/compiler-init/spcomp.sln b/compiler-init/spcomp.sln new file mode 100644 index 00000000..461a2bf6 --- /dev/null +++ b/compiler-init/spcomp.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spcomp", "spcomp.vcproj", "{B4C844FF-008D-4BD5-B82F-DC06E706C64B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Debug.ActiveCfg = Debug|Win32 + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Debug.Build.0 = Debug|Win32 + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Release.ActiveCfg = Release|Win32 + {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/compiler-init/spcomp.vcproj b/compiler-init/spcomp.vcproj new file mode 100644 index 00000000..028f5acb --- /dev/null +++ b/compiler-init/spcomp.vcproj @@ -0,0 +1,305 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler-init/svnrev.h b/compiler-init/svnrev.h new file mode 100644 index 00000000..b0d8a703 --- /dev/null +++ b/compiler-init/svnrev.h @@ -0,0 +1,9 @@ +#define SMC_VERSION 1 +#define SMC_REVISION 0 +#define SMC_BUILD 1 +#define SMC_VERSTRING "1.0.1.3599" + +#define SVN_REV 3599 +#define SVN_REVSTR "3599" +#define SVN_REVDATE "2006-07-05" +#define SVN_REVSTAMP 20060705L From a3cc0fa6ba3a50ca02edd28a703333d03a44c774 Mon Sep 17 00:00:00 2001 From: Borja Ferrer Date: Fri, 14 Jul 2006 02:12:40 +0000 Subject: [PATCH 06/20] bye --HG-- branch : faluco extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/faluco%408 --- compiler-init/compiler-init/amx.h | 462 -- compiler-init/compiler-init/amxdbg.h | 172 - compiler-init/compiler-init/libpawnc.c | 257 - compiler-init/compiler-init/libpawnc.rc | 55 - compiler-init/compiler-init/lstring.c | 124 - compiler-init/compiler-init/lstring.h | 18 - compiler-init/compiler-init/memfile.c | 105 - compiler-init/compiler-init/memfile.h | 23 - compiler-init/compiler-init/osdefs.h | 108 - compiler-init/compiler-init/pawn.ico | Bin 8478 -> 0 bytes compiler-init/compiler-init/pawncc.c | 30 - compiler-init/compiler-init/sc.h | 813 --- compiler-init/compiler-init/sc1.c | 5605 --------------------- compiler-init/compiler-init/sc2.c | 2801 ---------- compiler-init/compiler-init/sc3.c | 2453 --------- compiler-init/compiler-init/sc4.c | 1331 ----- compiler-init/compiler-init/sc5.c | 228 - compiler-init/compiler-init/sc5.scp | 342 -- compiler-init/compiler-init/sc6.c | 1298 ----- compiler-init/compiler-init/sc7.c | 703 --- compiler-init/compiler-init/sc7.scp | 2022 -------- compiler-init/compiler-init/scexpand.c | 68 - compiler-init/compiler-init/sci18n.c | 428 -- compiler-init/compiler-init/sclist.c | 486 -- compiler-init/compiler-init/scmemfil.c | 179 - compiler-init/compiler-init/scstate.c | 375 -- compiler-init/compiler-init/scvars.c | 113 - compiler-init/compiler-init/spcomp.sln | 21 - compiler-init/compiler-init/spcomp.vcproj | 305 -- compiler-init/compiler-init/svnrev.h | 9 - 30 files changed, 20934 deletions(-) delete mode 100644 compiler-init/compiler-init/amx.h delete mode 100644 compiler-init/compiler-init/amxdbg.h delete mode 100644 compiler-init/compiler-init/libpawnc.c delete mode 100644 compiler-init/compiler-init/libpawnc.rc delete mode 100644 compiler-init/compiler-init/lstring.c delete mode 100644 compiler-init/compiler-init/lstring.h delete mode 100644 compiler-init/compiler-init/memfile.c delete mode 100644 compiler-init/compiler-init/memfile.h delete mode 100644 compiler-init/compiler-init/osdefs.h delete mode 100644 compiler-init/compiler-init/pawn.ico delete mode 100644 compiler-init/compiler-init/pawncc.c delete mode 100644 compiler-init/compiler-init/sc.h delete mode 100644 compiler-init/compiler-init/sc1.c delete mode 100644 compiler-init/compiler-init/sc2.c delete mode 100644 compiler-init/compiler-init/sc3.c delete mode 100644 compiler-init/compiler-init/sc4.c delete mode 100644 compiler-init/compiler-init/sc5.c delete mode 100644 compiler-init/compiler-init/sc5.scp delete mode 100644 compiler-init/compiler-init/sc6.c delete mode 100644 compiler-init/compiler-init/sc7.c delete mode 100644 compiler-init/compiler-init/sc7.scp delete mode 100644 compiler-init/compiler-init/scexpand.c delete mode 100644 compiler-init/compiler-init/sci18n.c delete mode 100644 compiler-init/compiler-init/sclist.c delete mode 100644 compiler-init/compiler-init/scmemfil.c delete mode 100644 compiler-init/compiler-init/scstate.c delete mode 100644 compiler-init/compiler-init/scvars.c delete mode 100644 compiler-init/compiler-init/spcomp.sln delete mode 100644 compiler-init/compiler-init/spcomp.vcproj delete mode 100644 compiler-init/compiler-init/svnrev.h diff --git a/compiler-init/compiler-init/amx.h b/compiler-init/compiler-init/amx.h deleted file mode 100644 index 6efdd35e..00000000 --- a/compiler-init/compiler-init/amx.h +++ /dev/null @@ -1,462 +0,0 @@ -/* Pawn Abstract Machine (for the Pawn language) - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amx.h 3579 2006-06-06 13:35:29Z thiadmer $ - */ - -#ifndef AMX_H_INCLUDED -#define AMX_H_INCLUDED - -#include /* for size_t */ -#include - -#if defined FREEBSD && !defined __FreeBSD__ - #define __FreeBSD__ -#endif -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ - #include -#endif - -#if defined HAVE_STDINT_H - #include -#else - #if defined __LCC__ || defined __DMC__ || defined LINUX || (defined __WATCOMC__ && __WATCOMC__ >= 1200) - #if defined HAVE_INTTYPES_H - #include - #else - #include - #endif - #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L - /* The ISO C99 defines the int16_t and int_32t types. If the compiler got - * here, these types are probably undefined. - */ - #if defined __MACH__ - #include - typedef unsigned short int uint16_t; - typedef unsigned long int uint32_t; - #elif defined __FreeBSD__ - #include - #else - typedef short int int16_t; - typedef unsigned short int uint16_t; - #if defined SN_TARGET_PS2 - typedef int int32_t; - typedef unsigned int uint32_t; - #else - typedef long int int32_t; - typedef unsigned long int uint32_t; - #endif - #if defined __WIN32__ || defined _WIN32 || defined WIN32 - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; - #define HAVE_I64 - #elif defined __GNUC__ - typedef long long int64_t; - typedef unsigned long long uint64_t; - #define HAVE_I64 - #endif - #endif - #endif - #define HAVE_STDINT_H -#endif -#if defined _LP64 || defined WIN64 || defined _WIN64 - #if !defined __64BIT__ - #define __64BIT__ - #endif -#endif - -#if HAVE_ALLOCA_H - #include -#endif -#if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ - #if !defined alloca - #define alloca(n) _alloca(n) - #endif -#endif - -#if !defined arraysize - #define arraysize(array) (sizeof(array) / sizeof((array)[0])) -#endif -#if !defined assert_static - /* see "Compile-Time Assertions" by Ralf Holly, - * C/C++ Users Journal, November 2004 - */ - #define assert_static(e) \ - do { \ - enum { assert_static__ = 1/(e) }; \ - } while (0) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined PAWN_DLL - #if !defined AMX_NATIVE_CALL - #define AMX_NATIVE_CALL __stdcall - #endif - #if !defined AMXAPI - #define AMXAPI __stdcall - #endif -#endif - -/* calling convention for native functions */ -#if !defined AMX_NATIVE_CALL - #define AMX_NATIVE_CALL -#endif -/* calling convention for all interface functions and callback functions */ -#if !defined AMXAPI - #if defined STDECL - #define AMXAPI __stdcall - #elif defined CDECL - #define AMXAPI __cdecl - #elif defined GCC_HASCLASSVISIBILITY - #define AMXAPI __attribute__ ((visibility("default"))) - #else - #define AMXAPI - #endif -#endif -#if !defined AMXEXPORT - #define AMXEXPORT -#endif - -/* File format version (in CUR_FILE_VERSION) - * 0 (original version) - * 1 (opcodes JUMP.pri, SWITCH and CASETBL) - * 2 (compressed files) - * 3 (public variables) - * 4 (opcodes SWAP.pri/alt and PUSHADDR) - * 5 (tagnames table) - * 6 (reformatted header) - * 7 (name table, opcodes SYMTAG & SYSREQ.D) - * 8 (opcode STMT, renewed debug interface) - * 9 (macro opcodes) - * MIN_FILE_VERSION is the lowest file version number that the current AMX - * implementation supports. If the AMX file header gets new fields, this number - * often needs to be incremented. MAX_AMX_VERSION is the lowest AMX version that - * is needed to support the current file version. When there are new opcodes, - * this number needs to be incremented. - * The file version supported by the JIT may run behind MIN_AMX_VERSION. So - * there is an extra constant for it: MAX_FILE_VER_JIT. - */ -#define CUR_FILE_VERSION 9 /* current file version; also the current AMX version */ -#define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */ -#define MIN_AMX_VERSION 9 /* minimum AMX version needed to support the current file format */ -#define MAX_FILE_VER_JIT 8 /* file version supported by the JIT */ -#define MIN_AMX_VER_JIT 8 /* AMX version supported by the JIT */ - -#if !defined PAWN_CELL_SIZE - #define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */ -#endif -#if PAWN_CELL_SIZE==16 - typedef uint16_t ucell; - typedef int16_t cell; -#elif PAWN_CELL_SIZE==32 - typedef uint32_t ucell; - typedef int32_t cell; -#elif PAWN_CELL_SIZE==64 - typedef uint64_t ucell; - typedef int64_t cell; -#else - #error Unsupported cell size (PAWN_CELL_SIZE) -#endif - -#define UNPACKEDMAX (((cell)1 << (sizeof(cell)-1)*8) - 1) -#define UNLIMITED (~1u >> 1) - -struct tagAMX; -typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, cell *params); -typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index, - cell *result, cell *params); -typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx); -typedef int (AMXAPI *AMX_IDLE)(struct tagAMX *amx, int AMXAPI Exec(struct tagAMX *, cell *, int)); -#if !defined _FAR - #define _FAR -#endif - -#if defined _MSC_VER - #pragma warning(disable:4103) /* disable warning message 4103 that complains - * about pragma pack in a header file */ - #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ -#endif - -/* Some compilers do not support the #pragma align, which should be fine. Some - * compilers give a warning on unknown #pragmas, which is not so fine... - */ -#if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN - #define AMX_NO_ALIGN -#endif - -#if defined __GNUC__ - #define PACKED __attribute__((packed)) -#else - #define PACKED -#endif - -#if !defined AMX_NO_ALIGN - #if defined LINUX || defined __FreeBSD__ - #pragma pack(1) /* structures must be packed (byte-aligned) */ - #elif defined MACOS && defined __MWERKS__ - #pragma options align=mac68k - #else - #pragma pack(push) - #pragma pack(1) /* structures must be packed (byte-aligned) */ - #if defined __TURBOC__ - #pragma option -a- /* "pack" pragma for older Borland compilers */ - #endif - #endif -#endif - -typedef struct tagAMX_NATIVE_INFO { - const char _FAR *name PACKED; - AMX_NATIVE func PACKED; -} AMX_NATIVE_INFO; - -#define AMX_USERNUM 4 -#define sEXPMAX 19 /* maximum name length for file version <= 6 */ -#define sNAMEMAX 31 /* maximum name length of symbol name */ - -typedef struct tagAMX_FUNCSTUB { - ucell address PACKED; - char name[sEXPMAX+1] PACKED; -} AMX_FUNCSTUB; - -typedef struct tagFUNCSTUBNT { - ucell address PACKED; - uint32_t nameofs PACKED; -} AMX_FUNCSTUBNT; - -/* The AMX structure is the internal structure for many functions. Not all - * fields are valid at all times; many fields are cached in local variables. - */ -typedef struct tagAMX { - unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */ - unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */ - AMX_CALLBACK callback PACKED; - AMX_DEBUG debug PACKED; /* debug callback */ - /* for external functions a few registers must be accessible from the outside */ - cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */ - cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */ - cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */ - cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */ - cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */ - cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */ - int flags PACKED; /* current status, see amx_Flags() */ - /* user data */ - long usertags[AMX_USERNUM] PACKED; - void _FAR *userdata[AMX_USERNUM] PACKED; - /* native functions can raise an error */ - int error PACKED; - /* passing parameters requires a "count" field */ - int paramcount; - /* the sleep opcode needs to store the full AMX status */ - cell pri PACKED; - cell alt PACKED; - cell reset_stk PACKED; - cell reset_hea PACKED; - cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */ - #if defined JIT - /* support variables for the JIT */ - int reloc_size PACKED; /* required temporary buffer for relocations */ - long code_size PACKED; /* estimated memory footprint of the native code */ - #endif -} AMX; - -/* The AMX_HEADER structure is both the memory format as the file format. The - * structure is used internaly. - */ -typedef struct tagAMX_HEADER { - int32_t size PACKED; /* size of the "file" */ - uint16_t magic PACKED; /* signature */ - char file_version PACKED; /* file format version */ - char amx_version PACKED; /* required version of the AMX */ - int16_t flags PACKED; - int16_t defsize PACKED; /* size of a definition record */ - int32_t cod PACKED; /* initial value of COD - code block */ - int32_t dat PACKED; /* initial value of DAT - data block */ - int32_t hea PACKED; /* initial value of HEA - start of the heap */ - int32_t stp PACKED; /* initial value of STP - stack top */ - int32_t cip PACKED; /* initial value of CIP - the instruction pointer */ - int32_t publics PACKED; /* offset to the "public functions" table */ - int32_t natives PACKED; /* offset to the "native functions" table */ - int32_t libraries PACKED; /* offset to the table of libraries */ - int32_t pubvars PACKED; /* the "public variables" table */ - int32_t tags PACKED; /* the "public tagnames" table */ - int32_t nametable PACKED; /* name table */ -} AMX_HEADER; - -#if PAWN_CELL_SIZE==16 - #define AMX_MAGIC 0xf1e2 -#elif PAWN_CELL_SIZE==32 - #define AMX_MAGIC 0xf1e0 -#elif PAWN_CELL_SIZE==64 - #define AMX_MAGIC 0xf1e1 -#endif - -enum { - AMX_ERR_NONE, - /* reserve the first 15 error codes for exit codes of the abstract machine */ - AMX_ERR_EXIT, /* forced exit */ - AMX_ERR_ASSERT, /* assertion failed */ - AMX_ERR_STACKERR, /* stack/heap collision */ - AMX_ERR_BOUNDS, /* index out of bounds */ - AMX_ERR_MEMACCESS, /* invalid memory access */ - AMX_ERR_INVINSTR, /* invalid instruction */ - AMX_ERR_STACKLOW, /* stack underflow */ - AMX_ERR_HEAPLOW, /* heap underflow */ - AMX_ERR_CALLBACK, /* no callback, or invalid callback */ - AMX_ERR_NATIVE, /* native function failed */ - AMX_ERR_DIVIDE, /* divide by zero */ - AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */ - AMX_ERR_INVSTATE, /* invalid state for this access */ - - AMX_ERR_MEMORY = 16, /* out of memory */ - AMX_ERR_FORMAT, /* invalid file format */ - AMX_ERR_VERSION, /* file is for a newer version of the AMX */ - AMX_ERR_NOTFOUND, /* function not found */ - AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */ - AMX_ERR_DEBUG, /* debugger cannot run */ - AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */ - AMX_ERR_USERDATA, /* unable to set user data field (table full) */ - AMX_ERR_INIT_JIT, /* cannot initialize the JIT */ - AMX_ERR_PARAMS, /* parameter error */ - AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */ - AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */ -}; - -/* AMX_FLAG_CHAR16 0x01 no longer used */ -#define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */ -#define AMX_FLAG_COMPACT 0x04 /* compact encoding */ -#define AMX_FLAG_SLEEP 0x08 /* script uses the sleep instruction (possible re-entry or power-down mode) */ -#define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no BREAK opcodes */ -#define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */ -#define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */ -#define AMX_FLAG_BROWSE 0x4000 /* busy browsing */ -#define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ - -#define AMX_EXEC_MAIN (-1) /* start at program entry point */ -#define AMX_EXEC_CONT (-2) /* continue from last address */ - -#define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24)) - -#if !defined AMX_COMPACTMARGIN - #define AMX_COMPACTMARGIN 64 -#endif - -/* for native functions that use floating point parameters, the following - * two macros are convenient for casting a "cell" into a "float" type _without_ - * changing the bit pattern - */ -#if PAWN_CELL_SIZE==32 - #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ - #define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */ -#elif PAWN_CELL_SIZE==64 - #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ - #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ -#else - #error Unsupported cell size -#endif - -#define amx_StrParam(amx,param,result) \ - do { \ - cell *amx_cstr_; int amx_length_; \ - amx_GetAddr((amx), (param), &amx_cstr_); \ - amx_StrLen(amx_cstr_, &amx_length_); \ - if (amx_length_ > 0 && \ - ((result) = (void*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \ - amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \ - else (result) = NULL; \ - } while (0) - -uint16_t * AMXAPI amx_Align16(uint16_t *v); -uint32_t * AMXAPI amx_Align32(uint32_t *v); -#if defined _I64_MAX || defined HAVE_I64 - uint64_t * AMXAPI amx_Align64(uint64_t *v); -#endif -int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); -int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params); -int AMXAPI amx_Cleanup(AMX *amx); -int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); -int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); -int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); -int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); -int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr); -int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname); -int AMXAPI amx_Flags(AMX *amx,uint16_t *flags); -int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr); -int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname); -int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname); -int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr); -int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size); -int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id); -int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr); -int AMXAPI amx_Init(AMX *amx, void *program); -int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code); -int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap); -int AMXAPI amx_NameLength(AMX *amx, int *length); -AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func); -int AMXAPI amx_NumNatives(AMX *amx, int *number); -int AMXAPI amx_NumPublics(AMX *amx, int *number); -int AMXAPI amx_NumPubVars(AMX *amx, int *number); -int AMXAPI amx_NumTags(AMX *amx, int *number); -int AMXAPI amx_Push(AMX *amx, cell value); -int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); -int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); -int AMXAPI amx_RaiseError(AMX *amx, int error); -int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); -int AMXAPI amx_Release(AMX *amx, cell amx_addr); -int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback); -int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug); -int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size); -int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr); -int AMXAPI amx_StrLen(const cell *cstring, int *length); -int AMXAPI amx_UTF8Check(const char *string, int *length); -int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value); -int AMXAPI amx_UTF8Len(const cell *cstr, int *length); -int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); - -#if PAWN_CELL_SIZE==16 - #define amx_AlignCell(v) amx_Align16(v) -#elif PAWN_CELL_SIZE==32 - #define amx_AlignCell(v) amx_Align32(v) -#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) - #define amx_AlignCell(v) amx_Align64(v) -#else - #error Unsupported cell size -#endif - -#define amx_RegisterFunc(amx, name, func) \ - amx_Register((amx), amx_NativeInfo((name),(func)), 1); - -#if !defined AMX_NO_ALIGN - #if defined LINUX || defined __FreeBSD__ - #pragma pack() /* reset default packing */ - #elif defined MACOS && defined __MWERKS__ - #pragma options align=reset - #else - #pragma pack(pop) /* reset previous packing */ - #endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* AMX_H_INCLUDED */ diff --git a/compiler-init/compiler-init/amxdbg.h b/compiler-init/compiler-init/amxdbg.h deleted file mode 100644 index acc4d1b3..00000000 --- a/compiler-init/compiler-init/amxdbg.h +++ /dev/null @@ -1,172 +0,0 @@ -/* Abstract Machine for the Pawn compiler, debugger support - * - * This file contains extra definitions that are convenient for debugger - * support. - * - * Copyright (c) ITB CompuPhase, 2005-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: amxdbg.h 3519 2006-02-17 17:57:04Z thiadmer $ - */ - -#ifndef AMXDBG_H_INCLUDED -#define AMXDBG_H_INCLUDED - -#ifndef AMX_H_INCLUDED - #include "amx.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Some compilers do not support the #pragma align, which should be fine. Some - * compilers give a warning on unknown #pragmas, which is not so fine... - */ -#if defined SN_TARGET_PS2 || defined __GNUC__ - #define AMX_NO_ALIGN -#endif - -#if defined __GNUC__ - #define PACKED __attribute__((packed)) -#else - #define PACKED -#endif - -#if !defined AMX_NO_ALIGN - #if defined LINUX || defined __FreeBSD__ - #pragma pack(1) /* structures must be packed (byte-aligned) */ - #elif defined MACOS && defined __MWERKS__ - #pragma options align=mac68k - #else - #pragma pack(push) - #pragma pack(1) /* structures must be packed (byte-aligned) */ - #if defined __TURBOC__ - #pragma option -a- /* "pack" pragma for older Borland compilers */ - #endif - #endif -#endif - -typedef struct tagAMX_DBG_HDR { - int32_t size PACKED; /* size of the debug information chunk */ - uint16_t magic PACKED; /* signature, must be 0xf1ef */ - char file_version PACKED; /* file format version */ - char amx_version PACKED; /* required version of the AMX */ - int16_t flags PACKED; /* currently unused */ - int16_t files PACKED; /* number of entries in the "file" table */ - int16_t lines PACKED; /* number of entries in the "line" table */ - int16_t symbols PACKED; /* number of entries in the "symbol" table */ - int16_t tags PACKED; /* number of entries in the "tag" table */ - int16_t automatons PACKED; /* number of entries in the "automaton" table */ - int16_t states PACKED; /* number of entries in the "state" table */ -} PACKED AMX_DBG_HDR; -#define AMX_DBG_MAGIC 0xf1ef - -typedef struct tagAMX_DBG_FILE { - ucell address PACKED; /* address in the code segment where generated code (for this file) starts */ - const char name[1] PACKED; /* ASCII string, zero-terminated */ -} PACKED AMX_DBG_FILE; - -typedef struct tagAMX_DBG_LINE { - ucell address PACKED; /* address in the code segment where generated code (for this line) starts */ - int32_t line PACKED; /* line number */ -} PACKED AMX_DBG_LINE; - -typedef struct tagAMX_DBG_SYMBOL { - ucell address PACKED; /* address in the data segment or relative to the frame */ - int16_t tag PACKED; /* tag for the symbol */ - ucell codestart PACKED; /* address in the code segment from which this symbol is valid (in scope) */ - ucell codeend PACKED; /* address in the code segment until which this symbol is valid (in scope) */ - char ident PACKED; /* kind of symbol (function/variable) */ - char vclass PACKED; /* class of symbol (global/local) */ - int16_t dim PACKED; /* number of dimensions */ - const char name[1] PACKED; /* ASCII string, zero-terminated */ -} PACKED AMX_DBG_SYMBOL; - -typedef struct tagAMX_DBG_SYMDIM { - int16_t tag PACKED; /* tag for the array dimension */ - ucell size PACKED; /* size of the array dimension */ -} PACKED AMX_DBG_SYMDIM; - -typedef struct tagAMX_DBG_TAG { - int16_t tag PACKED; /* tag id */ - const char name[1] PACKED; /* ASCII string, zero-terminated */ -} PACKED AMX_DBG_TAG; - -typedef struct tagAMX_DBG_MACHINE { - int16_t automaton PACKED; /* automaton id */ - ucell address PACKED; /* address of state variable */ - const char name[1] PACKED; /* ASCII string, zero-terminated */ -} PACKED AMX_DBG_MACHINE; - -typedef struct tagAMX_DBG_STATE { - int16_t state PACKED; /* state id */ - int16_t automaton PACKED; /* automaton id */ - const char name[1] PACKED; /* ASCII string, zero-terminated */ -} PACKED AMX_DBG_STATE; - -typedef struct tagAMX_DBG { - AMX_DBG_HDR *hdr PACKED; /* points to the AMX_DBG header */ - AMX_DBG_FILE **filetbl PACKED; - AMX_DBG_LINE *linetbl PACKED; - AMX_DBG_SYMBOL **symboltbl PACKED; - AMX_DBG_TAG **tagtbl PACKED; - AMX_DBG_MACHINE **automatontbl PACKED; - AMX_DBG_STATE **statetbl PACKED; -} PACKED AMX_DBG; - -#if !defined iVARIABLE - #define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ - #define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ - #define iARRAY 3 - #define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ - #define iFUNCTN 9 -#endif - - -int AMXAPI dbg_FreeInfo(AMX_DBG *amxdbg); -int AMXAPI dbg_LoadInfo(AMX_DBG *amxdbg, FILE *fp); - -int AMXAPI dbg_LookupFile(AMX_DBG *amxdbg, ucell address, const char **filename); -int AMXAPI dbg_LookupFunction(AMX_DBG *amxdbg, ucell address, const char **funcname); -int AMXAPI dbg_LookupLine(AMX_DBG *amxdbg, ucell address, long *line); - -int AMXAPI dbg_GetFunctionAddress(AMX_DBG *amxdbg, const char *funcname, const char *filename, ucell *address); -int AMXAPI dbg_GetLineAddress(AMX_DBG *amxdbg, long line, const char *filename, ucell *address); -int AMXAPI dbg_GetAutomatonName(AMX_DBG *amxdbg, int automaton, const char **name); -int AMXAPI dbg_GetStateName(AMX_DBG *amxdbg, int state, const char **name); -int AMXAPI dbg_GetTagName(AMX_DBG *amxdbg, int tag, const char **name); -int AMXAPI dbg_GetVariable(AMX_DBG *amxdbg, const char *symname, ucell scopeaddr, const AMX_DBG_SYMBOL **sym); -int AMXAPI dbg_GetArrayDim(AMX_DBG *amxdbg, const AMX_DBG_SYMBOL *sym, const AMX_DBG_SYMDIM **symdim); - - -#if !defined AMX_NO_ALIGN - #if defined LINUX || defined __FreeBSD__ - #pragma pack() /* reset default packing */ - #elif defined MACOS && defined __MWERKS__ - #pragma options align=reset - #else - #pragma pack(pop) /* reset previous packing */ - #endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* AMXDBG_H_INCLUDED */ diff --git a/compiler-init/compiler-init/libpawnc.c b/compiler-init/compiler-init/libpawnc.c deleted file mode 100644 index ca42d1a9..00000000 --- a/compiler-init/compiler-init/libpawnc.c +++ /dev/null @@ -1,257 +0,0 @@ -/* LIBPAWNC.C - * - * A "glue file" for building the Pawn compiler as a DLL or shared library. - * - * Copyright (c) ITB CompuPhase, 2000-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: libsc.c 3114 2005-03-17 14:48:29Z thiadmer $ - */ -#include -#include -#include -#include -#include -#include "sc.h" -#include "memfile.h" - -/* pc_printf() - * Called for general purpose "console" output. This function prints general - * purpose messages; errors go through pc_error(). The function is modelled - * after printf(). - */ -int pc_printf(const char *message,...) -{ - int ret; - va_list argptr; - - va_start(argptr,message); - ret=vprintf(message,argptr); - va_end(argptr); - - return ret; -} - -/* pc_error() - * Called for producing error output. - * number the error number (as documented in the manual) - * message a string describing the error with embedded %d and %s tokens - * filename the name of the file currently being parsed - * firstline the line number at which the expression started on which - * the error was found, or -1 if there is no "starting line" - * lastline the line number at which the error was detected - * argptr a pointer to the first of a series of arguments (for macro - * "va_arg") - * Return: - * If the function returns 0, the parser attempts to continue compilation. - * On a non-zero return value, the parser aborts. - */ -int pc_error(int number,char *message,char *filename,int firstline,int lastline,va_list argptr) -{ -static char *prefix[3]={ "error", "fatal error", "warning" }; - - if (number!=0) { - char *pre; - - pre=prefix[number/100]; - if (firstline>=0) - fprintf(stderr,"%s(%d -- %d) : %s %03d: ",filename,firstline,lastline,pre,number); - else - fprintf(stderr,"%s(%d) : %s %03d: ",filename,lastline,pre,number); - } /* if */ - vfprintf(stderr,message,argptr); - fflush(stderr); - return 0; -} - -/* pc_opensrc() - * Opens a source file (or include file) for reading. The "file" does not have - * to be a physical file, one might compile from memory. - * filename the name of the "file" to read from - * Return: - * The function must return a pointer, which is used as a "magic cookie" to - * all I/O functions. When failing to open the file for reading, the - * function must return NULL. - * Note: - * Several "source files" may be open at the same time. Specifically, one - * file can be open for reading and another for writing. - */ -void *pc_opensrc(char *filename) -{ - return fopen(filename,"rt"); -} - -/* pc_createsrc() - * Creates/overwrites a source file for writing. The "file" does not have - * to be a physical file, one might compile from memory. - * filename the name of the "file" to create - * Return: - * The function must return a pointer which is used as a "magic cookie" to - * all I/O functions. When failing to open the file for reading, the - * function must return NULL. - * Note: - * Several "source files" may be open at the same time. Specifically, one - * file can be open for reading and another for writing. - */ -void *pc_createsrc(char *filename) -{ - return fopen(filename,"wt"); -} - -/* pc_closesrc() - * Closes a source file (or include file). The "handle" parameter has the - * value that pc_opensrc() returned in an earlier call. - */ -void pc_closesrc(void *handle) -{ - assert(handle!=NULL); - fclose((FILE*)handle); -} - -/* pc_resetsrc() - * "position" may only hold a pointer that was previously obtained from - * pc_getpossrc() - */ -void pc_resetsrc(void *handle,void *position) -{ - assert(handle!=NULL); - fsetpos((FILE*)handle,(fpos_t *)position); -} - -/* pc_readsrc() - * Reads a single line from the source file (or up to a maximum number of - * characters if the line in the input file is too long). - */ -char *pc_readsrc(void *handle,unsigned char *target,int maxchars) -{ - return fgets((char*)target,maxchars,(FILE*)handle); -} - -/* pc_writesrc() - * Writes to to the source file. There is no automatic line ending; to end a - * line, write a "\n". - */ -int pc_writesrc(void *handle,unsigned char *source) -{ - return fputs((char*)source,(FILE*)handle) >= 0; -} - -void *pc_getpossrc(void *handle) -{ - static fpos_t lastpos; /* may need to have a LIFO stack of such positions */ - - fgetpos((FILE*)handle,&lastpos); - return &lastpos; -} - -int pc_eofsrc(void *handle) -{ - return feof((FILE*)handle); -} - -/* should return a pointer, which is used as a "magic cookie" to all I/O - * functions; return NULL for failure - */ -void *pc_openasm(char *filename) -{ - #if defined __MSDOS__ || defined SC_LIGHT - return fopen(filename,"w+t"); - #else - return mfcreate(filename); - #endif -} - -void pc_closeasm(void *handle, int deletefile) -{ - #if defined __MSDOS__ || defined SC_LIGHT - if (handle!=NULL) - fclose((FILE*)handle); - if (deletefile) - remove(outfname); - #else - if (handle!=NULL) { - if (!deletefile) - mfdump((MEMFILE*)handle); - mfclose((MEMFILE*)handle); - } /* if */ - #endif -} - -void pc_resetasm(void *handle) -{ - assert(handle!=NULL); - #if defined __MSDOS__ || defined SC_LIGHT - fflush((FILE*)handle); - fseek((FILE*)handle,0,SEEK_SET); - #else - mfseek((MEMFILE*)handle,0,SEEK_SET); - #endif -} - -int pc_writeasm(void *handle,char *string) -{ - #if defined __MSDOS__ || defined SC_LIGHT - return fputs(string,(FILE*)handle) >= 0; - #else - return mfputs((MEMFILE*)handle,string); - #endif -} - -char *pc_readasm(void *handle, char *string, int maxchars) -{ - #if defined __MSDOS__ || defined SC_LIGHT - return fgets(string,maxchars,(FILE*)handle); - #else - return mfgets((MEMFILE*)handle,string,maxchars); - #endif -} - -/* Should return a pointer, which is used as a "magic cookie" to all I/O - * functions; return NULL for failure. - */ -void *pc_openbin(char *filename) -{ - return memfile_creat(filename, 1); -} - -void pc_closebin(void *handle,int deletefile) -{ - if (deletefile) - { - memfile_destroy((memfile_t *)handle); - } -} - -/* pc_resetbin() - * Can seek to any location in the file. - * The offset is always from the start of the file. - */ -void pc_resetbin(void *handle,long offset) -{ - memfile_seek((memfile_t *)handle, offset); -} - -int pc_writebin(void *handle,void *buffer,int size) -{ - return memfile_write((memfile_t *)handle, buffer, size); -} - -long pc_lengthbin(void *handle) -{ - return memfile_tell((memfile_t *)handle); -} diff --git a/compiler-init/compiler-init/libpawnc.rc b/compiler-init/compiler-init/libpawnc.rc deleted file mode 100644 index df89015a..00000000 --- a/compiler-init/compiler-init/libpawnc.rc +++ /dev/null @@ -1,55 +0,0 @@ -#include -#if defined WIN32 || defined _WIN32 || defined __WIN32__ -# include -#else -# include -#endif -#include "svnrev.h" - -AppIcon ICON "pawn.ico" - -/* Version information - * - * All strings MUST have an explicit \0. See the Windows SDK documentation - * for details on version information and the VERSIONINFO structure. - */ -#define VERSION SMC_VERSION -#define REVISION SMC_REVISION -#define BUILD SMC_BUILD -#define VERSIONSTR SMC_VERSTRING -#define VERSIONNAME "smcomp.exe\0" -#define VERSIONDESCRIPTION "SourcePawn Compiler\0" -#define VERSIONPRODUCTNAME "smcomp\0" - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VERSION, REVISION, BUILD, 0 -PRODUCTVERSION VERSION, REVISION, BUILD, 0 -FILEFLAGSMASK 0x0000003FL -FILEFLAGS 0 -#if defined(WIN32) - FILEOS VOS__WINDOWS32 -#else - FILEOS VOS__WINDOWS16 -#endif -FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904E4" - BEGIN - VALUE "CompanyName", "(C)1998-2006 ITB CompuPhase, AlliedModders LLC\0" - VALUE "FileDescription", VERSIONDESCRIPTION - VALUE "FileVersion", VERSIONSTR - VALUE "InternalName", VERSIONNAME - VALUE "LegalCopyright", "(C)1998-2006 ITB CompuPhase, AlliedModders LLC\0" - VALUE "OriginalFilename", VERSIONNAME - VALUE "ProductName", VERSIONPRODUCTNAME - VALUE "ProductVersion", VERSIONSTR - END - END - - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END diff --git a/compiler-init/compiler-init/lstring.c b/compiler-init/compiler-init/lstring.c deleted file mode 100644 index f5156b32..00000000 --- a/compiler-init/compiler-init/lstring.c +++ /dev/null @@ -1,124 +0,0 @@ -/* Safe string copying and concatenation - * These routines are originally distributed in two separate files. I have - * copied the files verbatim in this single source file, including all comments. - * The only change is that the second set of include files is commented out - * (there is no need to include the same files twice). - */ - -#include "lstring.h" - -#if !defined HAVE_SAFESTR - -/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - #include already included through lstring.h - */ -#include /* for strlen() */ - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -strlcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} - -/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - #include already included - #include already included -*/ - -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -size_t -strlcat(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -} - -#endif /* #if !defined HAVE_SAFESTR */ diff --git a/compiler-init/compiler-init/lstring.h b/compiler-init/compiler-init/lstring.h deleted file mode 100644 index ca62cb9a..00000000 --- a/compiler-init/compiler-init/lstring.h +++ /dev/null @@ -1,18 +0,0 @@ -/* prototypes for strlcpy() and strlcat() */ - -#include - -#if defined __WATCOMC__ && __WATCOMC__ >= 1240 - /* OpenWatcom introduced BSD "safe string functions" with version 1.4 */ - #define HAVE_SAFESTR -#endif - -#if !defined HAVE_SAFESTR - -size_t -strlcpy(char *dst, const char *src, size_t siz); - -size_t -strlcat(char *dst, const char *src, size_t siz); - -#endif diff --git a/compiler-init/compiler-init/memfile.c b/compiler-init/compiler-init/memfile.c deleted file mode 100644 index 2047f710..00000000 --- a/compiler-init/compiler-init/memfile.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "memfile.h" -#include -#include "osdefs.h" - -memfile_t *memfile_creat(const char *name, size_t init) -{ - memfile_t mf; - memfile_t *pmf; - - mf.size = init; - mf.base = (char *)malloc(init); - mf.usedoffs = 0; - if (!mf.base) - { - return NULL; - } - - mf.offs = 0; - mf._static = 0; - - pmf = (memfile_t *)malloc(sizeof(memfile_t)); - memcpy(pmf, &mf, sizeof(memfile_t)); - - pmf->name = strdup(name); - - return pmf; -} - -void memfile_destroy(memfile_t *mf) -{ - if (!mf->_static) - { - free(mf->name); - free(mf->base); - free(mf); - } -} - -void memfile_seek(memfile_t *mf, long seek) -{ - mf->offs = seek; -} - -long memfile_tell(memfile_t *mf) -{ - return mf->offs; -} - -size_t memfile_read(memfile_t *mf, void *buffer, size_t maxsize) -{ - if (!maxsize || mf->offs >= mf->usedoffs) - { - return 0; - } - - if (mf->usedoffs - mf->offs < (long)maxsize) - { - maxsize = mf->usedoffs - mf->offs; - if (!maxsize) - { - return 0; - } - } - - memcpy(buffer, mf->base + mf->offs, maxsize); - - mf->offs += maxsize; - - return maxsize; -} - -int memfile_write(memfile_t *mf, void *buffer, size_t size) -{ - if (mf->offs + size > mf->size) - { - size_t newsize = (mf->size + size) * 2; - if (mf->_static) - { - char *oldbase = mf->base; - mf->base = (char *)malloc(newsize); - if (!mf->base) - { - return 0; - } - memcpy(mf->base, oldbase, mf->size); - } else { - mf->base = (char *)realloc(mf->base, newsize); - if (!mf->base) - { - return 0; - } - } - mf->_static = 0; - mf->size = newsize; - } - memcpy(mf->base + mf->offs, buffer, size); - mf->offs += size; - - if (mf->offs > mf->usedoffs) - { - mf->usedoffs = mf->offs; - } - - return 1; -} diff --git a/compiler-init/compiler-init/memfile.h b/compiler-init/compiler-init/memfile.h deleted file mode 100644 index f5222a9e..00000000 --- a/compiler-init/compiler-init/memfile.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _INCLUDE_MEMFILE_H -#define _INCLUDE_MEMFILE_H - -#include - -typedef struct memfile_s -{ - char *name; - char *base; - long offs; - long usedoffs; - size_t size; - int _static; -} memfile_t; - -memfile_t *memfile_creat(const char *name, size_t init); -void memfile_destroy(memfile_t *mf); -void memfile_seek(memfile_t *mf, long seek); -int memfile_write(memfile_t *mf, void *buffer, size_t size); -size_t memfile_read(memfile_t *mf, void *buffer, size_t maxsize); -long memfile_tell(memfile_t *mf); - -#endif //_INCLUDE_MEMFILE_H diff --git a/compiler-init/compiler-init/osdefs.h b/compiler-init/compiler-init/osdefs.h deleted file mode 100644 index 299af953..00000000 --- a/compiler-init/compiler-init/osdefs.h +++ /dev/null @@ -1,108 +0,0 @@ -/* __MSDOS__ set when compiling for DOS (not Windows) - * _Windows set when compiling for any version of Microsoft Windows - * __WIN32__ set when compiling for Windows95 or WindowsNT (32 bit mode) - * __32BIT__ set when compiling in 32-bit "flat" mode (DOS or Windows) - * - * Copyright 1998-2005, ITB CompuPhase, The Netherlands. - * info@compuphase.com. - */ - -#ifndef _OSDEFS_H -#define _OSDEFS_H - -/* Every compiler uses different "default" macros to indicate the mode - * it is in. Throughout the source, we use the Borland C++ macros, so - * the macros of Watcom C/C++ and Microsoft Visual C/C++ are mapped to - * those of Borland C++. - */ -#if defined(__WATCOMC__) -# if defined(__WINDOWS__) || defined(__NT__) -# define _Windows 1 -# endif -# ifdef __386__ -# define __32BIT__ 1 -# endif -# if defined(_Windows) && defined(__32BIT__) -# define __WIN32__ 1 -# endif -#elif defined(_MSC_VER) -# if defined(_WINDOWS) || defined(_WIN32) -# define _Windows 1 -# endif -# ifdef _WIN32 -# define __WIN32__ 1 -# define __32BIT__ 1 -# endif -# if _MSC_VER >= 1400 -# if !defined _CRT_SECURE_NO_DEPRECATE -# define _CRT_SECURE_NO_DEPRECATE -# endif -# define strdup _strdup -# define stricmp _stricmp -# define access _access -# define chdir _chdir -# define strdup _strdup -# endif -#endif - -#if defined __FreeBSD__ - #include -#elif defined LINUX - #include -#endif - -/* Linux NOW has these */ -#if !defined BIG_ENDIAN - #define BIG_ENDIAN 4321 -#endif -#if !defined LITTLE_ENDIAN - #define LITTLE_ENDIAN 1234 -#endif - -/* educated guess, BYTE_ORDER is undefined, i386 is common => little endian */ -#if !defined BYTE_ORDER - #if defined UCLINUX - #define BYTE_ORDER BIG_ENDIAN - #else - #define BYTE_ORDER LITTLE_ENDIAN - #endif -#endif - -#if defined __MSDOS__ || defined __WIN32__ || defined _Windows - #define DIRSEP_CHAR '\\' -#elif defined macintosh - #define DIRSEP_CHAR ':' -#else - #define DIRSEP_CHAR '/' /* directory separator character */ -#endif - -/* _MAX_PATH is sometimes called differently and it may be in limits.h or - * stdlib.h instead of stdio.h. - */ -#if !defined _MAX_PATH - /* not defined, perhaps stdio.h was not included */ - #if !defined PATH_MAX - #include - #endif - #if !defined _MAX_PATH && !defined PATH_MAX - /* no _MAX_PATH and no MAX_PATH, perhaps it is in limits.h */ - #include - #endif - #if !defined _MAX_PATH && !defined PATH_MAX - /* no _MAX_PATH and no MAX_PATH, perhaps it is in stdlib.h */ - #include - #endif - /* if _MAX_PATH is undefined, try common alternative names */ - #if !defined _MAX_PATH - #if defined MAX_PATH - #define _MAX_PATH MAX_PATH - #elif defined _POSIX_PATH_MAX - #define _MAX_PATH _POSIX_PATH_MAX - #else - /* everything failed, actually we have a problem here... */ - #define _MAX_PATH 1024 - #endif - #endif -#endif - -#endif /* _OSDEFS_H */ diff --git a/compiler-init/compiler-init/pawn.ico b/compiler-init/compiler-init/pawn.ico deleted file mode 100644 index 7a6ab60bc5be72862ee6fee5e3bd4585a6644f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8478 zcmeI1J!~6C7RN`X0-1jOasId_?ko&&fT6+xS*394Ykk$=3c7X~x^Y9`0H!do zU5Z1HN;p`{=BPq$&)d5&1?24JqK^}mD=Z*^=YId$U22!&QoJ_M$p4+M_ujmDGn|?C zY|N7R#N4}QLi%CNm_Jc!G}8KCJ~8GaZH=tq%Ozvn7slMWmG=K|)tJAotDZ9a`-(At z{Jk+>(k+pH21^DarFxo8f4gen)6ELqtg^eiYj#Jw=KFt+&GPcHxq9`gSzTQ<&1TbF zzkc1^xN*bWxpT+dy?fU@di2OVeE86`S}k+`{(bZ8*)#J@a`{jUh0sF9Qga{z2~dLt z4n!aU9*D>S2O^LF55#DJ0})7o2NGt10})7oSKU(w34kK}Ild7{fCo~*0tX_H01u>; z1r9_Y0Uk(E3mk|*0z8oN7B~=r1bASXkOL7&fCmU?WEcJ-z6tO^leWNt2qeG*8Nvbw zB9H(X#sUW-kN^*4EDIu#01sq53nGvJ&xi?M88O13`b%V7S%x~4j2%Hi8^*T4iHDslm->rW7>Dp%>D(KSYTU|S<<~Y9H>4gMKU}!g6jn!&~>nXLqDM!1_ar9Z`03QPV7AbH@CFu7n=~qPG zkK?%Cha>0&{b?orIHiUm4h%tjYmSLh`eA(tLnozfwJXIN6HA%VXaI4mi7HCm4b@4i z^jsj#=-buiL`>XIiCay`1IIjdj5&e4t)(JiOE~7tR%&*7@vu%BjpBH#=i2kdn?AEm z(_2lY-|KLy%}McFC6Tgbe0rVM9BSs9r#|b;9GJ{pdQ*?Xh6tKy#!u#x(>n-5Nb~jr z)G$czAfKCcku(T9wC5I4Cdlg{f2+gRSwuQC+I1av#093ANZaZ4CB60Sq~(HwbZ^bw z_+$}bZ{9lzudfb*Gj@RaD}) z=)>_3eFeo-bDdM2Y4qZ$_UdYVI2cs;KHmR$jdzguAN3?6u|f}&kBzyxNS@Q@`L7v2 z`8VRv^IU#jpU(4s->r$w``wjtp6QwEJITj4Q@Ta-TyE7kdAnIk*F{Bwv22Jl9)b;-@ZAnUT6`<8$e!;x%6SscZ)Mi1Hip8p-9gUHYl`lD||+PI-Gz2fnEdQ#u9TLbBOHPM`jHeB)h0XL0$+j2eY8wvM(V1Kk8GoY{#3b)o3awbNH622m-o?cKW=)ma&+)y zfB)d{c&uY!Ivsw!zV6(5yVYp44}PBRHK%EaQ^7ZE`8` zmYZptyuXj9Ubx2d=1Kkm>&-BIslQTtsQQE&=E(1FDIRowZ{+gB(pSgR#@ZPy{;buL zh$o@Da)!qWp9GTGQ#cB-cu~xbSFyvjzmYL}iUaN75HFq0KaPnX5N;?x#qzn64@dtZ ze!(b(wo;Mk^C;fxm)c5|z52QML2%EzxX5yMi;(*~4Y}NMd*z}Fa($;eUDWkZ0-2AM8fMoHg+Orq*V!GLeM( zMk9Og&8iQ#Z8LqniN3aJH}u+?B&!EvZBng$jzmVxQ*CoN+}_q}FHNdLkyjaT6Zw_BT&6^Jd!x`T4bQX{MQx*?jp~gvZ9@Owr|Jc5XZ16`(s)ewY&tN|S*%NVaRAGI zxSyE|b+ -#include -#include -#if defined __BORLANDC__ && defined _Windows && !(defined __32BIT__ || defined __WIN32__) - /* setjmp() and longjmp() not well supported in 16-bit windows */ - #include - typedef int jmp_buf[9]; - #define setjmp(b) Catch(b) - #define longjmp(b,e) Throw(b,e) -#else - #include -#endif -#include "osdefs.h" -#include "amx.h" - -/* Note: the "cell" and "ucell" types are defined in AMX.H */ - -#define PUBLIC_CHAR '@' /* character that defines a function "public" */ -#define CTRL_CHAR '\\' /* default control character */ -#define sCHARBITS 8 /* size of a packed character */ - -#define sDIMEN_MAX 4 /* maximum number of array dimensions */ -#define sLINEMAX 1024 /* input line length (in characters) */ -#define sCOMP_STACK 32 /* maximum nesting of #if .. #endif sections */ -#define sDEF_LITMAX 500 /* initial size of the literal pool, in "cells" */ -#define sDEF_AMXSTACK 4096 /* default stack size for AMX files */ -#define PREPROC_TERM '\x7f'/* termination character for preprocessor expressions (the "DEL" code) */ -#define sDEF_PREFIX "sourcemod.inc" /* default prefix filename */ - -typedef union { - void *pv; /* e.g. a name */ - int i; -} stkitem; /* type of items stored on the compiler stack */ - -typedef struct s_arginfo { /* function argument info */ - char name[sNAMEMAX+1]; - char ident; /* iVARIABLE, iREFERENCE, iREFARRAY or iVARARGS */ - char usage; /* uCONST */ - int *tags; /* argument tag id. list */ - int numtags; /* number of tags in the tag list */ - int dim[sDIMEN_MAX]; - int idxtag[sDIMEN_MAX]; - int numdim; /* number of dimensions */ - unsigned char hasdefault; /* bit0: is there a default value? bit6: "tagof"; bit7: "sizeof" */ - union { - cell val; /* default value */ - struct { - char *symname; /* name of another symbol */ - short level; /* indirection level for that symbol */ - } size; /* used for "sizeof" default value */ - struct { - cell *data; /* values of default array */ - int size; /* complete length of default array */ - int arraysize; /* size to reserve on the heap */ - cell addr; /* address of the default array in the data segment */ - } array; - } defvalue; /* default value, or pointer to default array */ - int defvalue_tag; /* tag of the default value */ -} arginfo; - -/* Equate table, tagname table, library table */ -typedef struct s_constvalue { - struct s_constvalue *next; - char name[sNAMEMAX+1]; - cell value; - int index; /* index level, for constants referring to array sizes/tags - * automaton id. for states and automatons - * tag for enumeration lists */ -} constvalue; - -/* Symbol table format - * - * The symbol name read from the input file is stored in "name", the - * value of "addr" is written to the output file. The address in "addr" - * depends on the class of the symbol: - * global offset into the data segment - * local offset relative to the stack frame - * label generated hexadecimal number - * function offset into code segment - */ -typedef struct s_symbol { - struct s_symbol *next; - struct s_symbol *parent; /* hierarchical types (multi-dimensional arrays) */ - char name[sNAMEMAX+1]; - uint32_t hash; /* value derived from name, for quicker searching */ - cell addr; /* address or offset (or value for constant, index for native function) */ - cell codeaddr; /* address (in the code segment) where the symbol declaration starts */ - char vclass; /* sLOCAL if "addr" refers to a local symbol */ - char ident; /* see below for possible values */ - short usage; /* see below for possible values */ - char flags; /* see below for possible values */ - int compound; /* compound level (braces nesting level) */ - int tag; /* tagname id */ - union { - int declared; /* label: how many local variables are declared */ - struct { - int index; /* array & enum: tag of array indices or the enum item */ - int field; /* enumeration fields, where a size is attached to the field */ - } tags; /* extra tags */ - constvalue *lib; /* native function: library it is part of */ - long stacksize; /* normal/public function: stack requirements */ - } x; /* 'x' for 'extra' */ - union { - arginfo *arglist; /* types of all parameters for functions */ - constvalue *enumlist;/* list of names for the "root" of an enumeration */ - struct { - cell length; /* arrays: length (size) */ - short level; /* number of dimensions below this level */ - } array; - } dim; /* for 'dimension', both functions and arrays */ - constvalue *states; /* list of state function/state variable ids + addresses */ - int fnumber; /* static global variables: file number in which the declaration is visible */ - int lnumber; /* line number (in the current source file) for the declaration */ - struct s_symbol **refer; /* referrer list, functions that "use" this symbol */ - int numrefers; /* number of entries in the referrer list */ - char *documentation; /* optional documentation string */ -} symbol; - - -/* Possible entries for "ident". These are used in the "symbol", "value" - * and arginfo structures. Not every constant is valid for every use. - * In an argument list, the list is terminated with a "zero" ident; labels - * cannot be passed as function arguments, so the value 0 is overloaded. - */ -#define iLABEL 0 -#define iVARIABLE 1 /* cell that has an address and that can be fetched directly (lvalue) */ -#define iREFERENCE 2 /* iVARIABLE, but must be dereferenced */ -#define iARRAY 3 -#define iREFARRAY 4 /* an array passed by reference (i.e. a pointer) */ -#define iARRAYCELL 5 /* array element, cell that must be fetched indirectly */ -#define iARRAYCHAR 6 /* array element, character from cell from array */ -#define iEXPRESSION 7 /* expression result, has no address (rvalue) */ -#define iCONSTEXPR 8 /* constant expression (or constant symbol) */ -#define iFUNCTN 9 -#define iREFFUNC 10 -#define iVARARGS 11 /* function specified ... as argument(s) */ - -/* Possible entries for "usage" - * - * This byte is used as a serie of bits, the syntax is different for - * functions and other symbols: - * - * VARIABLE - * bits: 0 (uDEFINE) the variable is defined in the source file - * 1 (uREAD) the variable is "read" (accessed) in the source file - * 2 (uWRITTEN) the variable is altered (assigned a value) - * 3 (uCONST) the variable is constant (may not be assigned to) - * 4 (uPUBLIC) the variable is public - * 6 (uSTOCK) the variable is discardable (without warning) - * - * FUNCTION - * bits: 0 (uDEFINE) the function is defined ("implemented") in the source file - * 1 (uREAD) the function is invoked in the source file - * 2 (uRETVALUE) the function returns a value (or should return a value) - * 3 (uPROTOTYPED) the function was prototyped (implicitly via a definition or explicitly) - * 4 (uPUBLIC) the function is public - * 5 (uNATIVE) the function is native - * 6 (uSTOCK) the function is discardable (without warning) - * 7 (uMISSING) the function is not implemented in this source file - * 8 (uFORWARD) the function is explicitly forwardly declared - * - * CONSTANT - * bits: 0 (uDEFINE) the symbol is defined in the source file - * 1 (uREAD) the constant is "read" (accessed) in the source file - * 2 (uWRITTEN) redundant, but may be set for constants passed by reference - * 3 (uPREDEF) the constant is pre-defined and should be kept between passes - * 5 (uENUMROOT) the constant is the "root" of an enumeration - * 6 (uENUMFIELD) the constant is a field in a named enumeration - */ -#define uDEFINE 0x001 -#define uREAD 0x002 -#define uWRITTEN 0x004 -#define uRETVALUE 0x004 /* function returns (or should return) a value */ -#define uCONST 0x008 -#define uPROTOTYPED 0x008 -#define uPREDEF 0x008 /* constant is pre-defined */ -#define uPUBLIC 0x010 -#define uNATIVE 0x020 -#define uENUMROOT 0x020 -#define uSTOCK 0x040 -#define uENUMFIELD 0x040 -#define uMISSING 0x080 -#define uFORWARD 0x100 -/* uRETNONE is not stored in the "usage" field of a symbol. It is - * used during parsing a function, to detect a mix of "return;" and - * "return value;" in a few special cases. - */ -#define uRETNONE 0x10 - -#define flgDEPRICATED 0x01 /* symbol is depricated (avoid use) */ - -#define uTAGOF 0x40 /* set in the "hasdefault" field of the arginfo struct */ -#define uSIZEOF 0x80 /* set in the "hasdefault" field of the arginfo struct */ - -#define uMAINFUNC "main" -#define uENTRYFUNC "entry" - -#define sGLOBAL 0 /* global variable/constant class (no states) */ -#define sLOCAL 1 /* local variable/constant */ -#define sSTATIC 2 /* global life, local scope */ - -#define sSTATEVAR 3 /* criterion to find variables (sSTATEVAR implies a global variable) */ - -typedef struct s_value { - symbol *sym; /* symbol in symbol table, NULL for (constant) expression */ - cell constval; /* value of the constant expression (if ident==iCONSTEXPR) - * also used for the size of a literal array */ - int tag; /* tag (of the expression) */ - int cmptag; /* for searching symbols: choose the one with the matching tag */ - char ident; /* iCONSTEXPR, iVARIABLE, iARRAY, iARRAYCELL, - * iEXPRESSION or iREFERENCE */ - char boolresult; /* boolean result for relational operators */ - cell *arrayidx; /* last used array indices, for checking self assignment */ -} value; - -/* "while" statement queue (also used for "for" and "do - while" loops) */ -enum { - wqBRK, /* used to restore stack for "break" */ - wqCONT, /* used to restore stack for "continue" */ - wqLOOP, /* loop start label number */ - wqEXIT, /* loop exit label number (jump if false) */ - /* --- */ - wqSIZE /* "while queue" size */ -}; -#define wqTABSZ (24*wqSIZE) /* 24 nested loop statements */ - -enum { - statIDLE, /* not compiling yet */ - statFIRST, /* first pass */ - statWRITE, /* writing output */ - statSKIP, /* skipping output */ -}; - -typedef struct s_stringlist { - struct s_stringlist *next; - char *line; -} stringlist; - -typedef struct s_stringpair { - struct s_stringpair *next; - char *first; - char *second; - int matchlength; -} stringpair; - -/* macros for code generation */ -#define opcodes(n) ((n)*sizeof(cell)) /* opcode size */ -#define opargs(n) ((n)*sizeof(cell)) /* size of typical argument */ - -/* Tokens recognized by lex() - * Some of these constants are assigned as well to the variable "lastst" - */ -#define tFIRST 256 /* value of first multi-character operator */ -#define tMIDDLE 280 /* value of last multi-character operator */ -#define tLAST 326 /* value of last multi-character match-able token */ -/* multi-character operators */ -#define taMULT 256 /* *= */ -#define taDIV 257 /* /= */ -#define taMOD 258 /* %= */ -#define taADD 259 /* += */ -#define taSUB 260 /* -= */ -#define taSHL 261 /* <<= */ -#define taSHRU 262 /* >>>= */ -#define taSHR 263 /* >>= */ -#define taAND 264 /* &= */ -#define taXOR 265 /* ^= */ -#define taOR 266 /* |= */ -#define tlOR 267 /* || */ -#define tlAND 268 /* && */ -#define tlEQ 269 /* == */ -#define tlNE 270 /* != */ -#define tlLE 271 /* <= */ -#define tlGE 272 /* >= */ -#define tSHL 273 /* << */ -#define tSHRU 274 /* >>> */ -#define tSHR 275 /* >> */ -#define tINC 276 /* ++ */ -#define tDEC 277 /* -- */ -#define tELLIPS 278 /* ... */ -#define tDBLDOT 279 /* .. */ -#define tDBLCOLON 280 /* :: */ -/* reserved words (statements) */ -#define tASSERT 281 -#define tBREAK 282 -#define tCASE 283 -#define tCHAR 284 -#define tCONST 285 -#define tCONTINUE 286 -#define tDEFAULT 287 -#define tDEFINED 288 -#define tDO 289 -#define tELSE 290 -#define tENUM 291 -#define tEXIT 292 -#define tFOR 293 -#define tFORWARD 294 -#define tGOTO 295 -#define tIF 296 -#define tNATIVE 297 -#define tNEW 298 -#define tDECL 299 -#define tOPERATOR 300 -#define tPUBLIC 301 -#define tRETURN 303 -#define tSIZEOF 303 -#define tSLEEP 304 -#define tSTATE 305 -#define tSTATIC 306 -#define tSTOCK 307 -#define tSWITCH 308 -#define tTAGOF 309 -#define tWHILE 310 -/* compiler directives */ -#define tpASSERT 311 /* #assert */ -#define tpDEFINE 312 -#define tpELSE 313 /* #else */ -#define tpELSEIF 314 /* #elseif */ -#define tpEMIT 315 -#define tpENDIF 316 -#define tpENDINPUT 317 -#define tpENDSCRPT 318 -#define tpERROR 319 -#define tpFILE 320 -#define tpIF 321 /* #if */ -#define tINCLUDE 322 -#define tpLINE 323 -#define tpPRAGMA 324 -#define tpTRYINCLUDE 325 -#define tpUNDEF 326 -/* semicolon is a special case, because it can be optional */ -#define tTERM 327 /* semicolon or newline */ -#define tENDEXPR 328 /* forced end of expression */ -/* other recognized tokens */ -#define tNUMBER 329 /* integer number */ -#define tRATIONAL 330 /* rational number */ -#define tSYMBOL 331 -#define tLABEL 332 -#define tSTRING 333 -#define tEXPR 334 /* for assigment to "lastst" only */ - -/* (reversed) evaluation of staging buffer */ -#define sSTARTREORDER 0x01 -#define sENDREORDER 0x02 -#define sEXPRSTART 0x80 /* top bit set, rest is free */ -#define sMAXARGS 127 /* relates to the bit pattern of sEXPRSTART */ - -#define sDOCSEP 0x01 /* to separate documentation comments between functions */ - -/* codes for ffabort() */ -#define xEXIT 1 /* exit code in PRI */ -#define xASSERTION 2 /* abort caused by failing assertion */ -#define xSTACKERROR 3 /* stack/heap overflow */ -#define xBOUNDSERROR 4 /* array index out of bounds */ -#define xMEMACCESS 5 /* data access error */ -#define xINVINSTR 6 /* invalid instruction */ -#define xSTACKUNDERFLOW 7 /* stack underflow */ -#define xHEAPUNDERFLOW 8 /* heap underflow */ -#define xCALLBACKERR 9 /* no, or invalid, callback */ -#define xSLEEP 12 /* sleep, exit code in PRI, tag in ALT */ - -/* Miscellaneous */ -#if !defined TRUE - #define FALSE 0 - #define TRUE 1 -#endif -#define sIN_CSEG 1 /* if parsing CODE */ -#define sIN_DSEG 2 /* if parsing DATA */ -#define sCHKBOUNDS 1 /* bit position in "debug" variable: check bounds */ -#define sSYMBOLIC 2 /* bit position in "debug" variable: symbolic info */ -#define sRESET 0 /* reset error flag */ -#define sFORCESET 1 /* force error flag on */ -#define sEXPRMARK 2 /* mark start of expression */ -#define sEXPRRELEASE 3 /* mark end of expression */ -#define sSETPOS 4 /* set line number for the error */ - -enum { - sOPTIMIZE_NONE, /* no optimization */ - sOPTIMIZE_NOMACRO, /* no macro instructions */ - sOPTIMIZE_DEFAULT, /* full optimization */ - /* ----- */ - sOPTIMIZE_NUMBER -}; - -typedef enum s_regid { - sPRI, /* indicates the primary register */ - sALT, /* indicates the secundary register */ -} regid; - -typedef enum s_optmark { - sEXPR, /* end of expression (for expressions that form a statement) */ - sPARM, /* end of parameter (in a function parameter list) */ - sLDECL, /* start of local declaration (variable) */ -} optmark; - -#define suSLEEP_INSTR 0x01 /* the "sleep" instruction was used */ - -#if INT_MAX<0x8000u - #define PUBLICTAG 0x8000u - #define FIXEDTAG 0x4000u -#else - #define PUBLICTAG 0x80000000Lu - #define FIXEDTAG 0x40000000Lu -#endif -#define TAGMASK (~PUBLICTAG) -#define CELL_MAX (((ucell)1 << (sizeof(cell)*8-1)) - 1) - - -/* interface functions */ -#if defined __cplusplus - extern "C" { -#endif - -/* - * Functions you call from the "driver" program - */ -int pc_compile(int argc, char **argv); -int pc_addconstant(char *name,cell value,int tag); -int pc_addtag(char *name); -int pc_enablewarning(int number,int enable); - -/* - * Functions called from the compiler (to be implemented by you) - */ - -/* general console output */ -int pc_printf(const char *message,...); - -/* error report function */ -int pc_error(int number,char *message,char *filename,int firstline,int lastline,va_list argptr); - -/* input from source file */ -void *pc_opensrc(char *filename); /* reading only */ -void *pc_createsrc(char *filename); -void pc_closesrc(void *handle); /* never delete */ -void pc_resetsrc(void *handle,void *position); /* reset to a position marked earlier */ -char *pc_readsrc(void *handle,unsigned char *target,int maxchars); -int pc_writesrc(void *handle,unsigned char *source); -void *pc_getpossrc(void *handle); /* mark the current position */ -int pc_eofsrc(void *handle); - -/* output to intermediate (.ASM) file */ -void *pc_openasm(char *filename); /* read/write */ -void pc_closeasm(void *handle,int deletefile); -void pc_resetasm(void *handle); -int pc_writeasm(void *handle,char *str); -char *pc_readasm(void *handle,char *target,int maxchars); - -/* output to binary (.AMX) file */ -void *pc_openbin(char *filename); -void pc_closebin(void *handle,int deletefile); -void pc_resetbin(void *handle,long offset); -int pc_writebin(void *handle,void *buffer,int size); -long pc_lengthbin(void *handle); /* return the length of the file */ - -#if defined __cplusplus - } -#endif - - -/* by default, functions and variables used in throughout the compiler - * files are "external" - */ -#if !defined SC_FUNC - #define SC_FUNC -#endif -#if !defined SC_VDECL - #define SC_VDECL extern -#endif -#if !defined SC_VDEFINE - #define SC_VDEFINE -#endif - -/* function prototypes in SC1.C */ -SC_FUNC void set_extension(char *filename,char *extension,int force); -SC_FUNC symbol *fetchfunc(char *name,int tag); -SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag); -SC_FUNC char *funcdisplayname(char *dest,char *funcname); -SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr); -SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index); -SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index); -SC_FUNC void delete_consttable(constvalue *table); -SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag); -SC_FUNC void exporttag(int tag); -SC_FUNC void sc_attachdocumentation(symbol *sym); - -/* function prototypes in SC2.C */ -#define PUSHSTK_P(v) { stkitem s_; s_.pv=(v); pushstk(s_); } -#define PUSHSTK_I(v) { stkitem s_; s_.i=(v); pushstk(s_); } -#define POPSTK_P() (popstk().pv) -#define POPSTK_I() (popstk().i) -SC_FUNC void pushstk(stkitem val); -SC_FUNC stkitem popstk(void); -SC_FUNC void clearstk(void); -SC_FUNC int plungequalifiedfile(char *name); /* explicit path included */ -SC_FUNC int plungefile(char *name,int try_currentpath,int try_includepaths); /* search through "include" paths */ -SC_FUNC void preprocess(void); -SC_FUNC void lexinit(void); -SC_FUNC int lex(cell *lexvalue,char **lexsym); -SC_FUNC void lexpush(void); -SC_FUNC void lexclr(int clreol); -SC_FUNC int matchtoken(int token); -SC_FUNC int tokeninfo(cell *val,char **str); -SC_FUNC int needtoken(int token); -SC_FUNC void litadd(cell value); -SC_FUNC void litinsert(cell value,int pos); -SC_FUNC int alphanum(char c); -SC_FUNC int ishex(char c); -SC_FUNC void delete_symbol(symbol *root,symbol *sym); -SC_FUNC void delete_symbols(symbol *root,int level,int del_labels,int delete_functions); -SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom); -SC_FUNC void markusage(symbol *sym,int usage); -SC_FUNC uint32_t namehash(const char *name); -SC_FUNC symbol *findglb(const char *name,int filter); -SC_FUNC symbol *findloc(const char *name); -SC_FUNC symbol *findconst(const char *name,int *matchtag); -SC_FUNC symbol *finddepend(const symbol *parent); -SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag, - int usage); -SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag, - int dim[],int numdim,int idxtag[]); -SC_FUNC int getlabel(void); -SC_FUNC char *itoh(ucell val); - -/* function prototypes in SC3.C */ -SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, - value *lval,int *resulttag); -SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce); -SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult); -SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state); -SC_FUNC cell array_totalsize(symbol *sym); - -/* function prototypes in SC4.C */ -SC_FUNC void writeleader(symbol *root); -SC_FUNC void writetrailer(void); -SC_FUNC void begcseg(void); -SC_FUNC void begdseg(void); -SC_FUNC void setline(int chkbounds); -SC_FUNC void setfiledirect(char *name); -SC_FUNC void setlinedirect(int line); -SC_FUNC void setlabel(int index); -SC_FUNC void markexpr(optmark type,const char *name,cell offset); -SC_FUNC void startfunc(char *fname); -SC_FUNC void endfunc(void); -SC_FUNC void alignframe(int numbytes); -SC_FUNC void rvalue(value *lval); -SC_FUNC void address(symbol *ptr,regid reg); -SC_FUNC void store(value *lval); -SC_FUNC void loadreg(cell address,regid reg); -SC_FUNC void storereg(cell address,regid reg); -SC_FUNC void memcopy(cell size); -SC_FUNC void copyarray(symbol *sym,cell size); -SC_FUNC void fillarray(symbol *sym,cell size,cell value); -SC_FUNC void ldconst(cell val,regid reg); -SC_FUNC void moveto1(void); -SC_FUNC void pushreg(regid reg); -SC_FUNC void pushval(cell val); -SC_FUNC void popreg(regid reg); -SC_FUNC void swap1(void); -SC_FUNC void ffswitch(int label); -SC_FUNC void ffcase(cell value,char *labelname,int newtable); -SC_FUNC void ffcall(symbol *sym,const char *label,int numargs); -SC_FUNC void ffret(int remparams); -SC_FUNC void ffabort(int reason); -SC_FUNC void ffbounds(cell size); -SC_FUNC void jumplabel(int number); -SC_FUNC void defstorage(void); -SC_FUNC void modstk(int delta); -SC_FUNC void setstk(cell value); -SC_FUNC void modheap(int delta); -SC_FUNC void setheap_pri(void); -SC_FUNC void setheap(cell value); -SC_FUNC void cell2addr(void); -SC_FUNC void cell2addr_alt(void); -SC_FUNC void addr2cell(void); -SC_FUNC void char2addr(void); -SC_FUNC void charalign(void); -SC_FUNC void addconst(cell value); - -/* Code generation functions for arithmetic operators. - * - * Syntax: o[u|s|b]_name - * | | | +--- name of operator - * | | +----- underscore - * | +--------- "u"nsigned operator, "s"igned operator or "b"oth - * +------------- "o"perator - */ -SC_FUNC void os_mult(void); /* multiplication (signed) */ -SC_FUNC void os_div(void); /* division (signed) */ -SC_FUNC void os_mod(void); /* modulus (signed) */ -SC_FUNC void ob_add(void); /* addition */ -SC_FUNC void ob_sub(void); /* subtraction */ -SC_FUNC void ob_sal(void); /* shift left (arithmetic) */ -SC_FUNC void os_sar(void); /* shift right (arithmetic, signed) */ -SC_FUNC void ou_sar(void); /* shift right (logical, unsigned) */ -SC_FUNC void ob_or(void); /* bitwise or */ -SC_FUNC void ob_xor(void); /* bitwise xor */ -SC_FUNC void ob_and(void); /* bitwise and */ -SC_FUNC void ob_eq(void); /* equality */ -SC_FUNC void ob_ne(void); /* inequality */ -SC_FUNC void relop_prefix(void); -SC_FUNC void relop_suffix(void); -SC_FUNC void os_le(void); /* less or equal (signed) */ -SC_FUNC void os_ge(void); /* greater or equal (signed) */ -SC_FUNC void os_lt(void); /* less (signed) */ -SC_FUNC void os_gt(void); /* greater (signed) */ - -SC_FUNC void lneg(void); -SC_FUNC void neg(void); -SC_FUNC void invert(void); -SC_FUNC void nooperation(void); -SC_FUNC void inc(value *lval); -SC_FUNC void dec(value *lval); -SC_FUNC void jmp_ne0(int number); -SC_FUNC void jmp_eq0(int number); -SC_FUNC void outval(cell val,int newline); - -/* function prototypes in SC5.C */ -SC_FUNC int error(int number,...); -SC_FUNC void errorset(int code,int line); - -/* function prototypes in SC6.C */ -SC_FUNC int assemble(FILE *fout,FILE *fin); - -/* function prototypes in SC7.C */ -SC_FUNC void stgbuffer_cleanup(void); -SC_FUNC void stgmark(char mark); -SC_FUNC void stgwrite(const char *st); -SC_FUNC void stgout(int index); -SC_FUNC void stgdel(int index,cell code_index); -SC_FUNC int stgget(int *index,cell *code_index); -SC_FUNC void stgset(int onoff); -SC_FUNC int phopt_init(void); -SC_FUNC int phopt_cleanup(void); - -/* function prototypes in SCLIST.C */ -SC_FUNC char* duplicatestring(const char* sourcestring); -SC_FUNC stringpair *insert_alias(char *name,char *alias); -SC_FUNC stringpair *find_alias(char *name); -SC_FUNC int lookup_alias(char *target,char *name); -SC_FUNC void delete_aliastable(void); -SC_FUNC stringlist *insert_path(char *path); -SC_FUNC char *get_path(int index); -SC_FUNC void delete_pathtable(void); -SC_FUNC stringpair *insert_subst(char *pattern,char *substitution,int prefixlen); -SC_FUNC int get_subst(int index,char **pattern,char **substitution); -SC_FUNC stringpair *find_subst(char *name,int length); -SC_FUNC int delete_subst(char *name,int length); -SC_FUNC void delete_substtable(void); -SC_FUNC stringlist *insert_sourcefile(char *string); -SC_FUNC char *get_sourcefile(int index); -SC_FUNC void delete_sourcefiletable(void); -SC_FUNC stringlist *insert_docstring(char *string); -SC_FUNC char *get_docstring(int index); -SC_FUNC void delete_docstring(int index); -SC_FUNC void delete_docstringtable(void); -SC_FUNC stringlist *insert_autolist(char *string); -SC_FUNC char *get_autolist(int index); -SC_FUNC void delete_autolisttable(void); -SC_FUNC stringlist *insert_dbgfile(const char *filename); -SC_FUNC stringlist *insert_dbgline(int linenr); -SC_FUNC stringlist *insert_dbgsymbol(symbol *sym); -SC_FUNC char *get_dbgstring(int index); -SC_FUNC void delete_dbgstringtable(void); - -/* function prototypes in SCMEMFILE.C */ -#if !defined tMEMFILE - typedef unsigned char MEMFILE; - #define tMEMFILE 1 -#endif -MEMFILE *mfcreate(char *filename); -void mfclose(MEMFILE *mf); -int mfdump(MEMFILE *mf); -long mflength(MEMFILE *mf); -long mfseek(MEMFILE *mf,long offset,int whence); -unsigned int mfwrite(MEMFILE *mf,unsigned char *buffer,unsigned int size); -unsigned int mfread(MEMFILE *mf,unsigned char *buffer,unsigned int size); -char *mfgets(MEMFILE *mf,char *string,unsigned int size); -int mfputs(MEMFILE *mf,char *string); - -/* function prototypes in SCI18N.C */ -#define MAXCODEPAGE 12 -SC_FUNC int cp_path(const char *root,const char *directory); -SC_FUNC int cp_set(const char *name); -SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr); -SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr); -SC_FUNC int scan_utf8(FILE *fp,const char *filename); - -/* function prototypes in SCSTATE.C */ -SC_FUNC constvalue *automaton_add(const char *name); -SC_FUNC constvalue *automaton_find(const char *name); -SC_FUNC constvalue *automaton_findid(int id); -SC_FUNC constvalue *state_add(const char *name,int fsa_id); -SC_FUNC constvalue *state_find(const char *name,int fsa_id); -SC_FUNC constvalue *state_findid(int id); -SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid); -SC_FUNC int state_addlist(int *list,int count,int fsa_id); -SC_FUNC void state_deletetable(void); -SC_FUNC int state_getfsa(int listid); -SC_FUNC int state_count(int listid); -SC_FUNC int state_inlist(int listid,int state); -SC_FUNC int state_listitem(int listid,int index); -SC_FUNC void state_conflict(symbol *root); -SC_FUNC int state_conflict_id(int listid1,int listid2); - -/* external variables (defined in scvars.c) */ -#if !defined SC_SKIP_VDECL -SC_VDECL symbol loctab; /* local symbol table */ -SC_VDECL symbol glbtab; /* global symbol table */ -SC_VDECL cell *litq; /* the literal queue */ -SC_VDECL unsigned char pline[]; /* the line read from the input file */ -SC_VDECL const unsigned char *lptr;/* points to the current position in "pline" */ -SC_VDECL constvalue tagname_tab;/* tagname table */ -SC_VDECL constvalue libname_tab;/* library table (#pragma library "..." syntax) */ -SC_VDECL constvalue *curlibrary;/* current library */ -SC_VDECL int pc_addlibtable; /* is the library table added to the AMX file? */ -SC_VDECL symbol *curfunc; /* pointer to current function */ -SC_VDECL char *inpfname; /* name of the file currently read from */ -SC_VDECL char outfname[]; /* intermediate (assembler) file name */ -SC_VDECL char binfname[]; /* binary file name */ -SC_VDECL char errfname[]; /* error file name */ -SC_VDECL char sc_ctrlchar; /* the control character (or escape character) */ -SC_VDECL char sc_ctrlchar_org;/* the default control character */ -SC_VDECL int litidx; /* index to literal table */ -SC_VDECL int litmax; /* current size of the literal table */ -SC_VDECL int stgidx; /* index to the staging buffer */ -SC_VDECL int sc_labnum; /* number of (internal) labels */ -SC_VDECL int staging; /* true if staging output */ -SC_VDECL cell declared; /* number of local cells declared */ -SC_VDECL cell glb_declared; /* number of global cells declared */ -SC_VDECL cell code_idx; /* number of bytes with generated code */ -SC_VDECL int ntv_funcid; /* incremental number of native function */ -SC_VDECL int errnum; /* number of errors */ -SC_VDECL int warnnum; /* number of warnings */ -SC_VDECL int sc_debug; /* debug/optimization options (bit field) */ -SC_VDECL int sc_packstr; /* strings are packed by default? */ -SC_VDECL int sc_asmfile; /* create .ASM file? */ -SC_VDECL int sc_listing; /* create .LST file? */ -SC_VDECL int sc_compress; /* compress bytecode? */ -SC_VDECL int sc_needsemicolon;/* semicolon required to terminate expressions? */ -SC_VDECL int sc_dataalign; /* data alignment value */ -SC_VDECL int sc_alignnext; /* must frame of the next function be aligned? */ -SC_VDECL int pc_docexpr; /* must expression be attached to documentation comment? */ -SC_VDECL int curseg; /* 1 if currently parsing CODE, 2 if parsing DATA */ -SC_VDECL cell pc_stksize; /* stack size */ -SC_VDECL cell pc_amxlimit; /* abstract machine size limit (code + data, or only code) */ -SC_VDECL cell pc_amxram; /* abstract machine data size limit */ -SC_VDECL int freading; /* is there an input file ready for reading? */ -SC_VDECL int fline; /* the line number in the current file */ -SC_VDECL short fnumber; /* number of files in the file table (debugging) */ -SC_VDECL short fcurrent; /* current file being processed (debugging) */ -SC_VDECL short sc_intest; /* true if inside a test */ -SC_VDECL int sideeffect; /* true if an expression causes a side-effect */ -SC_VDECL int stmtindent; /* current indent of the statement */ -SC_VDECL int indent_nowarn; /* skip warning "217 loose indentation" */ -SC_VDECL int sc_tabsize; /* number of spaces that a TAB represents */ -SC_VDECL short sc_allowtags; /* allow/detect tagnames in lex() */ -SC_VDECL int sc_status; /* read/write status */ -SC_VDECL int sc_rationaltag; /* tag for rational numbers */ -SC_VDECL int rational_digits; /* number of fractional digits */ -SC_VDECL int sc_allowproccall;/* allow/detect tagnames in lex() */ -SC_VDECL short sc_is_utf8; /* is this source file in UTF-8 encoding */ -SC_VDECL char *pc_depricate; /* if non-NULL, mark next declaration as depricated */ -SC_VDECL int sc_curstates; /* ID of the current state list */ -SC_VDECL int pc_optimize; /* (peephole) optimization level */ -SC_VDECL int pc_memflags; /* special flags for the stack/heap usage */ - -SC_VDECL constvalue sc_automaton_tab; /* automaton table */ -SC_VDECL constvalue sc_state_tab; /* state table */ - -SC_VDECL FILE *inpf; /* file read from (source or include) */ -SC_VDECL FILE *inpf_org; /* main source file */ -SC_VDECL FILE *outf; /* file written to */ - -SC_VDECL jmp_buf errbuf; /* target of longjmp() on a fatal error */ - -#if !defined SC_LIGHT - SC_VDECL int sc_makereport; /* generate a cross-reference report */ -#endif - -#endif /* SC_SKIP_VDECL */ - -#endif /* SC_H_INCLUDED */ diff --git a/compiler-init/compiler-init/sc1.c b/compiler-init/compiler-init/sc1.c deleted file mode 100644 index 64f44b92..00000000 --- a/compiler-init/compiler-init/sc1.c +++ /dev/null @@ -1,5605 +0,0 @@ -/* Pawn compiler - * - * Function and variable definition and declaration, statement parser. - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc1.c 3591 2006-06-25 14:30:07Z thiadmer $ - */ -#include -#include -#include -#include -#include -#include -#include - -#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ - #include - #include -#endif - -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ - #include - #include /* from BinReloc, see www.autopackage.org */ -#endif - -#if defined FORTIFY - #include -#endif - -#if defined __BORLANDC__ || defined __WATCOMC__ - #include - static unsigned total_drives; /* dummy variable */ - #define dos_setdrive(i) _dos_setdrive(i,&total_drives) -#elif defined _MSC_VER && defined _WIN32 - #include /* for _chdrive() */ - #define dos_setdrive(i) _chdrive(i) -#endif -#if defined __BORLANDC__ - #include /* for chdir() */ -#elif defined __WATCOMC__ - #include /* for chdir() */ -#endif -#if defined __WIN32__ || defined _WIN32 || defined _Windows - #include -#endif - -#include "lstring.h" -#include "sc.h" -#include "svnrev.h" -#define VERSION_STR "3.2." SVN_REVSTR -#define VERSION_INT 0x0302 - -static void resetglobals(void); -static void initglobals(void); -static char *get_extension(char *filename); -static void setopt(int argc,char **argv,char *oname,char *ename,char *pname, - char *rname,char *codepage); -static void setconfig(char *root); -static void setcaption(void); -static void about(void); -static void setconstants(void); -static void parse(void); -static void dumplits(void); -static void dumpzero(int count); -static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst); -static void declglb(char *firstname,int firsttag,int fpublic,int fstatic, - int stock,int fconst); -static int declloc(int fstatic); -static void decl_const(int table); -static void decl_enum(int table); -static cell needsub(int *tag,constvalue **enumroot); -static void initials(int ident,int tag,cell *size,int dim[],int numdim, - constvalue *enumroot); -static cell initarray(int ident,int tag,int dim[],int numdim,int cur, - int startlit,int counteddim[],constvalue *lastdim, - constvalue *enumroot,int *errorfound); -static cell initvector(int ident,int tag,cell size,int fillzero, - constvalue *enumroot,int *errorfound); -static cell init(int ident,int *tag,int *errorfound); -static int getstates(const char *funcname); -static void attachstatelist(symbol *sym, int state_id); -static void funcstub(int fnative); -static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock); -static int declargs(symbol *sym,int chkshadow); -static void doarg(char *name,int ident,int offset,int tags[],int numtags, - int fpublic,int fconst,int chkshadow,arginfo *arg); -static void make_report(symbol *root,FILE *log,char *sourcefile); -static void reduce_referrers(symbol *root); -static long max_stacksize(symbol *root,int *recursion); -static int testsymbols(symbol *root,int level,int testlabs,int testconst); -static void destructsymbols(symbol *root,int level); -static constvalue *find_constval_byval(constvalue *table,cell val); -static void statement(int *lastindent,int allow_decl); -static void compound(int stmt_sameline); -static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr, - int *tag,symbol **symptr,int chkfuncresult); -static void doassert(void); -static void doexit(void); -static void test(int label,int parens,int invert); -static void doif(void); -static void dowhile(void); -static void dodo(void); -static void dofor(void); -static void doswitch(void); -static void dogoto(void); -static void dolabel(void); -static symbol *fetchlab(char *name); -static void doreturn(void); -static void dobreak(void); -static void docont(void); -static void dosleep(void); -static void dostate(void); -static void addwhile(int *ptr); -static void delwhile(void); -static int *readwhile(void); - -static int norun = 0; /* the compiler never ran */ -static int lastst = 0; /* last executed statement type */ -static int nestlevel = 0; /* number of active (open) compound statements */ -static int rettype = 0; /* the type that a "return" expression should have */ -static int skipinput = 0; /* number of lines to skip from the first input file */ -static int optproccall = TRUE; /* support "procedure call" */ -static int verbosity = 1; /* verbosity level, 0=quiet, 1=normal, 2=verbose */ -static int sc_reparse = 0; /* needs 3th parse because of changed prototypes? */ -static int sc_parsenum = 0; /* number of the extra parses */ -static int wq[wqTABSZ]; /* "while queue", internal stack for nested loops */ -static int *wqptr; /* pointer to next entry */ -#if !defined SC_LIGHT - static char sc_rootpath[_MAX_PATH]; - static char *sc_documentation=NULL;/* main documentation */ -#endif -#if defined __WIN32__ || defined _WIN32 || defined _Windows - static HWND hwndFinish = 0; -#endif - -/* "main" of the compiler - */ -#if defined __cplusplus - extern "C" -#endif -int pc_compile(int argc, char *argv[]) -{ - int entry,i,jmpcode; - int retcode; - char incfname[_MAX_PATH]; - char reportname[_MAX_PATH]; - char codepage[MAXCODEPAGE+1]; - FILE *binf; - void *inpfmark; - int lcl_packstr,lcl_needsemicolon,lcl_tabsize; - #if !defined SC_LIGHT - int hdrsize=0; - #endif - char *ptr; - - /* set global variables to their initial value */ - binf=NULL; - initglobals(); - errorset(sRESET,0); - errorset(sEXPRRELEASE,0); - lexinit(); - - /* make sure that we clean up on a fatal error; do this before the first - * call to error(). */ - if ((jmpcode=setjmp(errbuf))!=0) - goto cleanup; - - /* allocate memory for fixed tables */ - inpfname=(char*)malloc(_MAX_PATH); - if (inpfname==NULL) - error(103); /* insufficient memory */ - litq=(cell*)malloc(litmax*sizeof(cell)); - if (litq==NULL) - error(103); /* insufficient memory */ - if (!phopt_init()) - error(103); /* insufficient memory */ - - setopt(argc,argv,outfname,errfname,incfname,reportname,codepage); - strcpy(binfname,outfname); - ptr=get_extension(binfname); - if (ptr!=NULL && stricmp(ptr,".asm")==0) - set_extension(binfname,".smx",TRUE); - else - set_extension(binfname,".smx",FALSE); - /* set output names that depend on the input name */ - if (sc_listing) - set_extension(outfname,".lst",TRUE); - else - set_extension(outfname,".asm",TRUE); - if (strlen(errfname)!=0) - remove(errfname); /* delete file on startup */ - else if (verbosity>0) - setcaption(); - setconfig(argv[0]); /* the path to the include and codepage files */ - sc_ctrlchar_org=sc_ctrlchar; - lcl_packstr=sc_packstr; - lcl_needsemicolon=sc_needsemicolon; - lcl_tabsize=sc_tabsize; - #if !defined NO_CODEPAGE - if (!cp_set(codepage)) /* set codepage */ - error(108); /* codepage mapping file not found */ - #endif - /* optionally create a temporary input file that is a collection of all - * input files - */ - assert(get_sourcefile(0)!=NULL); /* there must be at least one source file */ - if (get_sourcefile(1)!=NULL) { - /* there are at least two or more source files */ - char *tname,*sname; - FILE *ftmp,*fsrc; - int fidx; - #if defined __WIN32__ || defined _WIN32 - tname=_tempnam(NULL,"pawn"); - #elif defined __MSDOS__ || defined _Windows - tname=tempnam(NULL,"pawn"); - #elif defined(MACOS) && !defined(__MACH__) - /* tempnam is not supported for the Macintosh CFM build. */ - error(104,get_sourcefile(1)); - tname=NULL; - sname=NULL; - #else - tname=tempnam(NULL,"pawn"); - #endif - ftmp=(FILE*)pc_createsrc(tname); - for (fidx=0; (sname=get_sourcefile(fidx))!=NULL; fidx++) { - unsigned char tstring[128]; - fsrc=(FILE*)pc_opensrc(sname); - if (fsrc==NULL) { - strcpy(inpfname,sname); /* avoid invalid filename */ - error(100,sname); - } /* if */ - pc_writesrc(ftmp,(unsigned char*)"#file "); - pc_writesrc(ftmp,(unsigned char*)sname); - pc_writesrc(ftmp,(unsigned char*)"\n"); - while (!pc_eofsrc(fsrc)) { - pc_readsrc(fsrc,tstring,sizeof tstring); - pc_writesrc(ftmp,tstring); - } /* while */ - pc_closesrc(fsrc); - } /* for */ - pc_closesrc(ftmp); - strcpy(inpfname,tname); - free(tname); - } else { - strcpy(inpfname,get_sourcefile(0)); - } /* if */ - inpf_org=(FILE*)pc_opensrc(inpfname); - if (inpf_org==NULL) - error(100,inpfname); - freading=TRUE; - outf=(FILE*)pc_openasm(outfname); /* first write to assembler file (may be temporary) */ - if (outf==NULL) - error(101,outfname); - /* immediately open the binary file, for other programs to check */ - if (sc_asmfile || sc_listing) { - binf=NULL; - } else { - binf=(FILE*)pc_openbin(binfname); - if (binf==NULL) - error(101,binfname); - } /* if */ - setconstants(); /* set predefined constants and tagnames */ - for (i=0; i0) { - if (strcmp(incfname,sDEF_PREFIX)==0) { - plungefile(incfname,FALSE,TRUE); /* parse "default.inc" */ - } else { - if (!plungequalifiedfile(incfname)) /* parse "prefix" include file */ - error(100,incfname); /* cannot read from ... (fatal error) */ - } /* if */ - } /* if */ - preprocess(); /* fetch first line */ - parse(); /* process all input */ - sc_parsenum++; - } while (sc_reparse); - - /* second (or third) pass */ - sc_status=statWRITE; /* set, to enable warnings */ - state_conflict(&glbtab); - - /* write a report, if requested */ - #if !defined SC_LIGHT - if (sc_makereport) { - FILE *frep=stdout; - if (strlen(reportname)>0) - frep=fopen(reportname,"wb"); /* avoid translation of \n to \r\n in DOS/Windows */ - if (frep!=NULL) { - make_report(&glbtab,frep,get_sourcefile(0)); - if (strlen(reportname)>0) - fclose(frep); - } /* if */ - if (sc_documentation!=NULL) { - free(sc_documentation); - sc_documentation=NULL; - } /* if */ - } /* if */ - #endif - if (sc_listing) - goto cleanup; - - /* ??? for re-parsing the listing file instead of the original source - * file (and doing preprocessing twice): - * - close input file, close listing file - * - re-open listing file for reading (inpf) - * - open assembler file (outf) - */ - - /* reset "defined" flag of all functions and global variables */ - reduce_referrers(&glbtab); - delete_symbols(&glbtab,0,TRUE,FALSE); - #if !defined NO_DEFINE - delete_substtable(); - #endif - resetglobals(); - sc_ctrlchar=sc_ctrlchar_org; - sc_packstr=lcl_packstr; - sc_needsemicolon=lcl_needsemicolon; - sc_tabsize=lcl_tabsize; - errorset(sRESET,0); - /* reset the source file */ - inpf=inpf_org; - freading=TRUE; - pc_resetsrc(inpf,inpfmark); /* reset file position */ - fline=skipinput; /* reset line number */ - lexinit(); /* clear internal flags of lex() */ - sc_status=statWRITE; /* allow to write --this variable was reset by resetglobals() */ - writeleader(&glbtab); - insert_dbgfile(inpfname); - if (strlen(incfname)>0) { - if (strcmp(incfname,sDEF_PREFIX)==0) - plungefile(incfname,FALSE,TRUE); /* parse "default.inc" (again) */ - else - plungequalifiedfile(incfname); /* parse implicit include file (again) */ - } /* if */ - preprocess(); /* fetch first line */ - parse(); /* process all input */ - /* inpf is already closed when readline() attempts to pop of a file */ - writetrailer(); /* write remaining stuff */ - - entry=testsymbols(&glbtab,0,TRUE,FALSE); /* test for unused or undefined - * functions and variables */ - if (!entry) - error(13); /* no entry point (no public functions) */ - -cleanup: - if (inpf!=NULL) /* main source file is not closed, do it now */ - pc_closesrc(inpf); - /* write the binary file (the file is already open) */ - if (!(sc_asmfile || sc_listing) && errnum==0 && jmpcode==0) { - assert(binf!=NULL); - pc_resetasm(outf); /* flush and loop back, for reading */ - #if !defined SC_LIGHT - hdrsize= - #endif - assemble(binf,outf); /* assembler file is now input */ - } /* if */ - if (outf!=NULL) { - pc_closeasm(outf,!(sc_asmfile || sc_listing)); - outf=NULL; - } /* if */ - if (binf!=NULL) { - pc_closebin(binf,errnum!=0); - binf=NULL; - } /* if */ - - #if !defined SC_LIGHT - if (errnum==0 && strlen(errfname)==0) { - int recursion; - long stacksize=max_stacksize(&glbtab,&recursion); - int flag_exceed=0; - if (pc_amxlimit>0) { - long totalsize=hdrsize+code_idx; - if (pc_amxram==0) - totalsize+=(glb_declared+pc_stksize)*sizeof(cell); - if (totalsize>=pc_amxlimit) - flag_exceed=1; - } /* if */ - if (pc_amxram>0 && (glb_declared+pc_stksize)*sizeof(cell)>=(unsigned long)pc_amxram) - flag_exceed=1; - if (!norun && (sc_debug & sSYMBOLIC)!=0 || verbosity>=2 || stacksize+32>=(long)pc_stksize || flag_exceed) { - pc_printf("Header size: %8ld bytes\n", (long)hdrsize); - pc_printf("Code size: %8ld bytes\n", (long)code_idx); - pc_printf("Data size: %8ld bytes\n", (long)glb_declared*sizeof(cell)); - pc_printf("Stack/heap size: %8ld bytes; ", (long)pc_stksize*sizeof(cell)); - pc_printf("estimated max. usage"); - if (recursion) - pc_printf(" is unknown, due to recursion\n"); - else if ((pc_memflags & suSLEEP_INSTR)!=0) - pc_printf(" is unknown, due to the \"sleep\" instruction\n"); - else - pc_printf("=%ld cells (%ld bytes)\n",stacksize,stacksize*sizeof(cell)); - pc_printf("Total requirements:%8ld bytes\n", (long)hdrsize+(long)code_idx+(long)glb_declared*sizeof(cell)+(long)pc_stksize*sizeof(cell)); - } /* if */ - if (flag_exceed) - error(106,pc_amxlimit+pc_amxram); /* this causes a jump back to label "cleanup" */ - } /* if */ - #endif - - if (inpfname!=NULL) { - if (get_sourcefile(1)!=NULL) - remove(inpfname); /* the "input file" was in fact a temporary file */ - free(inpfname); - } /* if */ - if (litq!=NULL) - free(litq); - phopt_cleanup(); - stgbuffer_cleanup(); - clearstk(); - assert(jmpcode!=0 || loctab.next==NULL);/* on normal flow, local symbols - * should already have been deleted */ - delete_symbols(&loctab,0,TRUE,TRUE); /* delete local variables if not yet - * done (i.e. on a fatal error) */ - delete_symbols(&glbtab,0,TRUE,TRUE); - delete_consttable(&tagname_tab); - delete_consttable(&libname_tab); - delete_consttable(&sc_automaton_tab); - delete_consttable(&sc_state_tab); - state_deletetable(); - delete_aliastable(); - delete_pathtable(); - delete_sourcefiletable(); - delete_dbgstringtable(); - #if !defined NO_DEFINE - delete_substtable(); - #endif - #if !defined SC_LIGHT - delete_docstringtable(); - if (sc_documentation!=NULL) - free(sc_documentation); - #endif - delete_autolisttable(); - if (errnum!=0) { - if (strlen(errfname)==0) - pc_printf("\n%d Error%s.\n",errnum,(errnum>1) ? "s" : ""); - retcode=1; - } else if (warnnum!=0){ - if (strlen(errfname)==0) - pc_printf("\n%d Warning%s.\n",warnnum,(warnnum>1) ? "s" : ""); - retcode=0; /* use "0", so that MAKE and similar tools continue */ - } else { - retcode=jmpcode; - if (retcode==0 && verbosity>=2) - pc_printf("\nDone.\n"); - } /* if */ - #if defined __WIN32__ || defined _WIN32 || defined _Windows - if (IsWindow(hwndFinish)) - PostMessageA(hwndFinish,RegisterWindowMessageA("PawnNotify"),retcode,0L); - #endif - #if defined FORTIFY - Fortify_ListAllMemory(); - #endif - return retcode; -} - -#if defined __cplusplus - extern "C" -#endif -int pc_addconstant(char *name,cell value,int tag) -{ - errorset(sFORCESET,0); /* make sure error engine is silenced */ - sc_status=statIDLE; - add_constant(name,value,sGLOBAL,tag); - return 1; -} - -#if defined __cplusplus - extern "C" -#endif -int pc_addtag(char *name) -{ - cell val; - constvalue *ptr; - int last,tag; - - if (name==NULL) { - /* no tagname was given, check for one */ - if (lex(&val,&name)!=tLABEL) { - lexpush(); - return 0; /* untagged */ - } /* if */ - } /* if */ - - assert(strchr(name,':')==NULL); /* colon should already have been stripped */ - last=0; - ptr=tagname_tab.next; - while (ptr!=NULL) { - tag=(int)(ptr->value & TAGMASK); - if (strcmp(name,ptr->name)==0) - return tag; /* tagname is known, return its sequence number */ - tag &= (int)~FIXEDTAG; - if (tag>last) - last=tag; - ptr=ptr->next; - } /* while */ - - /* tagname currently unknown, add it */ - tag=last+1; /* guaranteed not to exist already */ - if (isupper(*name)) - tag |= (int)FIXEDTAG; - append_constval(&tagname_tab,name,(cell)tag,0); - return tag; -} - -static void resetglobals(void) -{ - /* reset the subset of global variables that is modified by the first pass */ - curfunc=NULL; /* pointer to current function */ - lastst=0; /* last executed statement type */ - nestlevel=0; /* number of active (open) compound statements */ - rettype=0; /* the type that a "return" expression should have */ - litidx=0; /* index to literal table */ - stgidx=0; /* index to the staging buffer */ - sc_labnum=0; /* top value of (internal) labels */ - staging=0; /* true if staging output */ - declared=0; /* number of local cells declared */ - glb_declared=0; /* number of global cells declared */ - code_idx=0; /* number of bytes with generated code */ - ntv_funcid=0; /* incremental number of native function */ - curseg=0; /* 1 if currently parsing CODE, 2 if parsing DATA */ - freading=FALSE; /* no input file ready yet */ - fline=0; /* the line number in the current file */ - fnumber=0; /* the file number in the file table (debugging) */ - fcurrent=0; /* current file being processed (debugging) */ - sc_intest=FALSE; /* true if inside a test */ - sideeffect=0; /* true if an expression causes a side-effect */ - stmtindent=0; /* current indent of the statement */ - indent_nowarn=FALSE; /* do not skip warning "217 loose indentation" */ - sc_allowtags=TRUE; /* allow/detect tagnames */ - sc_status=statIDLE; - sc_allowproccall=FALSE; - pc_addlibtable=TRUE; /* by default, add a "library table" to the output file */ - sc_alignnext=FALSE; - pc_docexpr=FALSE; - pc_depricate=NULL; - sc_curstates=0; - pc_memflags=0; -} - -static void initglobals(void) -{ - resetglobals(); - - sc_asmfile=FALSE; /* do not create .ASM file */ - sc_listing=FALSE; /* do not create .LST file */ - skipinput=0; /* number of lines to skip from the first input file */ - sc_ctrlchar=CTRL_CHAR;/* the escape character */ - litmax=sDEF_LITMAX; /* current size of the literal table */ - errnum=0; /* number of errors */ - warnnum=0; /* number of warnings */ - optproccall=FALSE; /* sourcemod: do not support "procedure call" */ - verbosity=1; /* verbosity level, no copyright banner */ - sc_debug=sSYMBOLIC; /* sourcemod: full debug stuff */ - pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */ - sc_packstr=FALSE; /* strings are unpacked by default */ - #if AMX_COMPACTMARGIN > 2 - sc_compress=TRUE; /* compress output bytecodes */ - #else - sc_compress=FALSE; - #endif - sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */ - sc_dataalign=sizeof(cell); - pc_stksize=sDEF_AMXSTACK;/* default stack size */ - pc_amxlimit=0; /* no limit on size of the abstract machine */ - pc_amxram=0; /* no limit on data size of the abstract machine */ - sc_tabsize=8; /* assume a TAB is 8 spaces */ - sc_rationaltag=0; /* assume no support for rational numbers */ - rational_digits=0; /* number of fractional digits */ - - outfname[0]='\0'; /* output file name */ - errfname[0]='\0'; /* error file name */ - inpf=NULL; /* file read from */ - inpfname=NULL; /* pointer to name of the file currently read from */ - outf=NULL; /* file written to */ - litq=NULL; /* the literal queue */ - glbtab.next=NULL; /* clear global variables/constants table */ - loctab.next=NULL; /* " local " / " " */ - tagname_tab.next=NULL;/* tagname table */ - libname_tab.next=NULL;/* library table (#pragma library "..." syntax) */ - - pline[0]='\0'; /* the line read from the input file */ - lptr=NULL; /* points to the current position in "pline" */ - curlibrary=NULL; /* current library */ - inpf_org=NULL; /* main source file */ - - wqptr=wq; /* initialize while queue pointer */ - -#if !defined SC_LIGHT - sc_documentation=NULL; - sc_makereport=FALSE; /* do not generate a cross-reference report */ -#endif -} - -static char *get_extension(char *filename) -{ - char *ptr; - - assert(filename!=NULL); - ptr=strrchr(filename,'.'); - if (ptr!=NULL) { - /* ignore extension on a directory or at the start of the filename */ - if (strchr(ptr,DIRSEP_CHAR)!=NULL || ptr==filename || *(ptr-1)==DIRSEP_CHAR) - ptr=NULL; - } /* if */ - return ptr; -} - -/* set_extension - * Set the default extension, or force an extension. To erase the - * extension of a filename, set "extension" to an empty string. - */ -SC_FUNC void set_extension(char *filename,char *extension,int force) -{ - char *ptr; - - assert(extension!=NULL && (*extension=='\0' || *extension=='.')); - assert(filename!=NULL); - ptr=get_extension(filename); - if (force && ptr!=NULL) - *ptr='\0'; /* set zero terminator at the position of the period */ - if (force || ptr==NULL) - strcat(filename,extension); -} - -static const char *option_value(const char *optptr) -{ - return (*(optptr+1)=='=' || *(optptr+1)==':') ? optptr+2 : optptr+1; -} - -static int toggle_option(const char *optptr, int option) -{ - switch (*option_value(optptr)) { - case '\0': - option=!option; - break; - case '-': - option=FALSE; - break; - case '+': - option=TRUE; - break; - default: - about(); - } /* switch */ - return option; -} - -/* Parsing command line options is indirectly recursive: parseoptions() - * calls parserespf() to handle options in a a response file and - * parserespf() calls parseoptions() at its turn after having created - * an "option list" from the contents of the file. - */ -static void parserespf(char *filename,char *oname,char *ename,char *pname, - char *rname, char *codepage); - -static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pname, - char *rname, char *codepage) -{ - char str[_MAX_PATH],*name; - const char *ptr; - int arg,i,isoption; - - for (arg=1; arg1) - verbosity=1; - break; - case 'C': - #if AMX_COMPACTMARGIN > 2 - sc_compress=toggle_option(ptr,sc_compress); - #else - about(); - #endif - break; - case 'c': - strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */ - break; -#if defined dos_setdrive - case 'D': /* set active directory */ - ptr=option_value(ptr); - if (ptr[1]==':') - dos_setdrive(toupper(*ptr)-'A'+1); /* set active drive */ - chdir(ptr); - break; -#endif -#if 0 /* not allowed to change for SourceMod */ - case 'd': - switch (*option_value(ptr)) { - case '0': - sc_debug=0; - break; - case '1': - sc_debug=sCHKBOUNDS; /* assertions and bounds checking */ - break; - case '2': - sc_debug=sCHKBOUNDS | sSYMBOLIC; /* also symbolic info */ - break; - case '3': - sc_debug=sCHKBOUNDS | sSYMBOLIC; - pc_optimize=sOPTIMIZE_NONE; - /* also avoid peephole optimization */ - break; - default: - about(); - } /* switch */ - break; -#endif - case 'e': - strlcpy(ename,option_value(ptr),_MAX_PATH); /* set name of error file */ - break; -#if defined __WIN32__ || defined _WIN32 || defined _Windows - case 'H': - hwndFinish=(HWND)atoi(option_value(ptr)); - if (!IsWindow(hwndFinish)) - hwndFinish=(HWND)0; - break; -#endif - case 'i': - strlcpy(str,option_value(ptr),sizeof str); /* set name of include directory */ - i=strlen(str); - if (i>0) { - if (str[i-1]!=DIRSEP_CHAR) { - str[i]=DIRSEP_CHAR; - str[i+1]='\0'; - } /* if */ - insert_path(str); - } /* if */ - break; - case 'l': - if (*(ptr+1)!='\0') - about(); - sc_listing=TRUE; /* skip second pass & code generation */ - break; - case 'o': - strlcpy(oname,option_value(ptr),_MAX_PATH); /* set name of (binary) output file */ - break; - case 'O': - pc_optimize=*option_value(ptr) - '0'; - if (pc_optimize=sOPTIMIZE_NUMBER || pc_optimize==sOPTIMIZE_NOMACRO) - about(); - break; - case 'p': - strlcpy(pname,option_value(ptr),_MAX_PATH); /* set name of implicit include file */ - break; -#if !defined SC_LIGHT - case 'r': - strlcpy(rname,option_value(ptr),_MAX_PATH); /* set name of report file */ - sc_makereport=TRUE; - if (strlen(rname)>0) { - set_extension(rname,".xml",FALSE); - } else if ((name=get_sourcefile(0))!=NULL) { - assert(strlen(rname)==0); - assert(strlen(name)<_MAX_PATH); - if ((ptr=strrchr(name,DIRSEP_CHAR))!=NULL) - ptr++; /* strip path */ - else - ptr=name; - assert(strlen(ptr)<_MAX_PATH); - strcpy(rname,ptr); - set_extension(rname,".xml",TRUE); - } /* if */ - break; -#endif - case 'S': - i=atoi(option_value(ptr)); - if (i>32) - pc_stksize=(cell)i; /* stack size has minimum size */ - else - about(); - break; - case 's': - skipinput=atoi(option_value(ptr)); - break; - case 't': - sc_tabsize=atoi(option_value(ptr)); - break; - case 'v': - verbosity= isdigit(*option_value(ptr)) ? atoi(option_value(ptr)) : 2; - if (sc_asmfile && verbosity>1) - verbosity=1; - break; - case 'w': - i=(int)strtol(option_value(ptr),(char **)&ptr,10); - if (*ptr=='-') - pc_enablewarning(i,0); - else if (*ptr=='+') - pc_enablewarning(i,1); - else if (*ptr=='\0') - pc_enablewarning(i,2); - break; - case 'X': - if (*(ptr+1)=='D') { - i=atoi(option_value(ptr+1)); - if (i>64) - pc_amxram=(cell)i; /* abstract machine data/stack has minimum size */ - else - about(); - } else { - i=atoi(option_value(ptr)); - if (i>64) - pc_amxlimit=(cell)i;/* abstract machine has minimum size */ - else - about(); - } /* if */ - break; - case '\\': /* use \ instead for escape characters */ - sc_ctrlchar='\\'; - break; - case '^': /* use ^ instead for escape characters */ - sc_ctrlchar='^'; - break; - case ';': - sc_needsemicolon=toggle_option(ptr,sc_needsemicolon); - break; -#if 0 /* not allowed to change in SourceMod */ - case '(': - optproccall=!toggle_option(ptr,!optproccall); - break; -#endif - default: /* wrong option */ - about(); - } /* switch */ - } else if (argv[arg][0]=='@') { - #if !defined SC_LIGHT - parserespf(&argv[arg][1],oname,ename,pname,rname,codepage); - #endif - } else if ((ptr=strchr(argv[arg],'='))!=NULL) { - i=(int)(ptr-argv[arg]); - if (i>sNAMEMAX) { - i=sNAMEMAX; - error(200,argv[arg],sNAMEMAX); /* symbol too long, truncated to sNAMEMAX chars */ - } /* if */ - strlcpy(str,argv[arg],i+1); /* str holds symbol name */ - i=atoi(ptr+1); - add_constant(str,i,sGLOBAL,0); - } else { - strlcpy(str,argv[arg],sizeof(str)-5); /* -5 because default extension is ".psrc" */ - set_extension(str,".psrc",FALSE); - insert_sourcefile(str); - /* The output name is the first input name with a different extension, - * but it is stored in a different directory - */ - if (strlen(oname)==0) { - if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL) - ptr++; /* strip path */ - else - ptr=str; - assert(strlen(ptr)<_MAX_PATH); - strcpy(oname,ptr); - } /* if */ - set_extension(oname,".asm",TRUE); -#if !defined SC_LIGHT - if (sc_makereport && strlen(rname)==0) { - if ((ptr=strrchr(str,DIRSEP_CHAR))!=NULL) - ptr++; /* strip path */ - else - ptr=str; - assert(strlen(ptr)<_MAX_PATH); - strcpy(rname,ptr); - set_extension(rname,".xml",TRUE); - } /* if */ -#endif - } /* if */ - } /* for */ -} - -#if !defined SC_LIGHT -static void parserespf(char *filename,char *oname,char *ename,char *pname, - char *rname,char *codepage) -{ -#define MAX_OPTIONS 100 - FILE *fp; - char *string, *ptr, **argv; - int argc; - long size; - - if ((fp=fopen(filename,"r"))==NULL) - error(100,filename); /* error reading input file */ - /* load the complete file into memory */ - fseek(fp,0L,SEEK_END); - size=ftell(fp); - fseek(fp,0L,SEEK_SET); - assert(size [filename...] [options]\n\n"); - pc_printf("Options:\n"); - pc_printf(" -A alignment in bytes of the data segment and the stack\n"); - pc_printf(" -a output assembler code\n"); -#if AMX_COMPACTMARGIN > 2 - pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-'); -#endif - pc_printf(" -c codepage name or number; e.g. 1252 for Windows Latin-1\n"); -#if defined dos_setdrive - pc_printf(" -Dpath active directory path\n"); -#endif -#if 0 /* not used for SourceMod */ - pc_printf(" -d debugging level (default=-d%d)\n",sc_debug); - pc_printf(" 0 no symbolic information, no run-time checks\n"); - pc_printf(" 1 run-time checks, no symbolic information\n"); - pc_printf(" 2 full debug information and dynamic checking\n"); - pc_printf(" 3 same as -d2, but implies -O0\n"); -#endif - pc_printf(" -e set name of error file (quiet compile)\n"); -#if defined __WIN32__ || defined _WIN32 || defined _Windows - pc_printf(" -H window handle to send a notification message on finish\n"); -#endif - pc_printf(" -i path for include files\n"); - pc_printf(" -l create list file (preprocess only)\n"); - pc_printf(" -o set base name of (P-code) output file\n"); - pc_printf(" -O optimization level (default=-O%d)\n",pc_optimize); - pc_printf(" 0 no optimization\n"); -#if 0 /* not used for SourceMod */ - pc_printf(" 1 JIT-compatible optimizations only\n"); -#endif - pc_printf(" 2 full optimizations\n"); - pc_printf(" -p set name of \"prefix\" file\n"); -#if !defined SC_LIGHT - pc_printf(" -r[name] write cross reference report to console or to specified file\n"); -#endif - pc_printf(" -S stack/heap size in cells (default=%d)\n",(int)pc_stksize); - pc_printf(" -s skip lines from the input file\n"); - pc_printf(" -t TAB indent size (in character positions, default=%d)\n",sc_tabsize); - pc_printf(" -v verbosity level; 0=quiet, 1=normal, 2=verbose (default=%d)\n",verbosity); - pc_printf(" -w disable a specific warning by its number\n"); - pc_printf(" -X abstract machine size limit in bytes\n"); - pc_printf(" -XD abstract machine data/stack size limit in bytes\n"); - pc_printf(" -\\ use '\\' for escape characters\n"); - pc_printf(" -^ use '^' for escape characters\n"); - pc_printf(" -;[+/-] require a semicolon to end each statement (default=%c)\n", sc_needsemicolon ? '+' : '-'); -#if 0 /* not allowed in SourceMod */ - pc_printf(" -([+/-] require parantheses for function invocation (default=%c)\n", optproccall ? '-' : '+'); -#endif - pc_printf(" sym=val define constant \"sym\" with value \"val\"\n"); - pc_printf(" sym= define constant \"sym\" with value 0\n"); -#if defined __WIN32__ || defined _WIN32 || defined _Windows || defined __MSDOS__ - pc_printf("\nOptions may start with a dash or a slash; the options \"-d0\" and \"/d0\" are\n"); - pc_printf("equivalent.\n"); -#endif - pc_printf("\nOptions with a value may optionally separate the value from the option letter\n"); - pc_printf("with a colon (\":\") or an equal sign (\"=\"). That is, the options \"-d0\", \"-d=0\"\n"); - pc_printf("and \"-d:0\" are all equivalent.\n"); - } /* if */ - norun = 1; - longjmp(errbuf,3); /* user abort */ -} - -static void setconstants(void) -{ - int debug; - - assert(sc_status==statIDLE); - append_constval(&tagname_tab,"_",0,0);/* "untagged" */ - append_constval(&tagname_tab,"bool",1,0); - - add_constant("true",1,sGLOBAL,1); /* boolean flags */ - add_constant("false",0,sGLOBAL,1); - add_constant("EOS",0,sGLOBAL,0); /* End Of String, or '\0' */ - #if PAWN_CELL_SIZE==16 - add_constant("cellbits",16,sGLOBAL,0); - #if defined _I16_MAX - add_constant("cellmax",_I16_MAX,sGLOBAL,0); - add_constant("cellmin",_I16_MIN,sGLOBAL,0); - #else - add_constant("cellmax",SHRT_MAX,sGLOBAL,0); - add_constant("cellmin",SHRT_MIN,sGLOBAL,0); - #endif - #elif PAWN_CELL_SIZE==32 - add_constant("cellbits",32,sGLOBAL,0); - #if defined _I32_MAX - add_constant("cellmax",_I32_MAX,sGLOBAL,0); - add_constant("cellmin",_I32_MIN,sGLOBAL,0); - #else - add_constant("cellmax",LONG_MAX,sGLOBAL,0); - add_constant("cellmin",LONG_MIN,sGLOBAL,0); - #endif - #elif PAWN_CELL_SIZE==64 - #if !defined _I64_MIN - #define _I64_MIN (-9223372036854775807ULL - 1) - #define _I64_MAX 9223372036854775807ULL - #endif - add_constant("cellbits",64,sGLOBAL,0); - add_constant("cellmax",_I64_MAX,sGLOBAL,0); - add_constant("cellmin",_I64_MIN,sGLOBAL,0); - #else - #error Unsupported cell size - #endif - add_constant("charbits",sCHARBITS,sGLOBAL,0); - add_constant("charmin",0,sGLOBAL,0); - add_constant("charmax",~(-1 << sCHARBITS) - 1,sGLOBAL,0); - add_constant("ucharmax",(1 << (sizeof(cell)-1)*8)-1,sGLOBAL,0); - - add_constant("__Pawn",VERSION_INT,sGLOBAL,0); - - debug=0; - if ((sc_debug & (sCHKBOUNDS | sSYMBOLIC))==(sCHKBOUNDS | sSYMBOLIC)) - debug=2; - else if ((sc_debug & sCHKBOUNDS)==sCHKBOUNDS) - debug=1; - add_constant("debug",debug,sGLOBAL,0); - - append_constval(&sc_automaton_tab,"",0,0); /* anonymous automaton */ -} - -static int getclassspec(int initialtok,int *fpublic,int *fstatic,int *fstock,int *fconst) -{ - int tok,err; - cell val; - char *str; - - assert(fconst!=NULL); - assert(fstock!=NULL); - assert(fstatic!=NULL); - assert(fpublic!=NULL); - *fconst=FALSE; - *fstock=FALSE; - *fstatic=FALSE; - *fpublic=FALSE; - switch (initialtok) { - case tCONST: - *fconst=TRUE; - break; - case tSTOCK: - *fstock=TRUE; - break; - case tSTATIC: - *fstatic=TRUE; - break; - case tPUBLIC: - *fpublic=TRUE; - break; - } /* switch */ - - err=0; - do { - tok=lex(&val,&str); /* read in (new) token */ - switch (tok) { - case tCONST: - if (*fconst) - err=42; /* invalid combination of class specifiers */ - *fconst=TRUE; - break; - case tSTOCK: - if (*fstock) - err=42; /* invalid combination of class specifiers */ - *fstock=TRUE; - break; - case tSTATIC: - if (*fstatic) - err=42; /* invalid combination of class specifiers */ - *fstatic=TRUE; - break; - case tPUBLIC: - if (*fpublic) - err=42; /* invalid combination of class specifiers */ - *fpublic=TRUE; - break; - default: - lexpush(); - tok=0; /* force break out of loop */ - } /* switch */ - } while (tok && err==0); - - /* extra checks */ - if (*fstatic && *fpublic) { - err=42; /* invalid combination of class specifiers */ - *fstatic=*fpublic=FALSE; - } /* if */ - - if (err) - error(err); - return err==0; -} - -/* parse - process all input text - * - * At this level, only static declarations and function definitions are legal. - */ -static void parse(void) -{ - int tok,fconst,fstock,fstatic,fpublic; - cell val; - char *str; - - while (freading){ - /* first try whether a declaration possibly is native or public */ - tok=lex(&val,&str); /* read in (new) token */ - switch (tok) { - case 0: - /* ignore zero's */ - break; - case tNEW: - if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) - declglb(NULL,0,fpublic,fstatic,fstock,fconst); - break; - case tSTATIC: - /* This can be a static function or a static global variable; we know - * which of the two as soon as we have parsed up to the point where an - * opening paranthesis of a function would be expected. To back out after - * deciding it was a declaration of a static variable after all, we have - * to store the symbol name and tag. - */ - if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { - assert(!fpublic); - declfuncvar(fpublic,fstatic,fstock,fconst); - } /* if */ - break; - case tCONST: - decl_const(sGLOBAL); - break; - case tENUM: - decl_enum(sGLOBAL); - break; - case tPUBLIC: - /* This can be a public function or a public variable; see the comment - * above (for static functions/variables) for details. - */ - if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { - assert(!fstatic); - declfuncvar(fpublic,fstatic,fstock,fconst); - } /* if */ - break; - case tSTOCK: - /* This can be a stock function or a stock *global*) variable; see the - * comment above (for static functions/variables) for details. - */ - if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { - assert(fstock); - declfuncvar(fpublic,fstatic,fstock,fconst); - } /* if */ - break; - case tLABEL: - case tSYMBOL: - case tOPERATOR: - lexpush(); - if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) { - error(10); /* illegal function or declaration */ - lexclr(TRUE); /* drop the rest of the line */ - litidx=0; /* drop the literal queue too */ - } /* if */ - break; - case tNATIVE: - funcstub(TRUE); /* create a dummy function */ - break; - case tFORWARD: - funcstub(FALSE); - break; - case '}': - error(54); /* unmatched closing brace */ - break; - case '{': - error(55); /* start of function body without function header */ - break; - default: - if (freading) { - error(10); /* illegal function or declaration */ - lexclr(TRUE); /* drop the rest of the line */ - litidx=0; /* drop any literal arrays (strings) */ - } /* if */ - } /* switch */ - } /* while */ -} - -/* dumplits - * - * Dump the literal pool (strings etc.) - * - * Global references: litidx (referred to only) - */ -static void dumplits(void) -{ - int j,k; - - k=0; - while (k=litidx) - stgwrite("\n"); /* force a newline after 10 dumps */ - /* Note: stgwrite() buffers a line until it is complete. It recognizes - * the end of line as a sequence of "\n\0", so something like "\n\t" - * so should not be passed to stgwrite(). - */ - } /* while */ - } /* while */ -} - -/* dumpzero - * - * Dump zero's for default initial values - */ -static void dumpzero(int count) -{ - int i; - - if (count<=0) - return; - assert(curseg==2); - defstorage(); - i=0; - while (count-- > 0) { - outval(0, FALSE); - i=(i+1) % 16; - stgwrite((i==0 || count==0) ? "\n" : " "); - if (i==0 && count>0) - defstorage(); - } /* while */ -} - -static void aligndata(int numbytes) -{ - assert(numbytes % sizeof(cell) == 0); /* alignment must be a multiple of - * the cell size */ - assert(numbytes!=0); - - if ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) { - while ((((glb_declared+litidx)*sizeof(cell)) % numbytes)!=0) - litadd(0); - } /* if */ - -} - -#if !defined SC_LIGHT -/* sc_attachdocumentation() - * appends documentation comments to the passed-in symbol, or to a global - * string if "sym" is NULL. - */ -void sc_attachdocumentation(symbol *sym) -{ - int line; - size_t length; - char *str,*doc; - - if (!sc_makereport || sc_status!=statFIRST || sc_parsenum>0) { - /* just clear the entire table */ - delete_docstringtable(); - return; - } /* if */ - /* in the case of state functions, multiple documentation sections may - * appear; we should concatenate these - * (with forward declarations, this is also already the case, so the assertion - * below is invalid) - */ - // assert(sym==NULL || sym->documentation==NULL || sym->states!=NULL); - - /* first check the size */ - length=0; - for (line=0; (str=get_docstring(line))!=NULL && *str!=sDOCSEP; line++) { - if (length>0) - length++; /* count 1 extra for a separating space */ - length+=strlen(str); - } /* for */ - if (sym==NULL && sc_documentation!=NULL) { - length += strlen(sc_documentation) + 1 + 4; /* plus 4 for "

" */ - assert(length>strlen(sc_documentation)); - } /* if */ - - if (length>0) { - /* allocate memory for the documentation */ - if (sym!=NULL && sym->documentation!=NULL) - length+=strlen(sym->documentation) + 1 + 4;/* plus 4 for "

" */ - doc=(char*)malloc((length+1)*sizeof(char)); - if (doc!=NULL) { - /* initialize string or concatenate */ - if (sym==NULL && sc_documentation!=NULL) { - strcpy(doc,sc_documentation); - strcat(doc,"

"); - } else if (sym!=NULL && sym->documentation!=NULL) { - strcpy(doc,sym->documentation); - strcat(doc,"

"); - free(sym->documentation); - sym->documentation=NULL; - } else { - doc[0]='\0'; - } /* if */ - /* collect all documentation */ - while ((str=get_docstring(0))!=NULL && *str!=sDOCSEP) { - if (doc[0]!='\0') - strcat(doc," "); - strcat(doc,str); - delete_docstring(0); - } /* while */ - if (str!=NULL) { - /* also delete the separator */ - assert(*str==sDOCSEP); - delete_docstring(0); - } /* if */ - if (sym!=NULL) { - assert(sym->documentation==NULL); - sym->documentation=doc; - } else { - if (sc_documentation!=NULL) - free(sc_documentation); - sc_documentation=doc; - } /* if */ - } /* if */ - } else { - /* delete an empty separator, if present */ - if ((str=get_docstring(0))!=NULL && *str==sDOCSEP) - delete_docstring(0); - } /* if */ -} - -static void insert_docstring_separator(void) -{ - char sep[2]={sDOCSEP,'\0'}; - insert_docstring(sep); -} -#else - #define sc_attachdocumentation(s) (void)(s) - #define insert_docstring_separator() -#endif - -static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst) -{ - char name[sNAMEMAX+11]; - int tok,tag; - char *str; - cell val; - int invalidfunc; - - tag=pc_addtag(NULL); - tok=lex(&val,&str); - /* if we arrived here, this may not be a declaration of a native function - * or variable - */ - if (tok==tNATIVE) { - error(42); /* invalid combination of class specifiers */ - return; - } /* if */ - - if (tok!=tSYMBOL && tok!=tOPERATOR) { - lexpush(); - needtoken(tSYMBOL); - lexclr(TRUE); /* drop the rest of the line */ - litidx=0; /* drop the literal queue too */ - return; - } /* if */ - if (tok==tOPERATOR) { - lexpush(); /* push "operator" keyword back (for later analysis) */ - if (!newfunc(NULL,tag,fpublic,fstatic,fstock)) { - error(10); /* illegal function or declaration */ - lexclr(TRUE); /* drop the rest of the line */ - litidx=0; /* drop the literal queue too */ - } /* if */ - } else { - /* so tok is tSYMBOL */ - assert(strlen(str)<=sNAMEMAX); - strcpy(name,str); - /* only variables can be "const" or both "public" and "stock" */ - invalidfunc= fconst || (fpublic && fstock); - if (invalidfunc || !newfunc(name,tag,fpublic,fstatic,fstock)) { - /* if not a function, try a global variable */ - declglb(name,tag,fpublic,fstatic,fstock,fconst); - } /* if */ - } /* if */ -} - -/* declglb - declare global symbols - * - * Declare a static (global) variable. Global variables are stored in - * the DATA segment. - * - * global references: glb_declared (altered) - */ -static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fstock,int fconst) -{ - int ident,tag,ispublic; - int idxtag[sDIMEN_MAX]; - char name[sNAMEMAX+1]; - cell val,size,cidx; - ucell address; - int glb_incr; - char *str; - int dim[sDIMEN_MAX]; - int numdim; - short filenum; - symbol *sym; - constvalue *enumroot; - #if !defined NDEBUG - cell glbdecl=0; - #endif - - assert(!fpublic || !fstatic); /* may not both be set */ - insert_docstring_separator(); /* see comment in newfunc() */ - filenum=fcurrent; /* save file number at the start of the declaration */ - do { - size=1; /* single size (no array) */ - numdim=0; /* no dimensions */ - ident=iVARIABLE; - if (firstname!=NULL) { - assert(strlen(firstname)<=sNAMEMAX); - strcpy(name,firstname); /* save symbol name */ - tag=firsttag; - firstname=NULL; - } else { - tag=pc_addtag(NULL); - if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ - error(20,str); /* invalid symbol name */ - assert(strlen(str)<=sNAMEMAX); - strcpy(name,str); /* save symbol name */ - } /* if */ - ispublic=fpublic; - if (name[0]==PUBLIC_CHAR) { - ispublic=TRUE; /* implicitly public variable */ - assert(!fstatic); - } /* if */ - while (matchtoken('[')) { - ident=iARRAY; - if (numdim == sDIMEN_MAX) { - error(53); /* exceeding maximum number of dimensions */ - return; - } /* if */ - size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */ - #if INT_MAX < LONG_MAX - if (size > INT_MAX) - error(105); /* overflow, exceeding capacity */ - #endif - if (ispublic) - error(56,name); /* arrays cannot be public */ - dim[numdim++]=(int)size; - } /* while */ - assert(sc_curstates==0); - sc_curstates=getstates(name); - if (sc_curstates<0) { - error(85,name); /* empty state list on declaration */ - sc_curstates=0; - } else if (sc_curstates>0 && ispublic) { - error(88,name); /* public variables may not have states */ - sc_curstates=0; - } /* if */ - sym=findconst(name,NULL); - if (sym==NULL) { - sym=findglb(name,sSTATEVAR); - /* if a global variable without states is found and this declaration has - * states, the declaration is okay - */ - if (sym!=NULL && sym->states==NULL && sc_curstates>0) - sym=NULL; /* set to NULL, we found the global variable */ - if (sc_curstates>0 && findglb(name,sGLOBAL)!=NULL) - error(233,name); /* state variable shadows a global variable */ - } /* if */ - /* we have either: - * a) not found a matching variable (or rejected it, because it was a shadow) - * b) found a global variable and we were looking for that global variable - * c) found a state variable in the automaton that we were looking for - */ - assert(sym==NULL - || sym->states==NULL && sc_curstates==0 - || sym->states!=NULL && sym->next!=NULL && sym->states->next->index==sc_curstates); - /* a state variable may only have a single id in its list (so either this - * variable has no states, or it has a single list) - */ - assert(sym==NULL || sym->states==NULL || sym->states->next->next==NULL); - /* it is okay for the (global) variable to exist, as long as it belongs to - * a different automaton - */ - if (sym!=NULL && (sym->usage & uDEFINE)!=0) - error(21,name); /* symbol already defined */ - /* if this variable is never used (which can be detected only in the - * second stage), shut off code generation - */ - cidx=0; /* only to avoid a compiler warning */ - if (sc_status==statWRITE && sym!=NULL && (sym->usage & (uREAD | uWRITTEN))==0) { - sc_status=statSKIP; - cidx=code_idx; - #if !defined NDEBUG - glbdecl=glb_declared; - #endif - } /* if */ - begdseg(); /* real (initialized) data in data segment */ - assert(litidx==0); /* literal queue should be empty */ - if (sc_alignnext) { - litidx=0; - aligndata(sc_dataalign); - dumplits(); /* dump the literal queue */ - sc_alignnext=FALSE; - litidx=0; /* global initial data is dumped, so restart at zero */ - } /* if */ - assert(litidx==0); /* literal queue should be empty (again) */ - initials(ident,tag,&size,dim,numdim,enumroot);/* stores values in the literal queue */ - assert(size>=litidx); - if (numdim==1) - dim[0]=(int)size; - /* before dumping the initial values (or zeros) check whether this variable - * overlaps another - */ - if (sc_curstates>0) { - unsigned char *map; - - if (litidx!=0) - error(89,name); /* state variables may not be initialized */ - /* find an appropriate address for the state variable */ - /* assume that it cannot be found */ - address=sizeof(cell)*glb_declared; - glb_incr=(int)size; - /* use a memory map in which every cell occupies one bit */ - if (glb_declared>0 && (map=(unsigned char*)malloc((glb_declared+7)/8))!=NULL) { - int fsa=state_getfsa(sc_curstates); - symbol *sweep; - cell sweepsize,addr; - memset(map,0,(glb_declared+7)/8); - assert(fsa>=0); - /* fill in all variables belonging to this automaton */ - for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) { - if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym) - continue; /* hierarchical type, or no states, or same as this variable */ - if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY) - continue; /* a function or a constant */ - if ((sweep->usage & uDEFINE)==0) - continue; /* undefined variable, ignore */ - if (fsa!=state_getfsa(sweep->states->next->index)) - continue; /* wrong automaton */ - /* when arrived here, this is a global variable, with states and - * belonging to the same automaton as the variable we are declaring - */ - sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); - assert(sweep->addr % sizeof(cell) == 0); - addr=sweep->addr/sizeof(cell); - /* mark this address range */ - while (sweepsize-->0) { - map[addr/8] |= (unsigned char)(1 << (addr % 8)); - addr++; - } /* while */ - } /* for */ - /* go over it again, clearing any ranges that have conflicts */ - for (sweep=glbtab.next; sweep!=NULL; sweep=sweep->next) { - if (sweep->parent!=NULL || sweep->states==NULL || sweep==sym) - continue; /* hierarchical type, or no states, or same as this variable */ - if (sweep->ident!=iVARIABLE && sweep->ident!=iARRAY) - continue; /* a function or a constant */ - if ((sweep->usage & uDEFINE)==0) - continue; /* undefined variable, ignore */ - if (fsa!=state_getfsa(sweep->states->next->index)) - continue; /* wrong automaton */ - /* when arrived here, this is a global variable, with states and - * belonging to the same automaton as the variable we are declaring - */ - /* if the lists of states of the existing variable and the new - * variable have a non-empty intersection, this is not a suitable - * overlap point -> wipe the address range - */ - if (state_conflict_id(sc_curstates,sweep->states->next->index)) { - sweepsize=(sweep->ident==iVARIABLE) ? 1 : array_totalsize(sweep); - assert(sweep->addr % sizeof(cell) == 0); - addr=sweep->addr/sizeof(cell); - /* mark this address range */ - while (sweepsize-->0) { - map[addr/8] &= (unsigned char)(~(1 << (addr % 8))); - addr++; - } /* while */ - } /* if */ - } /* for */ - /* now walk through the map and find a starting point that is big enough */ - sweepsize=0; - for (addr=0; addr=size) - break; /* fitting range found, no need to search further */ - } /* for */ - if (sweepsize-addr>=size) - break; /* fitting range found, no need to search further */ - addr=sweepsize; - } /* for */ - free(map); - if (sweepsize-addr>=size) { - address=sizeof(cell)*addr; /* fitting range found, set it */ - glb_incr=0; - } /* if */ - } /* if */ - } else { - address=sizeof(cell)*glb_declared; - glb_incr=(int)size; - } /* if */ - if (address==sizeof(cell)*glb_declared) { - dumplits(); /* dump the literal queue */ - dumpzero((int)size-litidx); - } /* if */ - litidx=0; - if (sym==NULL) { /* define only if not yet defined */ - sym=addvariable(name,address,ident,sGLOBAL,tag,dim,numdim,idxtag); - if (sc_curstates>0) - attachstatelist(sym,sc_curstates); - } else { /* if declared but not yet defined, adjust the variable's address */ - assert(sym->states==NULL && sc_curstates==0 - || sym->states->next!=NULL && sym->states->next->index==sc_curstates && sym->states->next->next==NULL); - sym->addr=address; - sym->codeaddr=code_idx; - sym->usage|=uDEFINE; - } /* if */ - assert(sym!=NULL); - sc_curstates=0; - if (ispublic) - sym->usage|=uPUBLIC; - if (fconst) - sym->usage|=uCONST; - if (fstock) - sym->usage|=uSTOCK; - if (fstatic) - sym->fnumber=filenum; - sc_attachdocumentation(sym);/* attach any documenation to the variable */ - if (sc_status==statSKIP) { - sc_status=statWRITE; - code_idx=cidx; - assert(glb_declared==glbdecl); - } else { - glb_declared+=glb_incr; /* add total number of cells (if added to the end) */ - } /* if */ - } while (matchtoken(',')); /* enddo */ /* more? */ - needtoken(tTERM); /* if not comma, must be semicolumn */ -} - -/* declloc - declare local symbols - * - * Declare local (automatic) variables. Since these variables are relative - * to the STACK, there is no switch to the DATA segment. These variables - * cannot be initialized either. - * - * global references: declared (altered) - * funcstatus (referred to only) - */ -static int declloc(int fstatic) -{ - int ident,tag; - int idxtag[sDIMEN_MAX]; - char name[sNAMEMAX+1]; - symbol *sym; - constvalue *enumroot; - cell val,size; - char *str; - value lval = {0}; - int cur_lit=0; - int dim[sDIMEN_MAX]; - int numdim; - int fconst; - int staging_start; - - fconst=matchtoken(tCONST); - do { - ident=iVARIABLE; - size=1; - numdim=0; /* no dimensions */ - tag=pc_addtag(NULL); - if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ - error(20,str); /* invalid symbol name */ - assert(strlen(str)<=sNAMEMAX); - strcpy(name,str); /* save symbol name */ - if (name[0]==PUBLIC_CHAR) - error(56,name); /* local variables cannot be public */ - /* Note: block locals may be named identical to locals at higher - * compound blocks (as with standard C); so we must check (and add) - * the "nesting level" of local variables to verify the - * multi-definition of symbols. - */ - if ((sym=findloc(name))!=NULL && sym->compound==nestlevel) - error(21,name); /* symbol already defined */ - /* Although valid, a local variable whose name is equal to that - * of a global variable or to that of a local variable at a lower - * level might indicate a bug. - */ - if ((sym=findloc(name))!=NULL && sym->compound!=nestlevel || findglb(name,sGLOBAL)!=NULL) - error(219,name); /* variable shadows another symbol */ - while (matchtoken('[')){ - ident=iARRAY; - if (numdim == sDIMEN_MAX) { - error(53); /* exceeding maximum number of dimensions */ - return ident; - } /* if */ - size=needsub(&idxtag[numdim],&enumroot); /* get size; size==0 for "var[]" */ - #if INT_MAX < LONG_MAX - if (size > INT_MAX) - error(105); /* overflow, exceeding capacity */ - #endif - dim[numdim++]=(int)size; - } /* while */ - if (getstates(name)) - error(88,name); /* local variables may not have states */ - if (ident==iARRAY || fstatic) { - if (sc_alignnext) { - aligndata(sc_dataalign); - sc_alignnext=FALSE; - } /* if */ - cur_lit=litidx; /* save current index in the literal table */ - initials(ident,tag,&size,dim,numdim,enumroot); - if (size==0) - return ident; /* error message already given */ - if (numdim==1) - dim[0]=(int)size; - } /* if */ - /* reserve memory (on the stack) for the variable */ - if (fstatic) { - /* write zeros for uninitialized fields */ - while (litidxusage & uNATIVE)==0); - if (curfunc->x.stacksizex.stacksize=declared+1; /* +1 for PROC opcode */ - } /* if */ - /* now that we have reserved memory for the variable, we can proceed - * to initialize it */ - assert(sym!=NULL); /* we declared it, it must be there */ - sym->compound=nestlevel; /* for multiple declaration/shadowing check */ - if (fconst) - sym->usage|=uCONST; - if (!fstatic) { /* static variables already initialized */ - if (ident==iVARIABLE) { - /* simple variable, also supports initialization */ - int ctag = tag; /* set to "tag" by default */ - int explicit_init=FALSE;/* is the variable explicitly initialized? */ - if (matchtoken('=')) { - doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE); - explicit_init=TRUE; - } else { - ldconst(0,sPRI); /* uninitialized variable, set to zero */ - } /* if */ - /* now try to save the value (still in PRI) in the variable */ - lval.sym=sym; - lval.ident=iVARIABLE; - lval.constval=0; - lval.tag=tag; - check_userop(NULL,ctag,lval.tag,2,NULL,&ctag); - store(&lval); - markexpr(sEXPR,NULL,0); /* full expression ends after the store */ - assert(staging); /* end staging phase (optimize expression) */ - stgout(staging_start); - stgset(FALSE); - if (!matchtag(tag,ctag,TRUE)) - error(213); /* tag mismatch */ - /* if the variable was not explicitly initialized, reset the - * "uWRITTEN" flag that store() set */ - if (!explicit_init) - sym->usage &= ~uWRITTEN; - } else { - /* an array */ - assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax); - assert(size>0 && size>=sym->dim.array.length); - assert(numdim>1 || size==sym->dim.array.length); - /* final literal values that are zero make no sense to put in the literal - * pool, because values get zero-initialized anyway; we check for this, - * because users often explicitly initialize strings to "" - */ - while (litidx>cur_lit && litq[litidx-1]==0) - litidx--; - /* if the array is not completely filled, set all values to zero first */ - if (litidx-cur_lit=0 && cur<=numdim); - if (cur==numdim) - return 0; - subsize=calc_arraysize(dim,numdim,cur+1); - newsize=dim[cur]+dim[cur]*subsize; - if ((ucell)subsize>=CELL_MAX || newsize>=CELL_MAX || newsize<(ucell)subsize - || newsize*sizeof(cell)>=CELL_MAX) - return CELL_MAX; - return newsize; -} - -static cell adjust_indirectiontables(int dim[],int numdim,int cur,cell increment, - int startlit,constvalue *lastdim,int *skipdim) -{ -static int base; - int d; - cell accum; - - assert(cur>=0 && cur=0); - assert(cur>0 && startlit==-1 || startlit>=0 && startlit<=litidx); - if (cur==0) - base=startlit; - if (cur==numdim-1) - return 0; - /* 2 or more dimensions left, fill in an indirection vector */ - assert(dim[cur]>0); - if (dim[cur+1]>0) { - for (d=0; dnext; d<*skipdim; d++,ld=ld->next) { - assert(ld!=NULL); - } /* for */ - for (d=0; dname,NULL,16)==d); - litq[base++]=(dim[cur]+accum+increment) * sizeof(cell); - accum+=ld->value-1; - *skipdim+=1; - ld=ld->next; - } /* for */ - } /* if */ - /* create the indirection tables for the lower level */ - if (cur+2=dim[cur]) { - error(18); /* initialization data exceeds array size */ - break; - } /* if */ - if (cur+20) { - if (idxcounteddim[cur]) - error(18); /* initialization data exceeds declared size */ - } /* if */ - counteddim[cur]=idx; - - return totalsize+dim[cur]; /* size of sub-arrays + indirection vector */ -} - -/* initvector - * Initialize a single dimensional array - */ -static cell initvector(int ident,int tag,cell size,int fillzero, - constvalue *enumroot,int *errorfound) -{ - cell prev1=0,prev2=0; - int ellips=FALSE; - int curlit=litidx; - int rtag,ctag; - - assert(ident==iARRAY || ident==iREFARRAY); - if (matchtoken('{')) { - constvalue *enumfield=(enumroot!=NULL) ? enumroot->next : NULL; - do { - int fieldlit=litidx; - int matchbrace,i; - if (matchtoken('}')) { /* to allow for trailing ',' after the initialization */ - lexpush(); - break; - } /* if */ - if ((ellips=matchtoken(tELLIPS))!=0) - break; - /* for enumeration fields, allow another level of braces ("{...}") */ - matchbrace=0; /* preset */ - ellips=0; - if (enumfield!=NULL) - matchbrace=matchtoken('{'); - for ( ;; ) { - prev2=prev1; - prev1=init(ident,&ctag,errorfound); - if (!matchbrace) - break; - if ((ellips=matchtoken(tELLIPS))!=0) - break; - if (!matchtoken(',')) { - needtoken('}'); - break; - } /* for */ - } /* for */ - /* if this array is based on an enumeration, fill the "field" up with - * zeros, and toggle the tag - */ - if (enumroot!=NULL && enumfield==NULL) - error(227); /* more initiallers than enum fields */ - rtag=tag; /* preset, may be overridden by enum field tag */ - if (enumfield!=NULL) { - cell step; - int cmptag=enumfield->index; - symbol *symfield=findconst(enumfield->name,&cmptag); - if (cmptag>1) - error(91,enumfield->name); /* ambiguous constant, needs tag override */ - assert(symfield!=NULL); - assert(fieldlitsymfield->dim.array.length) - error(228); /* length of initialler exceeds size of the enum field */ - if (ellips) { - step=prev1-prev2; - } else { - step=0; - prev1=0; - } /* if */ - for (i=litidx-fieldlit; idim.array.length; i++) { - prev1+=step; - litadd(prev1); - } /* for */ - rtag=symfield->x.tags.index; /* set the expected tag to the index tag */ - enumfield=enumfield->next; - } /* if */ - if (!matchtag(rtag,ctag,TRUE)) - error(213); /* tag mismatch */ - } while (matchtoken(',')); /* do */ - needtoken('}'); - } else { - init(ident,&ctag,errorfound); - if (!matchtag(tag,ctag,TRUE)) - error(213); /* tagname mismatch */ - } /* if */ - /* fill up the literal queue with a series */ - if (ellips) { - cell step=((litidx-curlit)==1) ? (cell)0 : prev1-prev2; - if (size==0 || (litidx-curlit)==0) - error(41); /* invalid ellipsis, array size unknown */ - else if ((litidx-curlit)==(int)size) - error(18); /* initialisation data exceeds declared size */ - while ((litidx-curlit)<(int)size) { - prev1+=step; - litadd(prev1); - } /* while */ - } /* if */ - if (fillzero && size>0) { - while ((litidx-curlit)<(int)size) - litadd(0); - } /* if */ - if (size==0) { - size=litidx-curlit; /* number of elements defined */ - } else if (litidx-curlit>(int)size) { /* e.g. "myvar[3]={1,2,3,4};" */ - error(18); /* initialisation data exceeds declared size */ - litidx=(int)size+curlit; /* avoid overflow in memory moves */ - } /* if */ - return size; -} - -/* init - * - * Evaluate one initializer. - */ -static cell init(int ident,int *tag,int *errorfound) -{ - cell i = 0; - - if (matchtoken(tSTRING)){ - /* lex() automatically stores strings in the literal table (and - * increases "litidx") */ - if (ident==iVARIABLE) { - error(6); /* must be assigned to an array */ - litidx=1; /* reset literal queue */ - } /* if */ - *tag=0; - } else if (constexpr(&i,tag,NULL)){ - litadd(i); /* store expression result in literal table */ - } else { - if (errorfound!=NULL) - *errorfound=TRUE; - } /* if */ - return i; -} - -/* needsub - * - * Get required array size - */ -static cell needsub(int *tag,constvalue **enumroot) -{ - cell val; - symbol *sym; - - assert(tag!=NULL); - *tag=0; - if (enumroot!=NULL) - *enumroot=NULL; /* preset */ - if (matchtoken(']')) /* we have already seen "[" */ - return 0; /* zero size (like "char msg[]") */ - - constexpr(&val,tag,&sym); /* get value (must be constant expression) */ - if (val<0) { - error(9); /* negative array size is invalid; assumed zero */ - val=0; - } /* if */ - needtoken(']'); - - if (enumroot!=NULL) { - /* get the field list for an enumeration */ - assert(*enumroot==NULL);/* should have been preset */ - assert(sym==NULL || sym->ident==iCONSTEXPR); - if (sym!=NULL && (sym->usage & uENUMROOT)==uENUMROOT) { - assert(sym->dim.enumlist!=NULL); - *enumroot=sym->dim.enumlist; - } /* if */ - } /* if */ - - return val; /* return array size */ -} - -/* decl_const - declare a single constant - * - */ -static void decl_const(int vclass) -{ - char constname[sNAMEMAX+1]; - cell val; - char *str; - int tag,exprtag; - int symbolline; - symbol *sym; - - insert_docstring_separator(); /* see comment in newfunc() */ - tag=pc_addtag(NULL); - if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ - error(20,str); /* invalid symbol name */ - symbolline=fline; /* save line where symbol was found */ - strcpy(constname,str); /* save symbol name */ - needtoken('='); - constexpr(&val,&exprtag,NULL);/* get value */ - needtoken(tTERM); - /* add_constant() checks for duplicate definitions */ - if (!matchtag(tag,exprtag,FALSE)) { - /* temporarily reset the line number to where the symbol was defined */ - int orgfline=fline; - fline=symbolline; - error(213); /* tagname mismatch */ - fline=orgfline; - } /* if */ - sym=add_constant(constname,val,vclass,tag); - if (sym!=NULL) - sc_attachdocumentation(sym);/* attach any documenation to the function */ -} - -/* decl_enum - declare enumerated constants - * - */ -static void decl_enum(int vclass) -{ - char enumname[sNAMEMAX+1],constname[sNAMEMAX+1]; - cell val,value,size; - char *str; - int tag,explicittag; - cell increment,multiplier; - constvalue *enumroot; - symbol *enumsym; - - /* get an explicit tag, if any (we need to remember whether an explicit - * tag was passed, even if that explicit tag was "_:", so we cannot call - * pc_addtag() here - */ - if (lex(&val,&str)==tLABEL) { - tag=pc_addtag(str); - explicittag=TRUE; - } else { - lexpush(); - tag=0; - explicittag=FALSE; - } /* if */ - - /* get optional enum name (also serves as a tag if no explicit tag was set) */ - if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */ - strcpy(enumname,str); /* save enum name (last constant) */ - if (!explicittag) - tag=pc_addtag(enumname); - } else { - lexpush(); /* analyze again */ - enumname[0]='\0'; - } /* if */ - - /* get increment and multiplier */ - increment=1; - multiplier=1; - if (matchtoken('(')) { - if (matchtoken(taADD)) { - constexpr(&increment,NULL,NULL); - } else if (matchtoken(taMULT)) { - constexpr(&multiplier,NULL,NULL); - } else if (matchtoken(taSHL)) { - constexpr(&val,NULL,NULL); - while (val-->0) - multiplier*=2; - } /* if */ - needtoken(')'); - } /* if */ - - if (strlen(enumname)>0) { - /* already create the root symbol, so the fields can have it as their "parent" */ - enumsym=add_constant(enumname,0,vclass,tag); - if (enumsym!=NULL) - enumsym->usage |= uENUMROOT; - /* start a new list for the element names */ - if ((enumroot=(constvalue*)malloc(sizeof(constvalue)))==NULL) - error(103); /* insufficient memory (fatal error) */ - memset(enumroot,0,sizeof(constvalue)); - } else { - enumsym=NULL; - enumroot=NULL; - } /* if */ - - needtoken('{'); - /* go through all constants */ - value=0; /* default starting value */ - do { - int idxtag,fieldtag; - symbol *sym; - if (matchtoken('}')) { /* quick exit if '}' follows ',' */ - lexpush(); - break; - } /* if */ - idxtag=pc_addtag(NULL); /* optional explicit item tag */ - if (needtoken(tSYMBOL)) { /* read in (new) token */ - tokeninfo(&val,&str); /* get the information */ - strcpy(constname,str); /* save symbol name */ - } else { - constname[0]='\0'; - } /* if */ - size=increment; /* default increment of 'val' */ - fieldtag=0; /* default field tag */ - if (matchtoken('[')) { - constexpr(&size,&fieldtag,NULL); /* get size */ - needtoken(']'); - } /* if */ - if (matchtoken('=')) - constexpr(&value,NULL,NULL); /* get value */ - /* add_constant() checks whether a variable (global or local) or - * a constant with the same name already exists - */ - sym=add_constant(constname,value,vclass,tag); - if (sym==NULL) - continue; /* error message already given */ - /* set the item tag and the item size, for use in indexing arrays */ - sym->x.tags.index=idxtag; - sym->x.tags.field=fieldtag; - sym->dim.array.length=size; - sym->dim.array.level=0; - sym->parent=enumsym; - /* add the constant to a separate list as well */ - if (enumroot!=NULL) { - sym->usage |= uENUMFIELD; - append_constval(enumroot,constname,value,tag); - } /* if */ - if (multiplier==1) - value+=size; - else - value*=size*multiplier; - } while (matchtoken(',')); - needtoken('}'); /* terminates the constant list */ - matchtoken(';'); /* eat an optional ; */ - - /* set the enum name to the "next" value (typically the last value plus one) */ - if (enumsym!=NULL) { - assert((enumsym->usage & uENUMROOT)!=0); - enumsym->addr=value; - /* assign the constant list */ - assert(enumroot!=NULL); - enumsym->dim.enumlist=enumroot; - sc_attachdocumentation(enumsym); /* attach any documenation to the enumeration */ - } /* if */ -} - -static int getstates(const char *funcname) -{ - char fsaname[sNAMEMAX+1],statename[sNAMEMAX+1]; - cell val; - char *str; - constvalue *automaton; - constvalue *state; - int fsa,islabel; - int *list; - int count,listsize,state_id; - - if (!matchtoken('<')) - return 0; - if (matchtoken('>')) - return -1; /* special construct: all other states (fall-back) */ - - count=0; - listsize=0; - list=NULL; - fsa=-1; - - do { - if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL)) - break; - tokeninfo(&val,&str); - assert(strlen(str)=0 && automaton->index!=fsa) - error(83,funcname); /* multiple automatons for a single function/variable */ - fsa=automaton->index; - } /* if */ - state=state_add(statename,fsa); - /* add this state to the state combination list (it will be attached to the - * automaton later) */ - state_buildlist(&list,&listsize,&count,(int)state->value); - } while (matchtoken(',')); - needtoken('>'); - - if (count>0) { - assert(automaton!=NULL); - assert(fsa>=0); - state_id=state_addlist(list,count,fsa); - assert(state_id>0); - } else { - /* error is already given */ - state_id=0; - } /* if */ - if (list!=NULL) - free(list); - - return state_id; -} - -static void attachstatelist(symbol *sym, int state_id) -{ - assert(sym!=NULL); - if ((sym->usage & uDEFINE)!=0 && (sym->states==NULL || state_id==0)) - error(21,sym->name); /* function already defined, either without states or the current definition has no states */ - - if (state_id!=0) { - /* add the state list id */ - constvalue *stateptr; - if (sym->states==NULL) { - if ((sym->states=(constvalue*)malloc(sizeof(constvalue)))==NULL) - error(103); /* insufficient memory (fatal error) */ - memset(sym->states,0,sizeof(constvalue)); - } /* if */ - /* see whether the id already exists (add new state only if it does not - * yet exist - */ - assert(sym->states!=NULL); - for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index!=state_id; stateptr=stateptr->next) - /* nothing */; - assert(state_id<=SHRT_MAX); - if (stateptr==NULL) - append_constval(sym->states,"",code_idx,(short)state_id); - else if (stateptr->value==0) - stateptr->value=code_idx; - else - error(84,sym->name); - /* also check for another conflicting situation: a fallback function - * without any states - */ - if (state_id==-1 && sc_status!=statFIRST) { - /* in the second round, all states should have been accumulated */ - assert(sym->states!=NULL); - for (stateptr=sym->states->next; stateptr!=NULL && stateptr->index==-1; stateptr=stateptr->next) - /* nothing */; - if (stateptr==NULL) - error(85,sym->name); /* no states are defined for this function */ - } /* if */ - } /* if */ -} - -/* - * Finds a function in the global symbol table or creates a new entry. - * It does some basic processing and error checking. - */ -SC_FUNC symbol *fetchfunc(char *name,int tag) -{ - symbol *sym; - - if ((sym=findglb(name,sGLOBAL))!=0) { /* already in symbol table? */ - if (sym->ident!=iFUNCTN) { - error(21,name); /* yes, but not as a function */ - return NULL; /* make sure the old symbol is not damaged */ - } else if ((sym->usage & uNATIVE)!=0) { - error(21,name); /* yes, and it is a native */ - } /* if */ - assert(sym->vclass==sGLOBAL); - if ((sym->usage & uPROTOTYPED)!=0 && sym->tag!=tag) - error(25); /* mismatch from earlier prototype */ - if ((sym->usage & uDEFINE)==0) { - /* as long as the function stays undefined, update the address and the tag */ - if (sym->states==NULL) - sym->addr=code_idx; - sym->tag=tag; - } /* if */ - } else { - /* don't set the "uDEFINE" flag; it may be a prototype */ - sym=addsym(name,code_idx,iFUNCTN,sGLOBAL,tag,0); - assert(sym!=NULL); /* fatal error 103 must be given on error */ - /* assume no arguments */ - sym->dim.arglist=(arginfo*)malloc(1*sizeof(arginfo)); - sym->dim.arglist[0].ident=0; - /* set library ID to NULL (only for native functions) */ - sym->x.lib=NULL; - /* set the required stack size to zero (only for non-native functions) */ - sym->x.stacksize=1; /* 1 for PROC opcode */ - } /* if */ - if (pc_depricate!=NULL) { - assert(sym!=NULL); - sym->flags|=flgDEPRICATED; - if (sc_status==statWRITE) { - if (sym->documentation!=NULL) { - free(sym->documentation); - sym->documentation=NULL; - } /* if */ - sym->documentation=pc_depricate; - } else { - free(pc_depricate); - } /* if */ - pc_depricate=NULL; - } /* if */ - - return sym; -} - -/* This routine adds symbolic information for each argument. - */ -static void define_args(void) -{ - symbol *sym; - - /* At this point, no local variables have been declared. All - * local symbols are function arguments. - */ - sym=loctab.next; - while (sym!=NULL) { - assert(sym->ident!=iLABEL); - assert(sym->vclass==sLOCAL); - markexpr(sLDECL,sym->name,sym->addr); /* mark for better optimization */ - sym=sym->next; - } /* while */ -} - -static int operatorname(char *name) -{ - int opertok; - char *str; - cell val; - - assert(name!=NULL); - - /* check the operator */ - opertok=lex(&val,&str); - switch (opertok) { - case '+': - case '-': - case '*': - case '/': - case '%': - case '>': - case '<': - case '!': - case '~': - case '=': - name[0]=(char)opertok; - name[1]='\0'; - break; - case tINC: - strcpy(name,"++"); - break; - case tDEC: - strcpy(name,"--"); - break; - case tlEQ: - strcpy(name,"=="); - break; - case tlNE: - strcpy(name,"!="); - break; - case tlLE: - strcpy(name,"<="); - break; - case tlGE: - strcpy(name,">="); - break; - default: - name[0]='\0'; - error(7); /* operator cannot be redefined (or bad operator name) */ - return 0; - } /* switch */ - - return opertok; -} - -static int operatoradjust(int opertok,symbol *sym,char *opername,int resulttag) -{ - int tags[2]={0,0}; - int count=0; - arginfo *arg; - char tmpname[sNAMEMAX+1]; - symbol *oldsym; - - if (opertok==0) - return TRUE; - - assert(sym!=NULL && sym->ident==iFUNCTN && sym->dim.arglist!=NULL); - /* count arguments and save (first two) tags */ - while (arg=&sym->dim.arglist[count], arg->ident!=0) { - if (count<2) { - if (arg->numtags>1) - error(65,count+1); /* function argument may only have a single tag */ - else if (arg->numtags==1) - tags[count]=arg->tags[0]; - } /* if */ - if (opertok=='~' && count==0) { - if (arg->ident!=iREFARRAY) - error(73,arg->name);/* must be an array argument */ - } else { - if (arg->ident!=iVARIABLE) - error(66,arg->name);/* must be non-reference argument */ - } /* if */ - if (arg->hasdefault) - error(59,arg->name); /* arguments of an operator may not have a default value */ - count++; - } /* while */ - - /* for '!', '++' and '--', count must be 1 - * for '-', count may be 1 or 2 - * for '=', count must be 1, and the resulttag is also important - * for all other (binary) operators and the special '~' operator, count must be 2 - */ - switch (opertok) { - case '!': - case '=': - case tINC: - case tDEC: - if (count!=1) - error(62); /* number or placement of the operands does not fit the operator */ - break; - case '-': - if (count!=1 && count!=2) - error(62); /* number or placement of the operands does not fit the operator */ - break; - default: - if (count!=2) - error(62); /* number or placement of the operands does not fit the operator */ - } /* switch */ - - if (tags[0]==0 && (opertok!='=' && tags[1]==0 || opertok=='=' && resulttag==0)) - error(64); /* cannot change predefined operators */ - - /* change the operator name */ - assert(strlen(opername)>0); - operator_symname(tmpname,opername,tags[0],tags[1],count,resulttag); - if ((oldsym=findglb(tmpname,sGLOBAL))!=NULL) { - int i; - if ((oldsym->usage & uDEFINE)!=0) { - char errname[2*sNAMEMAX+16]; - funcdisplayname(errname,tmpname); - error(21,errname); /* symbol already defined */ - } /* if */ - sym->usage|=oldsym->usage; /* copy flags from the previous definition */ - for (i=0; inumrefers; i++) - if (oldsym->refer[i]!=NULL) - refer_symbol(sym,oldsym->refer[i]); - delete_symbol(&glbtab,oldsym); - } /* if */ - strcpy(sym->name,tmpname); - sym->hash=namehash(sym->name);/* calculate new hash */ - - /* operators should return a value, except the '~' operator */ - if (opertok!='~') - sym->usage |= uRETVALUE; - - return TRUE; -} - -static int check_operatortag(int opertok,int resulttag,char *opername) -{ - assert(opername!=NULL && strlen(opername)>0); - switch (opertok) { - case '!': - case '<': - case '>': - case tlEQ: - case tlNE: - case tlLE: - case tlGE: - if (resulttag!=pc_addtag("bool")) { - error(63,opername,"bool:"); /* operator X requires a "bool:" result tag */ - return FALSE; - } /* if */ - break; - case '~': - if (resulttag!=0) { - error(63,opername,"_:"); /* operator "~" requires a "_:" result tag */ - return FALSE; - } /* if */ - break; - } /* switch */ - return TRUE; -} - -static char *tag2str(char *dest,int tag) -{ - tag &= TAGMASK; - assert(tag>=0); - sprintf(dest,"0%x",tag); - return isdigit(dest[1]) ? &dest[1] : dest; -} - -SC_FUNC char *operator_symname(char *symname,char *opername,int tag1,int tag2,int numtags,int resulttag) -{ - char tagstr1[10], tagstr2[10]; - int opertok; - - assert(numtags>=1 && numtags<=2); - opertok= (opername[1]=='\0') ? opername[0] : 0; - if (opertok=='=') - sprintf(symname,"%s%s%s",tag2str(tagstr1,resulttag),opername,tag2str(tagstr2,tag1)); - else if (numtags==1 || opertok=='~') - sprintf(symname,"%s%s",opername,tag2str(tagstr1,tag1)); - else - sprintf(symname,"%s%s%s",tag2str(tagstr1,tag1),opername,tag2str(tagstr2,tag2)); - return symname; -} - -static int parse_funcname(char *fname,int *tag1,int *tag2,char *opname) -{ - char *ptr,*name; - int unary; - - /* tags are only positive, so if the function name starts with a '-', - * the operator is an unary '-' or '--' operator. - */ - if (*fname=='-') { - *tag1=0; - unary=TRUE; - ptr=fname; - } else { - *tag1=(int)strtol(fname,&ptr,16); - unary= ptr==fname; /* unary operator if it doesn't start with a tag name */ - } /* if */ - assert(!unary || *tag1==0); - assert(*ptr!='\0'); - for (name=opname; !isdigit(*ptr); ) - *name++ = *ptr++; - *name='\0'; - *tag2=(int)strtol(ptr,NULL,16); - return unary; -} - -static constvalue *find_tag_byval(int tag) -{ - constvalue *tagsym; - tagsym=find_constval_byval(&tagname_tab,tag & ~PUBLICTAG); - if (tagsym==NULL) - tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG); - return tagsym; -} - -SC_FUNC char *funcdisplayname(char *dest,char *funcname) -{ - int tags[2]; - char opname[10]; - constvalue *tagsym[2]; - int unary; - - if (isalpha(*funcname) || *funcname=='_' || *funcname==PUBLIC_CHAR || *funcname=='\0') { - if (dest!=funcname) - strcpy(dest,funcname); - return dest; - } /* if */ - - unary=parse_funcname(funcname,&tags[0],&tags[1],opname); - tagsym[1]=find_tag_byval(tags[1]); - assert(tagsym[1]!=NULL); - if (unary) { - sprintf(dest,"operator%s(%s:)",opname,tagsym[1]->name); - } else { - tagsym[0]=find_tag_byval(tags[0]); - assert(tagsym[0]!=NULL); - /* special case: the assignment operator has the return value as the 2nd tag */ - if (opname[0]=='=' && opname[1]=='\0') - sprintf(dest,"%s:operator%s(%s:)",tagsym[0]->name,opname,tagsym[1]->name); - else - sprintf(dest,"operator%s(%s:,%s:)",opname,tagsym[0]->name,tagsym[1]->name); - } /* if */ - return dest; -} - -static void funcstub(int fnative) -{ - int tok,tag,fpublic; - char *str; - cell val,size; - char symbolname[sNAMEMAX+1]; - int idxtag[sDIMEN_MAX]; - int dim[sDIMEN_MAX]; - int numdim; - symbol *sym,*sub; - int opertok; - - opertok=0; - lastst=0; - litidx=0; /* clear the literal pool */ - assert(loctab.next==NULL); /* local symbol table should be empty */ - - tag=pc_addtag(NULL); /* get the tag of the return value */ - numdim=0; - while (matchtoken('[')) { - /* the function returns an array, get this tag for the index and the array - * dimensions - */ - if (numdim == sDIMEN_MAX) { - error(53); /* exceeding maximum number of dimensions */ - return; - } /* if */ - size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */ - if (size==0) - error(9); /* invalid array size */ - #if INT_MAX < LONG_MAX - if (size > INT_MAX) - error(105); /* overflow, exceeding capacity */ - #endif - dim[numdim++]=(int)size; - } /* while */ - - tok=lex(&val,&str); - fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR); - if (fnative) { - if (fpublic || tok==tSTOCK || tok==tSTATIC || tok==tSYMBOL && *str==PUBLIC_CHAR) - error(42); /* invalid combination of class specifiers */ - } else { - if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC) - tok=lex(&val,&str); - } /* if */ - - if (tok==tOPERATOR) { - opertok=operatorname(symbolname); - if (opertok==0) - return; /* error message already given */ - check_operatortag(opertok,tag,symbolname); - } else { - if (tok!=tSYMBOL && freading) { - error(10); /* illegal function or declaration */ - return; - } /* if */ - strcpy(symbolname,str); - } /* if */ - needtoken('('); /* only functions may be native/forward */ - - sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ - if (sym==NULL) - return; - if (fnative) { - sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); - sym->x.lib=curlibrary; - } else if (fpublic) { - sym->usage|=uPUBLIC; - } /* if */ - sym->usage|=uFORWARD; - - declargs(sym,FALSE); - /* "declargs()" found the ")" */ - sc_attachdocumentation(sym); /* attach any documenation to the function */ - if (!operatoradjust(opertok,sym,symbolname,tag)) - sym->usage &= ~uDEFINE; - - if (getstates(symbolname)!=0) { - if (fnative || opertok!=0) - error(82); /* native functions and operators may not have states */ - else - error(231); /* ignoring state specifications on forward declarations */ - } /* if */ - - /* for a native operator, also need to specify an "exported" function name; - * for a native function, this is optional - */ - if (fnative) { - if (opertok!=0) { - needtoken('='); - lexpush(); /* push back, for matchtoken() to retrieve again */ - } /* if */ - if (matchtoken('=')) { - /* allow number or symbol */ - if (matchtoken(tSYMBOL)) { - tokeninfo(&val,&str); - insert_alias(sym->name,str); - } else { - constexpr(&val,NULL,NULL); - sym->addr=val; - /* At the moment, I have assumed that this syntax is only valid if - * val < 0. To properly mix "normal" native functions and indexed - * native functions, one should use negative indices anyway. - * Special code for a negative index in sym->addr exists in SC4.C - * (ffcall()) and in SC6.C (the loops for counting the number of native - * variables and for writing them). - */ - } /* if */ - } /* if */ - } /* if */ - needtoken(tTERM); - - /* attach the array to the function symbol */ - if (numdim>0) { - assert(sym!=NULL); - sub=addvariable(symbolname,0,iARRAY,sGLOBAL,tag,dim,numdim,idxtag); - sub->parent=sym; - } /* if */ - - litidx=0; /* clear the literal pool */ - delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */ -} - -/* newfunc - begin a function - * - * This routine is called from "parse" and tries to make a function - * out of the following text - * - * Global references: funcstatus,lastst,litidx - * rettype (altered) - * curfunc (altered) - * declared (altered) - * glb_declared (altered) - * sc_alignnext (altered) - */ -static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock) -{ - symbol *sym; - int argcnt,tok,tag,funcline; - int opertok,opererror; - char symbolname[sNAMEMAX+1]; - char *str; - cell val,cidx,glbdecl; - short filenum; - int state_id; - - assert(litidx==0); /* literal queue should be empty */ - litidx=0; /* clear the literal pool (should already be empty) */ - opertok=0; - lastst=0; /* no statement yet */ - cidx=0; /* just to avoid compiler warnings */ - glbdecl=0; - assert(loctab.next==NULL); /* local symbol table should be empty */ - filenum=fcurrent; /* save file number at the start of the declaration */ - - if (firstname!=NULL) { - assert(strlen(firstname)<=sNAMEMAX); - strcpy(symbolname,firstname); /* save symbol name */ - tag=firsttag; - } else { - tag= (firsttag>=0) ? firsttag : pc_addtag(NULL); - tok=lex(&val,&str); - assert(!fpublic); - if (tok==tNATIVE || tok==tPUBLIC && stock) - error(42); /* invalid combination of class specifiers */ - if (tok==tOPERATOR) { - opertok=operatorname(symbolname); - if (opertok==0) - return TRUE; /* error message already given */ - check_operatortag(opertok,tag,symbolname); - } else { - if (tok!=tSYMBOL && freading) { - error(20,str); /* invalid symbol name */ - return FALSE; - } /* if */ - assert(strlen(str)<=sNAMEMAX); - strcpy(symbolname,str); - } /* if */ - } /* if */ - /* check whether this is a function or a variable declaration */ - if (!matchtoken('(')) - return FALSE; - /* so it is a function, proceed */ - funcline=fline; /* save line at which the function is defined */ - if (symbolname[0]==PUBLIC_CHAR) { - fpublic=TRUE; /* implicitly public function */ - if (stock) - error(42); /* invalid combination of class specifiers */ - } /* if */ - sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ - if (sym==NULL || (sym->usage & uNATIVE)!=0) - return TRUE; /* it was recognized as a function declaration, but not as a valid one */ - if (fpublic) - sym->usage|=uPUBLIC; - if (fstatic) - sym->fnumber=filenum; - /* if the function was used before being declared, and it has a tag for the - * result, add a third pass (as second "skimming" parse) because the function - * result may have been used with user-defined operators, which have now - * been incorrectly flagged (as the return tag was unknown at the time of - * the call) - */ - if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) { - int curstatus=sc_status; - sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */ - error(208); - sc_status=curstatus; - sc_reparse=TRUE; /* must add another pass to "initial scan" phase */ - } /* if */ - /* we want public functions to be explicitly prototyped, as they are called - * from the outside - */ - if (fpublic && (sym->usage & uFORWARD)==0) - error(235,symbolname); - /* declare all arguments */ - argcnt=declargs(sym,TRUE); - opererror=!operatoradjust(opertok,sym,symbolname,tag); - if (strcmp(symbolname,uMAINFUNC)==0 || strcmp(symbolname,uENTRYFUNC)==0) { - if (argcnt>0) - error(5); /* "main()" and "entry()" functions may not have any arguments */ - sym->usage|=uREAD; /* "main()" is the program's entry point: always used */ - } /* if */ - state_id=getstates(symbolname); - if (state_id>0 && (opertok!=0 || strcmp(symbolname,uMAINFUNC)==0)) - error(82); /* operators may not have states, main() may neither */ - attachstatelist(sym,state_id); - /* "declargs()" found the ")"; if a ";" appears after this, it was a - * prototype */ - if (matchtoken(';')) { - sym->usage|=uFORWARD; - if (!sc_needsemicolon) - error(218); /* old style prototypes used with optional semicolumns */ - delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */ - return TRUE; - } /* if */ - /* so it is not a prototype, proceed */ - /* if this is a function that is not referred to (this can only be detected - * in the second stage), shut code generation off */ - if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) { - sc_status=statSKIP; - cidx=code_idx; - glbdecl=glb_declared; - } /* if */ - if ((sym->flags & flgDEPRICATED)!=0) { - char *ptr= (sym->documentation!=NULL) ? sym->documentation : ""; - error(234,symbolname,ptr); /* depricated (probably a public function) */ - } /* if */ - begcseg(); - sym->usage|=uDEFINE; /* set the definition flag */ - if (stock) - sym->usage|=uSTOCK; - if (opertok!=0 && opererror) - sym->usage &= ~uDEFINE; - /* if the function has states, dump the label to the start of the function */ - if (state_id!=0) { - constvalue *ptr=sym->states->next; - while (ptr!=NULL) { - assert(sc_status!=statWRITE || strlen(ptr->name)>0); - if (ptr->index==state_id) { - setlabel((int)strtol(ptr->name,NULL,16)); - break; - } /* if */ - ptr=ptr->next; - } /* while */ - } /* if */ - startfunc(sym->name); /* creates stack frame */ - insert_dbgline(funcline); - setline(FALSE); - if (sc_alignnext) { - alignframe(sc_dataalign); - sc_alignnext=FALSE; - } /* if */ - declared=0; /* number of local cells */ - rettype=(sym->usage & uRETVALUE); /* set "return type" variable */ - curfunc=sym; - define_args(); /* add the symbolic info for the function arguments */ - #if !defined SC_LIGHT - if (matchtoken('{')) { - lexpush(); - } else { - /* Insert a separator so that comments following the statement will not - * be attached to this function; they should be attached to the next - * function. This is not a problem for functions having a compound block, - * because the closing brace is an explicit "end token" for the function. - * With single statement functions, the preprocessor may overread the - * source code before the parser determines an "end of statement". - */ - insert_docstring_separator(); - } /* if */ - #endif - sc_curstates=state_id;/* set state id, for accessing global state variables */ - statement(NULL,FALSE); - sc_curstates=0; - if ((rettype & uRETVALUE)!=0) - sym->usage|=uRETVALUE; - if (declared!=0) { - /* This happens only in a very special (and useless) case, where a function - * has only a single statement in its body (no compound block) and that - * statement declares a new variable - */ - modstk((int)declared*sizeof(cell)); /* remove all local variables */ - declared=0; - } /* if */ - if ((lastst!=tRETURN) && (lastst!=tGOTO)){ - ldconst(0,sPRI); - ffret(strcmp(sym->name,uENTRYFUNC)!=0); - if ((sym->usage & uRETVALUE)!=0) { - char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ - funcdisplayname(symname,sym->name); - error(209,symname); /* function should return a value */ - } /* if */ - } /* if */ - endfunc(); - sym->codeaddr=code_idx; - sc_attachdocumentation(sym); /* attach collected documenation to the function */ - if (litidx) { /* if there are literals defined */ - glb_declared+=litidx; - begdseg(); /* flip to DATA segment */ - dumplits(); /* dump literal strings */ - litidx=0; - } /* if */ - testsymbols(&loctab,0,TRUE,TRUE); /* test for unused arguments and labels */ - delete_symbols(&loctab,0,TRUE,TRUE); /* clear local variables queue */ - assert(loctab.next==NULL); - curfunc=NULL; - if (sc_status==statSKIP) { - sc_status=statWRITE; - code_idx=cidx; - glb_declared=glbdecl; - } /* if */ - return TRUE; -} - -static int argcompare(arginfo *a1,arginfo *a2) -{ - int result,level,i; - - result= strcmp(a1->name,a2->name)==0; /* name */ - if (result) - result= a1->ident==a2->ident; /* type/class */ - if (result) - result= a1->usage==a2->usage; /* "const" flag */ - if (result) - result= a1->numtags==a2->numtags; /* tags (number and names) */ - for (i=0; result && inumtags; i++) - result= a1->tags[i]==a2->tags[i]; - if (result) - result= a1->numdim==a2->numdim; /* array dimensions & index tags */ - for (level=0; result && levelnumdim; level++) - result= a1->dim[level]==a2->dim[level]; - for (level=0; result && levelnumdim; level++) - result= a1->idxtag[level]==a2->idxtag[level]; - if (result) - result= a1->hasdefault==a2->hasdefault; /* availability of default value */ - if (a1->hasdefault) { - if (a1->ident==iREFARRAY) { - if (result) - result= a1->defvalue.array.size==a2->defvalue.array.size; - if (result) - result= a1->defvalue.array.arraysize==a2->defvalue.array.arraysize; - /* ??? should also check contents of the default array (these troubles - * go away in a 2-pass compiler that forbids double declarations, but - * Pawn currently does not forbid them) */ - } else { - if (result) { - if ((a1->hasdefault & uSIZEOF)!=0 || (a1->hasdefault & uTAGOF)!=0) - result= a1->hasdefault==a2->hasdefault - && strcmp(a1->defvalue.size.symname,a2->defvalue.size.symname)==0 - && a1->defvalue.size.level==a2->defvalue.size.level; - else - result= a1->defvalue.val==a2->defvalue.val; - } /* if */ - } /* if */ - if (result) - result= a1->defvalue_tag==a2->defvalue_tag; - } /* if */ - return result; -} - -/* declargs() - * - * This routine adds an entry in the local symbol table for each argument - * found in the argument list. It returns the number of arguments. - */ -static int declargs(symbol *sym,int chkshadow) -{ - #define MAXTAGS 16 - char *ptr; - int argcnt,oldargcnt,tok,tags[MAXTAGS],numtags; - cell val; - arginfo arg, *arglist; - char name[sNAMEMAX+1]; - int ident,fpublic,fconst; - int idx; - - /* if the function is already defined earlier, get the number of arguments - * of the existing definition - */ - oldargcnt=0; - if ((sym->usage & uPROTOTYPED)!=0) - while (sym->dim.arglist[oldargcnt].ident!=0) - oldargcnt++; - argcnt=0; /* zero aruments up to now */ - ident=iVARIABLE; - numtags=0; - fconst=FALSE; - fpublic= (sym->usage & uPUBLIC)!=0; - /* the '(' parantheses has already been parsed */ - if (!matchtoken(')')){ - do { /* there are arguments; process them */ - /* any legal name increases argument count (and stack offset) */ - tok=lex(&val,&ptr); - switch (tok) { - case 0: - /* nothing */ - break; - case '&': - if (ident!=iVARIABLE || numtags>0) - error(1,"-identifier-","&"); - ident=iREFERENCE; - break; - case tCONST: - if (ident!=iVARIABLE || numtags>0) - error(1,"-identifier-","const"); - fconst=TRUE; - break; - case tLABEL: - if (numtags>0) - error(1,"-identifier-","-tagname-"); - tags[0]=pc_addtag(ptr); - numtags=1; - break; - case '{': - if (numtags>0) - error(1,"-identifier-","-tagname-"); - numtags=0; - while (numtags=sMAXARGS) - error(45); /* too many function arguments */ - strcpy(name,ptr); /* save symbol name */ - if (name[0]==PUBLIC_CHAR) - error(56,name); /* function arguments cannot be public */ - if (numtags==0) - tags[numtags++]=0; /* default tag */ - /* Stack layout: - * base + 0*sizeof(cell) == previous "base" - * base + 1*sizeof(cell) == function return address - * base + 2*sizeof(cell) == number of arguments - * base + 3*sizeof(cell) == first argument of the function - * So the offset of each argument is "(argcnt+3) * sizeof(cell)". - */ - doarg(name,ident,(argcnt+3)*sizeof(cell),tags,numtags,fpublic,fconst,chkshadow,&arg); - if (fpublic && arg.hasdefault) - error(59,name); /* arguments of a public function may not have a default value */ - if ((sym->usage & uPROTOTYPED)==0) { - /* redimension the argument list, add the entry */ - sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo)); - if (sym->dim.arglist==0) - error(103); /* insufficient memory */ - memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */ - sym->dim.arglist[argcnt]=arg; - } else { - /* check the argument with the earlier definition */ - if (argcnt>oldargcnt || !argcompare(&sym->dim.arglist[argcnt],&arg)) - error(25); /* function definition does not match prototype */ - /* may need to free default array argument and the tag list */ - if (arg.ident==iREFARRAY && arg.hasdefault) - free(arg.defvalue.array.data); - else if (arg.ident==iVARIABLE - && ((arg.hasdefault & uSIZEOF)!=0 || (arg.hasdefault & uTAGOF)!=0)) - free(arg.defvalue.size.symname); - free(arg.tags); - } /* if */ - argcnt++; - ident=iVARIABLE; - numtags=0; - fconst=FALSE; - break; - case tELLIPS: - if (ident!=iVARIABLE) - error(10); /* illegal function or declaration */ - if (numtags==0) - tags[numtags++]=0; /* default tag */ - if ((sym->usage & uPROTOTYPED)==0) { - /* redimension the argument list, add the entry iVARARGS */ - sym->dim.arglist=(arginfo*)realloc(sym->dim.arglist,(argcnt+2)*sizeof(arginfo)); - if (sym->dim.arglist==0) - error(103); /* insufficient memory */ - memset(&sym->dim.arglist[argcnt+1],0,sizeof(arginfo)); /* keep the list terminated */ - sym->dim.arglist[argcnt].ident=iVARARGS; - sym->dim.arglist[argcnt].hasdefault=FALSE; - sym->dim.arglist[argcnt].defvalue.val=0; - sym->dim.arglist[argcnt].defvalue_tag=0; - sym->dim.arglist[argcnt].numtags=numtags; - sym->dim.arglist[argcnt].tags=(int*)malloc(numtags*sizeof tags[0]); - if (sym->dim.arglist[argcnt].tags==NULL) - error(103); /* insufficient memory */ - memcpy(sym->dim.arglist[argcnt].tags,tags,numtags*sizeof tags[0]); - } else { - if (argcnt>oldargcnt || sym->dim.arglist[argcnt].ident!=iVARARGS) - error(25); /* function definition does not match prototype */ - } /* if */ - argcnt++; - break; - default: - error(10); /* illegal function or declaration */ - } /* switch */ - } while (tok=='&' || tok==tLABEL || tok==tCONST - || tok!=tELLIPS && matchtoken(',')); /* more? */ - /* if the next token is not ",", it should be ")" */ - needtoken(')'); - } /* if */ - /* resolve any "sizeof" arguments (now that all arguments are known) */ - assert(sym->dim.arglist!=NULL); - arglist=sym->dim.arglist; - for (idx=0; idx=argcnt) { - error(17,ptr); /* undefined symbol */ - } else { - assert(arglist[idx].defvalue.size.symname!=NULL); - /* check the level against the number of dimensions */ - if (arglist[idx].defvalue.size.level>0 - && arglist[idx].defvalue.size.level>=arglist[altidx].numdim) - error(28,arglist[idx].name); /* invalid subscript */ - /* check the type of the argument whose size to take; for a iVARIABLE - * or a iREFERENCE, this is always 1 (so the code is redundant) - */ - assert(arglist[altidx].ident!=iVARARGS); - if (arglist[altidx].ident!=iREFARRAY && (arglist[idx].hasdefault & uSIZEOF)!=0) { - if ((arglist[idx].hasdefault & uTAGOF)!=0) { - error(81,arglist[idx].name); /* cannot take "tagof" an indexed array */ - } else { - assert(arglist[altidx].ident==iVARIABLE || arglist[altidx].ident==iREFERENCE); - error(223,ptr); /* redundant sizeof */ - } /* if */ - } /* if */ - } /* if */ - } /* if */ - } /* for */ - - sym->usage|=uPROTOTYPED; - errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/ - return argcnt; -} - -/* doarg - declare one argument type - * - * this routine is called from "declargs()" and adds an entry in the local - * symbol table for one argument. - * - * "fpublic" indicates whether the function for this argument list is public. - * The arguments themselves are never public. - */ -static void doarg(char *name,int ident,int offset,int tags[],int numtags, - int fpublic,int fconst,int chkshadow,arginfo *arg) -{ - symbol *argsym; - constvalue *enumroot; - cell size; - - strcpy(arg->name,name); - arg->hasdefault=FALSE; /* preset (most common case) */ - arg->defvalue.val=0; /* clear */ - arg->defvalue_tag=0; - arg->numdim=0; - if (matchtoken('[')) { - if (ident==iREFERENCE) - error(67,name); /* illegal declaration ("&name[]" is unsupported) */ - do { - if (arg->numdim == sDIMEN_MAX) { - error(53); /* exceeding maximum number of dimensions */ - return; - } /* if */ - size=needsub(&arg->idxtag[arg->numdim],&enumroot);/* may be zero here, it is a pointer anyway */ - #if INT_MAX < LONG_MAX - if (size > INT_MAX) - error(105); /* overflow, exceeding capacity */ - #endif - arg->dim[arg->numdim]=(int)size; - arg->numdim+=1; - } while (matchtoken('[')); - ident=iREFARRAY; /* "reference to array" (is a pointer) */ - if (matchtoken('=')) { - lexpush(); /* initials() needs the "=" token again */ - assert(litidx==0); /* at the start of a function, this is reset */ - assert(numtags>0); - initials(ident,tags[0],&size,arg->dim,arg->numdim,enumroot); - assert(size>=litidx); - /* allocate memory to hold the initial values */ - arg->defvalue.array.data=(cell *)malloc(litidx*sizeof(cell)); - if (arg->defvalue.array.data!=NULL) { - int i; - memcpy(arg->defvalue.array.data,litq,litidx*sizeof(cell)); - arg->hasdefault=TRUE; /* argument has default value */ - arg->defvalue.array.size=litidx; - arg->defvalue.array.addr=-1; - /* calulate size to reserve on the heap */ - arg->defvalue.array.arraysize=1; - for (i=0; inumdim; i++) - arg->defvalue.array.arraysize*=arg->dim[i]; - if (arg->defvalue.array.arraysize < arg->defvalue.array.size) - arg->defvalue.array.arraysize = arg->defvalue.array.size; - } /* if */ - litidx=0; /* reset */ - } /* if */ - } else { - if (matchtoken('=')) { - unsigned char size_tag_token; - assert(ident==iVARIABLE || ident==iREFERENCE); - arg->hasdefault=TRUE; /* argument has a default value */ - size_tag_token=(unsigned char)(matchtoken(tSIZEOF) ? uSIZEOF : 0); - if (size_tag_token==0) - size_tag_token=(unsigned char)(matchtoken(tTAGOF) ? uTAGOF : 0); - if (size_tag_token!=0) { - int paranthese; - if (ident==iREFERENCE) - error(66,name); /* argument may not be a reference */ - paranthese=0; - while (matchtoken('(')) - paranthese++; - if (needtoken(tSYMBOL)) { - /* save the name of the argument whose size id to take */ - char *name; - cell val; - tokeninfo(&val,&name); - if ((arg->defvalue.size.symname=duplicatestring(name)) == NULL) - error(103); /* insufficient memory */ - arg->defvalue.size.level=0; - if (size_tag_token==uSIZEOF) { - while (matchtoken('[')) { - arg->defvalue.size.level+=(short)1; - needtoken(']'); - } /* while */ - } /* if */ - if (ident==iVARIABLE) /* make sure we set this only if not a reference */ - arg->hasdefault |= size_tag_token; /* uSIZEOF or uTAGOF */ - } /* if */ - while (paranthese--) - needtoken(')'); - } else { - constexpr(&arg->defvalue.val,&arg->defvalue_tag,NULL); - assert(numtags>0); - if (!matchtag(tags[0],arg->defvalue_tag,TRUE)) - error(213); /* tagname mismatch */ - } /* if */ - } /* if */ - } /* if */ - arg->ident=(char)ident; - arg->usage=(char)(fconst ? uCONST : 0); - arg->numtags=numtags; - arg->tags=(int*)malloc(numtags*sizeof tags[0]); - if (arg->tags==NULL) - error(103); /* insufficient memory */ - memcpy(arg->tags,tags,numtags*sizeof tags[0]); - argsym=findloc(name); - if (argsym!=NULL) { - error(21,name); /* symbol already defined */ - } else { - if (chkshadow && (argsym=findglb(name,sSTATEVAR))!=NULL && argsym->ident!=iFUNCTN) - error(219,name); /* variable shadows another symbol */ - /* add details of type and address */ - assert(numtags>0); - argsym=addvariable(name,offset,ident,sLOCAL,tags[0], - arg->dim,arg->numdim,arg->idxtag); - argsym->compound=0; - if (ident==iREFERENCE) - argsym->usage|=uREAD; /* because references are passed back */ - if (fpublic) - argsym->usage|=uREAD; /* arguments of public functions are always "used" */ - if (fconst) - argsym->usage|=uCONST; - } /* if */ -} - -static int count_referrers(symbol *entry) -{ - int i,count; - - count=0; - for (i=0; inumrefers; i++) - if (entry->refer[i]!=NULL) - count++; - return count; -} - -#if !defined SC_LIGHT -static int find_xmltag(char *source,char *xmltag,char *xmlparam,char *xmlvalue, - char **outer_start,int *outer_length, - char **inner_start,int *inner_length) -{ - char *ptr,*inner_end; - int xmltag_len,xmlparam_len,xmlvalue_len; - int match; - - assert(source!=NULL); - assert(xmltag!=NULL); - assert(outer_start!=NULL); - assert(outer_length!=NULL); - assert(inner_start!=NULL); - assert(inner_length!=NULL); - - /* both NULL or both non-NULL */ - assert(xmlvalue!=NULL && xmlparam!=NULL || xmlvalue==NULL && xmlparam==NULL); - - xmltag_len=strlen(xmltag); - xmlparam_len= (xmlparam!=NULL) ? strlen(xmlparam) : 0; - xmlvalue_len= (xmlvalue!=NULL) ? strlen(xmlvalue) : 0; - ptr=source; - /* find an opening '<' */ - while ((ptr=strchr(ptr,'<'))!=NULL) { - *outer_start=ptr; /* be optimistic... */ - match=FALSE; /* ...and pessimistic at the same time */ - ptr++; /* skip '<' */ - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) { - /* xml tag found, optionally check the parameter */ - ptr+=xmltag_len; - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - if (xmlparam!=NULL) { - if (strncmp(ptr,xmlparam,xmlparam_len)==0 && (*(ptr+xmlparam_len)<=' ' || *(ptr+xmlparam_len)=='=')) { - ptr+=xmlparam_len; - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - if (*ptr=='=') { - ptr++; /* skip '=' */ - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - if (*ptr=='"' || *ptr=='\'') - ptr++; /* skip " or ' */ - assert(xmlvalue!=NULL); - if (strncmp(ptr,xmlvalue,xmlvalue_len)==0 - && (*(ptr+xmlvalue_len)<=' ' - || *(ptr+xmlvalue_len)=='>' - || *(ptr+xmlvalue_len)=='"' - || *(ptr+xmlvalue_len)=='\'')) - match=TRUE; /* found it */ - } /* if */ - } /* if */ - } else { - match=TRUE; /* don't check the parameter */ - } /* if */ - } /* if */ - if (match) { - /* now find the end of the opening tag */ - while (*ptr!='\0' && *ptr!='>') - ptr++; - if (*ptr=='>') - ptr++; - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - *inner_start=ptr; - /* find the start of the closing tag (assume no nesting) */ - while ((ptr=strchr(ptr,'<'))!=NULL) { - inner_end=ptr; - ptr++; /* skip '<' */ - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - if (*ptr=='/') { - ptr++; /* skip / */ - while (*ptr!='\0' && *ptr<=' ') - ptr++; /* skip white space */ - if (strncmp(ptr,xmltag,xmltag_len)==0 && (*(ptr+xmltag_len)<=' ' || *(ptr+xmltag_len)=='>')) { - /* find the end of the closing tag */ - while (*ptr!='\0' && *ptr!='>') - ptr++; - if (*ptr=='>') - ptr++; - /* set the lengths of the inner and outer segment */ - assert(*inner_start!=NULL); - *inner_length=(int)(inner_end-*inner_start); - assert(*outer_start!=NULL); - *outer_length=(int)(ptr-*outer_start); - break; /* break out of the loop */ - } /* if */ - } /* if */ - } /* while */ - return TRUE; - } /* if */ - } /* while */ - return FALSE; /* not found */ -} - -static char *xmlencode(char *dest,char *source) -{ - char temp[2*sNAMEMAX+20],*ptr; - - /* replace < by < and such; normally, such a symbol occurs at most once in - * a symbol name (e.g. "operator<") - */ - ptr=temp; - while (*source!='\0') { - switch (*source) { - case '<': - strcpy(ptr,"<"); - ptr+=4; - break; - case '>': - strcpy(ptr,">"); - ptr+=4; - break; - case '&': - strcpy(ptr,"&"); - ptr+=5; - break; - default: - *ptr++=*source; - } /* switch */ - source++; - } /* while */ - *ptr='\0'; - strcpy(dest,temp); - return dest; -} - -static void make_report(symbol *root,FILE *log,char *sourcefile) -{ - char symname[_MAX_PATH]; - int i,arg; - symbol *sym,*ref; - constvalue *tagsym; - constvalue *enumroot; - char *ptr; - - /* adapt the installation directory */ - strcpy(symname,sc_rootpath); - #if DIRSEP_CHAR=='\\' - while ((ptr=strchr(symname,':'))!=NULL) - *ptr='|'; - while ((ptr=strchr(symname,DIRSEP_CHAR))!=NULL) - *ptr='/'; - #endif - - /* the XML header */ - fprintf(log,"\n"); - fprintf(log,"\n",symname); - fprintf(log,"\n",sourcefile); - ptr=strrchr(sourcefile,DIRSEP_CHAR); - if (ptr!=NULL) - ptr++; - else - ptr=sourcefile; - fprintf(log,"\t\n\t\t%s\n\t\n",ptr); - - /* attach the global documentation, if any */ - if (sc_documentation!=NULL) { - fprintf(log,"\n\t\n"); - fprintf(log,"\t\n\t\t"); - fputs(sc_documentation,log); - fprintf(log,"\n\t\n\n"); - } /* if */ - - /* use multiple passes to print constants variables and functions in - * separate sections - */ - fprintf(log,"\t\n"); - - fprintf(log,"\n\t\t\n"); - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->parent!=NULL) - continue; /* hierarchical data type */ - assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE - || sym->ident==iARRAY || sym->ident==iFUNCTN); - if (sym->ident!=iCONSTEXPR || (sym->usage & uENUMROOT)==0) - continue; - if ((sym->usage & uREAD)==0) - continue; - fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name),sym->addr); - if (sym->tag!=0) { - tagsym=find_tag_byval(sym->tag); - assert(tagsym!=NULL); - fprintf(log,"\t\t\t\n",tagsym->name); - } /* if */ - /* browse through all fields */ - if ((enumroot=sym->dim.enumlist)!=NULL) { - enumroot=enumroot->next; /* skip root */ - while (enumroot!=NULL) { - fprintf(log,"\t\t\t\n",funcdisplayname(symname,enumroot->name),enumroot->value); - /* find the constant with this name and get the tag */ - ref=findglb(enumroot->name,sGLOBAL); - if (ref!=NULL) { - if (ref->x.tags.index!=0) { - tagsym=find_tag_byval(ref->x.tags.index); - assert(tagsym!=NULL); - fprintf(log,"\t\t\t\t\n",tagsym->name); - } /* if */ - if (ref->dim.array.length!=1) - fprintf(log,"\t\t\t\t\n",(long)ref->dim.array.length); - } /* if */ - fprintf(log,"\t\t\t\n"); - enumroot=enumroot->next; - } /* while */ - } /* if */ - assert(sym->refer!=NULL); - for (i=0; inumrefers; i++) { - if ((ref=sym->refer[i])!=NULL) - fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); - } /* for */ - if (sym->documentation!=NULL) - fprintf(log,"\t\t\t%s\n",sym->documentation); - fprintf(log,"\t\t\n"); - } /* for */ - - fprintf(log,"\n\t\t\n"); - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->parent!=NULL) - continue; /* hierarchical data type */ - assert(sym->ident==iCONSTEXPR || sym->ident==iVARIABLE - || sym->ident==iARRAY || sym->ident==iFUNCTN); - if (sym->ident!=iCONSTEXPR) - continue; - if ((sym->usage & uREAD)==0 || (sym->usage & (uENUMFIELD | uENUMROOT))!=0) - continue; - fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name),sym->addr); - if (sym->tag!=0) { - tagsym=find_tag_byval(sym->tag); - assert(tagsym!=NULL); - fprintf(log,"\t\t\t\n",tagsym->name); - } /* if */ - assert(sym->refer!=NULL); - for (i=0; inumrefers; i++) { - if ((ref=sym->refer[i])!=NULL) - fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); - } /* for */ - if (sym->documentation!=NULL) - fprintf(log,"\t\t\t%s\n",sym->documentation); - fprintf(log,"\t\t\n"); - } /* for */ - - fprintf(log,"\n\t\t\n"); - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->parent!=NULL) - continue; /* hierarchical data type */ - if (sym->ident!=iVARIABLE && sym->ident!=iARRAY) - continue; - fprintf(log,"\t\t\n",funcdisplayname(symname,sym->name)); - if (sym->tag!=0) { - tagsym=find_tag_byval(sym->tag); - assert(tagsym!=NULL); - fprintf(log,"\t\t\t\n",tagsym->name); - } /* if */ - assert(sym->refer!=NULL); - if ((sym->usage & uPUBLIC)!=0) - fprintf(log,"\t\t\t\n"); - for (i=0; inumrefers; i++) { - if ((ref=sym->refer[i])!=NULL) - fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); - } /* for */ - if (sym->documentation!=NULL) - fprintf(log,"\t\t\t%s\n",sym->documentation); - fprintf(log,"\t\t\n"); - } /* for */ - - fprintf(log,"\n\t\t\n"); - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->parent!=NULL) - continue; /* hierarchical data type */ - if (sym->ident!=iFUNCTN) - continue; - if ((sym->usage & (uREAD | uNATIVE))==uNATIVE) - continue; /* unused native function */ - funcdisplayname(symname,sym->name); - xmlencode(symname,symname); - fprintf(log,"\t\tdim.arglist!=NULL); - for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) { - int dim; - if (arg>0) - fprintf(log,", "); - switch (sym->dim.arglist[arg].ident) { - case iVARIABLE: - fprintf(log,"%s",sym->dim.arglist[arg].name); - break; - case iREFERENCE: - fprintf(log,"&%s",sym->dim.arglist[arg].name); - break; - case iREFARRAY: - fprintf(log,"%s",sym->dim.arglist[arg].name); - for (dim=0; dimdim.arglist[arg].numdim;dim++) - fprintf(log,"[]"); - break; - case iVARARGS: - fprintf(log,"..."); - break; - } /* switch */ - } /* for */ - /* ??? should also print an "array return" size */ - fprintf(log,")\">\n"); - if (sym->tag!=0) { - tagsym=find_tag_byval(sym->tag); - assert(tagsym!=NULL); - fprintf(log,"\t\t\t\n",tagsym->name); - } /* if */ - /* check whether this function is called from the outside */ - if ((sym->usage & uNATIVE)!=0) - fprintf(log,"\t\t\t\n"); - if ((sym->usage & uPUBLIC)!=0) - fprintf(log,"\t\t\t\n"); - if (strcmp(sym->name,uMAINFUNC)==0 || strcmp(sym->name,uENTRYFUNC)==0) - fprintf(log,"\t\t\t\n"); - if ((sym->usage & uNATIVE)==0) - fprintf(log,"\t\t\t\n",(long)sym->x.stacksize); - if (sym->states!=NULL) { - constvalue *stlist=sym->states->next; - assert(stlist!=NULL); /* there should be at least one state item */ - while (stlist!=NULL && stlist->index==-1) - stlist=stlist->next; - assert(stlist!=NULL); /* state id should be found */ - i=state_getfsa(stlist->index); - assert(i>=0); /* automaton 0 exists */ - stlist=automaton_findid(i); - assert(stlist!=NULL); /* automaton should be found */ - fprintf(log,"\t\t\t\n", strlen(stlist->name)>0 ? stlist->name : "(anonymous)"); - //??? dump state decision table - } /* if */ - assert(sym->refer!=NULL); - for (i=0; inumrefers; i++) - if ((ref=sym->refer[i])!=NULL) - fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); - /* print all symbols that are required for this function to compile */ - for (ref=root->next; ref!=NULL; ref=ref->next) { - if (ref==sym) - continue; - for (i=0; inumrefers; i++) - if (ref->refer[i]==sym) - fprintf(log,"\t\t\t\n",xmlencode(symname,funcdisplayname(symname,ref->name))); - } /* for */ - /* print parameter list, with tag & const information, plus descriptions */ - assert(sym->dim.arglist!=NULL); - for (arg=0; sym->dim.arglist[arg].ident!=0; arg++) { - int dim,paraminfo; - char *outer_start,*inner_start; - int outer_length,inner_length; - if (sym->dim.arglist[arg].ident==iVARARGS) - fprintf(log,"\t\t\t\n"); - else - fprintf(log,"\t\t\t\n",sym->dim.arglist[arg].name); - /* print the tag name(s) for each parameter */ - assert(sym->dim.arglist[arg].numtags>0); - assert(sym->dim.arglist[arg].tags!=NULL); - paraminfo=(sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) - || sym->dim.arglist[arg].ident==iREFERENCE - || sym->dim.arglist[arg].ident==iREFARRAY; - if (paraminfo) - fprintf(log,"\t\t\t\t"); - if (sym->dim.arglist[arg].numtags>1 || sym->dim.arglist[arg].tags[0]!=0) { - assert(paraminfo); - if (sym->dim.arglist[arg].numtags>1) - fprintf(log," {"); - for (i=0; idim.arglist[arg].numtags; i++) { - if (i>0) - fprintf(log,","); - tagsym=find_tag_byval(sym->dim.arglist[arg].tags[i]); - assert(tagsym!=NULL); - fprintf(log,"%s",tagsym->name); - } /* for */ - if (sym->dim.arglist[arg].numtags>1) - fprintf(log,"}"); - } /* if */ - switch (sym->dim.arglist[arg].ident) { - case iREFERENCE: - fprintf(log," &"); - break; - case iREFARRAY: - fprintf(log," "); - for (dim=0; dimdim.arglist[arg].numdim; dim++) { - if (sym->dim.arglist[arg].dim[dim]==0) { - fprintf(log,"[]"); - } else { - //??? find index tag - fprintf(log,"[%d]",sym->dim.arglist[arg].dim[dim]); - } /* if */ - } /* for */ - break; - } /* switch */ - if (paraminfo) - fprintf(log," \n"); - /* print the user description of the parameter (parse through - * sym->documentation) - */ - if (sym->documentation!=NULL - && find_xmltag(sym->documentation, "param", "name", sym->dim.arglist[arg].name, - &outer_start, &outer_length, &inner_start, &inner_length)) - { - char *tail; - fprintf(log,"\t\t\t\t%.*s\n",inner_length,inner_start); - /* delete from documentation string */ - tail=outer_start+outer_length; - memmove(outer_start,tail,strlen(tail)+1); - } /* if */ - fprintf(log,"\t\t\t\n"); - } /* for */ - if (sym->documentation!=NULL) - fprintf(log,"\t\t\t%s\n",sym->documentation); - fprintf(log,"\t\t\n"); - } /* for */ - - fprintf(log,"\n\t\n"); - fprintf(log,"\n"); -} -#endif - -/* Every symbol has a referrer list, that contains the functions that use - * the symbol. Now, if function "apple" is accessed by functions "banana" and - * "citron", but neither function "banana" nor "citron" are used by anyone - * else, then, by inference, function "apple" is not used either. - */ -static void reduce_referrers(symbol *root) -{ - int i,restart; - symbol *sym,*ref; - - do { - restart=0; - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->parent!=NULL) - continue; /* hierarchical data type */ - if (sym->ident==iFUNCTN - && (sym->usage & uNATIVE)==0 - && (sym->usage & uPUBLIC)==0 && strcmp(sym->name,uMAINFUNC)!=0 && strcmp(sym->name,uENTRYFUNC)!=0 - && count_referrers(sym)==0) - { - sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */ - /* find all symbols that are referred by this symbol */ - for (ref=root->next; ref!=NULL; ref=ref->next) { - if (ref->parent!=NULL) - continue; /* hierarchical data type */ - assert(ref->refer!=NULL); - for (i=0; inumrefers && ref->refer[i]!=sym; i++) - /* nothing */; - if (inumrefers) { - assert(ref->refer[i]==sym); - ref->refer[i]=NULL; - restart++; - } /* if */ - } /* for */ - } else if ((sym->ident==iVARIABLE || sym->ident==iARRAY) - && (sym->usage & uPUBLIC)==0 - && sym->parent==NULL - && count_referrers(sym)==0) - { - sym->usage&=~(uREAD | uWRITTEN); /* erase usage bits if there is no referrer */ - } /* if */ - } /* for */ - /* after removing a symbol, check whether more can be removed */ - } while (restart>0); -} - -#if !defined SC_LIGHT -static long max_stacksize_recurse(symbol *sourcesym,symbol *sym,long basesize,int *pubfuncparams,int *recursion) -{ - long size,maxsize; - int i; - - assert(sym!=NULL); - assert(sym->ident==iFUNCTN); - assert((sym->usage & uNATIVE)==0); - assert(recursion!=NULL); - - maxsize=sym->x.stacksize; - for (i=0; inumrefers; i++) { - if (sym->refer[i]!=NULL) { - assert(sym->refer[i]->ident==iFUNCTN); - assert((sym->refer[i]->usage & uNATIVE)==0); /* a native function cannot refer to a user-function */ - if (sym->refer[i]==sourcesym) { /* recursion detection */ - *recursion=1; - break; /* recursion was detected, quit loop */ - } /* if */ - size=max_stacksize_recurse(sourcesym,sym->refer[i],sym->x.stacksize,pubfuncparams,recursion); - if (maxsizeusage & uPUBLIC)!=0) { - /* Find out how many parameters a public function has, then see if this - * is bigger than some maximum - */ - arginfo *arg=sym->dim.arglist; - int count=0; - assert(arg!=0); - while (arg->ident!=0) { - count++; - arg++; - } /* while */ - assert(pubfuncparams!=0); - if (count>*pubfuncparams) - *pubfuncparams=count; - } /* if */ - - return maxsize+basesize; -} - -static long max_stacksize(symbol *root,int *recursion) -{ - /* Loop over all non-native functions. For each function, loop - * over all of its referrers, accumulating the stack requirements. - * Detect (indirect) recursion with a "mark-and-sweep" algorithm. - * I (mis-)use the "compound" field of the symbol structure for - * the marker, as this field is unused for functions. - * - * Note that the stack is shared with the heap. A host application - * may "eat" cells from the heap as well, through amx_Allot(). The - * stack requirements are thus only an estimate. - */ - long size,maxsize; - int maxparams; - symbol *sym; - - assert(root!=NULL); - assert(recursion!=NULL); - #if !defined NDEBUG - for (sym=root->next; sym!=NULL; sym=sym->next) - if (sym->ident==iFUNCTN) - assert(sym->compound==0); - #endif - - maxsize=0; - maxparams=0; - *recursion=0; /* assume no recursion */ - for (sym=root->next; sym!=NULL; sym=sym->next) { - /* drop out if this is not a user-implemented function */ - if (sym->ident!=iFUNCTN || (sym->usage & uNATIVE)!=0) - continue; - /* accumulate stack size for this symbol */ - size=max_stacksize_recurse(sym,sym,0L,&maxparams,recursion); - assert(size>=0); - if (maxsizenext; - while (sym!=NULL && sym->compound>=level) { - switch (sym->ident) { - case iLABEL: - if (testlabs) { - if ((sym->usage & uDEFINE)==0) { - error(19,sym->name); /* not a label: ... */ - } else if ((sym->usage & uREAD)==0) { - errorset(sSETPOS,sym->lnumber); - error(203,sym->name); /* symbol isn't used: ... */ - } /* if */ - } /* if */ - break; - case iFUNCTN: - if ((sym->usage & (uDEFINE | uREAD | uNATIVE | uSTOCK | uPUBLIC))==uDEFINE) { - funcdisplayname(symname,sym->name); - if (strlen(symname)>0) - error(203,symname); /* symbol isn't used ... (and not public/native/stock) */ - } /* if */ - if ((sym->usage & uPUBLIC)!=0 || strcmp(sym->name,uMAINFUNC)==0) - entry=TRUE; /* there is an entry point */ - /* also mark the function to the debug information */ - if (((sym->usage & uREAD)!=0 || (sym->usage & uPUBLIC)!=0) && (sym->usage & uNATIVE)==0) - insert_dbgsymbol(sym); - break; - case iCONSTEXPR: - if (testconst && (sym->usage & uREAD)==0) { - errorset(sSETPOS,sym->lnumber); - error(203,sym->name); /* symbol isn't used: ... */ - } /* if */ - break; - default: - /* a variable */ - if (sym->parent!=NULL) - break; /* hierarchical data type */ - if ((sym->usage & (uWRITTEN | uREAD | uSTOCK))==0) { - if (testconst) - errorset(sSETPOS,sym->lnumber); - error(203,sym->name,sym->lnumber); /* symbol isn't used (and not stock) */ - } else if ((sym->usage & (uREAD | uSTOCK | uPUBLIC))==0) { - errorset(sSETPOS,sym->lnumber); - error(204,sym->name); /* value assigned to symbol is never used */ -#if 0 // ??? not sure whether it is a good idea to force people use "const" - } else if ((sym->usage & (uWRITTEN | uPUBLIC | uCONST))==0 && sym->ident==iREFARRAY) { - errorset(sSETPOS,sym->lnumber); - error(214,sym->name); /* make array argument "const" */ -#endif - } /* if */ - /* also mark the variable (local or global) to the debug information */ - if ((sym->usage & (uWRITTEN | uREAD))!=0 && (sym->usage & uNATIVE)==0) - insert_dbgsymbol(sym); - } /* if */ - sym=sym->next; - } /* while */ - - return entry; -} - -static cell calc_array_datasize(symbol *sym, cell *offset) -{ - cell length; - - assert(sym!=NULL); - assert(sym->ident==iARRAY || sym->ident==iREFARRAY); - length=sym->dim.array.length; - if (sym->dim.array.level > 0) { - cell sublength=calc_array_datasize(finddepend(sym),offset); - if (offset!=NULL) - *offset=length*(*offset+sizeof(cell)); - if (sublength>0) - length*=length*sublength; - else - length=0; - } else { - if (offset!=NULL) - *offset=0; - } /* if */ - return length; -} - -static void destructsymbols(symbol *root,int level) -{ - cell offset=0; - int savepri=FALSE; - symbol *sym=root->next; - while (sym!=NULL && sym->compound>=level) { - if (sym->ident==iVARIABLE || sym->ident==iARRAY) { - char symbolname[16]; - symbol *opsym; - cell elements; - /* check that the '~' operator is defined for this tag */ - operator_symname(symbolname,"~",sym->tag,0,1,0); - if ((opsym=findglb(symbolname,sGLOBAL))!=NULL) { - /* save PRI, in case of a return statment */ - if (!savepri) { - pushreg(sPRI); /* right-hand operand is in PRI */ - savepri=TRUE; - } /* if */ - /* if the variable is an array, get the number of elements */ - if (sym->ident==iARRAY) { - elements=calc_array_datasize(sym,&offset); - /* "elements" can be zero when the variable is declared like - * new mytag: myvar[2][] = { {1, 2}, {3, 4} } - * one should declare all dimensions! - */ - if (elements==0) - error(46,sym->name); /* array size is unknown */ - } else { - elements=1; - offset=0; - } /* if */ - pushval(elements); - /* call the '~' operator */ - address(sym,sPRI); - addconst(offset); /* add offset to array data to the address */ - pushreg(sPRI); - pushval(2*sizeof(cell));/* 2 parameters */ - assert(opsym->ident==iFUNCTN); - ffcall(opsym,NULL,1); - if (sc_status!=statSKIP) - markusage(opsym,uREAD); /* do not mark as "used" when this call itself is skipped */ - if ((opsym->usage & uNATIVE)!=0 && opsym->x.lib!=NULL) - opsym->x.lib->value += 1; /* increment "usage count" of the library */ - } /* if */ - } /* if */ - sym=sym->next; - } /* while */ - /* restore PRI, if it was saved */ - if (savepri) - popreg(sPRI); -} - -static constvalue *insert_constval(constvalue *prev,constvalue *next,const char *name,cell val, - int index) -{ - constvalue *cur; - - if ((cur=(constvalue*)malloc(sizeof(constvalue)))==NULL) - error(103); /* insufficient memory (fatal error) */ - memset(cur,0,sizeof(constvalue)); - if (name!=NULL) { - assert(strlen(name)name,name); - } /* if */ - cur->value=val; - cur->index=index; - cur->next=next; - prev->next=cur; - return cur; -} - -SC_FUNC constvalue *append_constval(constvalue *table,const char *name,cell val,int index) -{ - constvalue *cur,*prev; - - /* find the end of the constant table */ - for (prev=table, cur=table->next; cur!=NULL; prev=cur, cur=cur->next) - /* nothing */; - return insert_constval(prev,NULL,name,val,index); -} - -SC_FUNC constvalue *find_constval(constvalue *table,char *name,int index) -{ - constvalue *ptr = table->next; - - while (ptr!=NULL) { - if (strcmp(name,ptr->name)==0 && ptr->index==index) - return ptr; - ptr=ptr->next; - } /* while */ - return NULL; -} - -static constvalue *find_constval_byval(constvalue *table,cell val) -{ - constvalue *ptr = table->next; - - while (ptr!=NULL) { - if (ptr->value==val) - return ptr; - ptr=ptr->next; - } /* while */ - return NULL; -} - -#if 0 /* never used */ -static int delete_constval(constvalue *table,char *name) -{ - constvalue *prev = table; - constvalue *cur = prev->next; - - while (cur!=NULL) { - if (strcmp(name,cur->name)==0) { - prev->next=cur->next; - free(cur); - return TRUE; - } /* if */ - prev=cur; - cur=cur->next; - } /* while */ - return FALSE; -} -#endif - -SC_FUNC void delete_consttable(constvalue *table) -{ - constvalue *cur=table->next, *next; - - while (cur!=NULL) { - next=cur->next; - free(cur); - cur=next; - } /* while */ - memset(table,0,sizeof(constvalue)); -} - -/* add_constant - * - * Adds a symbol to the symbol table. Returns NULL on failure. - */ -SC_FUNC symbol *add_constant(char *name,cell val,int vclass,int tag) -{ - symbol *sym; - - /* Test whether a global or local symbol with the same name exists. Since - * constants are stored in the symbols table, this also finds previously - * defind constants. */ - sym=findglb(name,sSTATEVAR); - if (!sym) - sym=findloc(name); - if (sym) { - int redef=0; - if (sym->ident!=iCONSTEXPR) - redef=1; /* redefinition a function/variable to a constant is not allowed */ - if ((sym->usage & uENUMFIELD)!=0) { - /* enum field, special case if it has a different tag and the new symbol is also an enum field */ - constvalue *tagid; - symbol *tagsym; - if (sym->tag==tag) - redef=1; /* enumeration field is redefined (same tag) */ - tagid=find_tag_byval(tag); - if (tagid==NULL) { - redef=1; /* new constant does not have a tag */ - } else { - tagsym=findconst(tagid->name,NULL); - if (tagsym==NULL || (tagsym->usage & uENUMROOT)==0) - redef=1; /* new constant is not an enumeration field */ - } /* if */ - /* in this particular case (enumeration field that is part of a different - * enum, and non-conflicting with plain constants) we want to be able to - * redefine it - */ - if (!redef) - goto redef_enumfield; - } else if (sym->tag!=tag) { - redef=1; /* redefinition of a constant (non-enum) to a different tag is not allowed */ - } /* if */ - if (redef) { - error(21,name); /* symbol already defined */ - return NULL; - } else if (sym->addr!=val) { - error(201,name); /* redefinition of constant (different value) */ - sym->addr=val; /* set new value */ - } /* if */ - /* silently ignore redefinitions of constants with the same value & tag */ - return sym; - } /* if */ - - /* constant doesn't exist yet (or is allowed to be redefined) */ -redef_enumfield: - sym=addsym(name,val,iCONSTEXPR,vclass,tag,uDEFINE); - assert(sym!=NULL); /* fatal error 103 must be given on error */ - if (sc_status == statIDLE) - sym->usage |= uPREDEF; - return sym; -} - -/* statement - The Statement Parser - * - * This routine is called whenever the parser needs to know what statement - * it encounters (i.e. whenever program syntax requires a statement). - */ -static void statement(int *lastindent,int allow_decl) -{ - int tok; - cell val; - char *st; - - if (!freading) { - error(36); /* empty statement */ - return; - } /* if */ - errorset(sRESET,0); - - tok=lex(&val,&st); - if (tok!='{') { - insert_dbgline(fline); - setline(TRUE); - } /* if */ - /* lex() has set stmtindent */ - if (lastindent!=NULL && tok!=tLABEL) { - if (*lastindent>=0 && *lastindent!=stmtindent && !indent_nowarn && sc_tabsize>0) - error(217); /* loose indentation */ - *lastindent=stmtindent; - indent_nowarn=FALSE; /* if warning was blocked, re-enable it */ - } /* if */ - switch (tok) { - case 0: - /* nothing */ - break; - case tNEW: - if (allow_decl) { - declloc(FALSE); - lastst=tNEW; - } else { - error(3); /* declaration only valid in a block */ - } /* if */ - break; - case tSTATIC: - if (allow_decl) { - declloc(TRUE); - lastst=tNEW; - } else { - error(3); /* declaration only valid in a block */ - } /* if */ - break; - case '{': - tok=fline; - if (!matchtoken('}')) /* {} is the empty statement */ - compound(tok==fline); - /* lastst (for "last statement") does not change */ - break; - case ';': - error(36); /* empty statement */ - break; - case tIF: - doif(); - lastst=tIF; - break; - case tWHILE: - dowhile(); - lastst=tWHILE; - break; - case tDO: - dodo(); - lastst=tDO; - break; - case tFOR: - dofor(); - lastst=tFOR; - break; - case tSWITCH: - doswitch(); - lastst=tSWITCH; - break; - case tCASE: - case tDEFAULT: - error(14); /* not in switch */ - break; - case tGOTO: - dogoto(); - lastst=tGOTO; - break; - case tLABEL: - dolabel(); - lastst=tLABEL; - break; - case tRETURN: - doreturn(); - lastst=tRETURN; - break; - case tBREAK: - dobreak(); - lastst=tBREAK; - break; - case tCONTINUE: - docont(); - lastst=tCONTINUE; - break; - case tEXIT: - doexit(); - lastst=tEXIT; - break; - case tASSERT: - doassert(); - lastst=tASSERT; - break; - case tSLEEP: - dosleep(); - lastst=tSLEEP; - break; - case tSTATE: - dostate(); - lastst=tSTATE; - break; - case tCONST: - decl_const(sLOCAL); - break; - case tENUM: - decl_enum(sLOCAL); - break; - default: /* non-empty expression */ - sc_allowproccall=optproccall; - lexpush(); /* analyze token later */ - doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); - needtoken(tTERM); - lastst=tEXPR; - sc_allowproccall=FALSE; - } /* switch */ -} - -static void compound(int stmt_sameline) -{ - int indent=-1; - cell save_decl=declared; - int count_stmt=0; - int block_start=fline; /* save line where the compound block started */ - - /* if there is more text on this line, we should adjust the statement indent */ - if (stmt_sameline) { - int i; - const unsigned char *p=lptr; - /* go back to the opening brace */ - while (*p!='{') { - assert(p>pline); - p--; - } /* while */ - assert(*p=='{'); /* it should be found */ - /* go forward, skipping white-space */ - p++; - while (*p<=' ' && *p!='\0') - p++; - assert(*p!='\0'); /* a token should be found */ - stmtindent=0; - for (i=0; i<(int)(p-pline); i++) - if (pline[i]=='\t' && sc_tabsize>0) - stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize); - else - stmtindent++; - } /* if */ - - nestlevel+=1; /* increase compound statement level */ - while (matchtoken('}')==0){ /* repeat until compound statement is closed */ - if (!freading){ - error(30,block_start); /* compound block not closed at end of file */ - break; - } else { - if (count_stmt>0 && (lastst==tRETURN || lastst==tBREAK || lastst==tCONTINUE)) - error(225); /* unreachable code */ - statement(&indent,TRUE); /* do a statement */ - count_stmt++; - } /* if */ - } /* while */ - if (lastst!=tRETURN) - destructsymbols(&loctab,nestlevel); - if (lastst!=tRETURN && lastst!=tGOTO) - modstk((int)(declared-save_decl)*sizeof(cell)); /* delete local variable space */ - testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ - declared=save_decl; - delete_symbols(&loctab,nestlevel,FALSE,TRUE); /* erase local symbols, but - * retain block local labels - * (within the function) */ - nestlevel-=1; /* decrease compound statement level */ -} - -/* doexpr - * - * Global references: stgidx (referred to only) - */ -static int doexpr(int comma,int chkeffect,int allowarray,int mark_endexpr, - int *tag,symbol **symptr,int chkfuncresult) -{ - int index,ident; - int localstaging=FALSE; - cell val; - - if (!staging) { - stgset(TRUE); /* start stage-buffering */ - localstaging=TRUE; - assert(stgidx==0); - } /* if */ - index=stgidx; - errorset(sEXPRMARK,0); - do { - /* on second round through, mark the end of the previous expression */ - if (index!=stgidx) - markexpr(sEXPR,NULL,0); - sideeffect=FALSE; - ident=expression(&val,tag,symptr,chkfuncresult); - if (!allowarray && (ident==iARRAY || ident==iREFARRAY)) - error(33,"-unknown-"); /* array must be indexed */ - if (chkeffect && !sideeffect) - error(215); /* expression has no effect */ - sc_allowproccall=FALSE; /* cannot use "procedure call" syntax anymore */ - } while (comma && matchtoken(',')); /* more? */ - if (mark_endexpr) - markexpr(sEXPR,NULL,0); /* optionally, mark the end of the expression */ - errorset(sEXPRRELEASE,0); - if (localstaging) { - stgout(index); - stgset(FALSE); /* stop staging */ - } /* if */ - return ident; -} - -/* constexpr - */ -SC_FUNC int constexpr(cell *val,int *tag,symbol **symptr) -{ - int ident,index; - cell cidx; - - stgset(TRUE); /* start stage-buffering */ - stgget(&index,&cidx); /* mark position in code generator */ - errorset(sEXPRMARK,0); - ident=expression(val,tag,symptr,FALSE); - stgdel(index,cidx); /* scratch generated code */ - stgset(FALSE); /* stop stage-buffering */ - if (ident!=iCONSTEXPR) { - error(8); /* must be constant expression */ - if (val!=NULL) - *val=0; - if (tag!=NULL) - *tag=0; - if (symptr!=NULL) - *symptr=NULL; - } /* if */ - errorset(sEXPRRELEASE,0); - return (ident==iCONSTEXPR); -} - -/* test - * - * In the case a "simple assignment" operator ("=") is used within a test, - * the warning "possibly unintended assignment" is displayed. This routine - * sets the global variable "sc_intest" to true, it is restored upon termination. - * In the case the assignment was intended, use parantheses around the - * expression to avoid the warning; primary() sets "sc_intest" to 0. - * - * Global references: sc_intest (altered, but restored upon termination) - */ -static void test(int label,int parens,int invert) -{ - int index,tok; - cell cidx; - int ident,tag; - cell constval; - symbol *sym; - int localstaging=FALSE; - - if (!staging) { - stgset(TRUE); /* start staging */ - localstaging=TRUE; - #if !defined NDEBUG - stgget(&index,&cidx); /* should start at zero if started locally */ - assert(index==0); - #endif - } /* if */ - - PUSHSTK_I(sc_intest); - sc_intest=TRUE; - if (parens) - needtoken('('); - do { - stgget(&index,&cidx); /* mark position (of last expression) in - * code generator */ - ident=expression(&constval,&tag,&sym,TRUE); - tok=matchtoken(','); - if (tok) - markexpr(sEXPR,NULL,0); - } while (tok); /* do */ - if (parens) - needtoken(')'); - if (ident==iARRAY || ident==iREFARRAY) { - char *ptr=(sym->name!=NULL) ? sym->name : "-unknown-"; - error(33,ptr); /* array must be indexed */ - } /* if */ - if (ident==iCONSTEXPR) { /* constant expression */ - sc_intest=(short)POPSTK_I();/* restore stack */ - stgdel(index,cidx); - if (constval) { /* code always executed */ - error(206); /* redundant test: always non-zero */ - } else { - error(205); /* redundant code: never executed */ - jumplabel(label); - } /* if */ - if (localstaging) { - stgout(0); /* write "jumplabel" code */ - stgset(FALSE); /* stop staging */ - } /* if */ - return; - } /* if */ - if (tag!=0 && tag!=pc_addtag("bool")) - if (check_userop(lneg,tag,0,1,NULL,&tag)) - invert= !invert; /* user-defined ! operator inverted result */ - if (invert) - jmp_ne0(label); /* jump to label if true (different from 0) */ - else - jmp_eq0(label); /* jump to label if false (equal to 0) */ - markexpr(sEXPR,NULL,0); /* end expression (give optimizer a chance) */ - sc_intest=(short)POPSTK_I(); /* double typecast to avoid warning with Microsoft C */ - if (localstaging) { - stgout(0); /* output queue from the very beginning (see - * assert() when localstaging is set to TRUE) */ - stgset(FALSE); /* stop staging */ - } /* if */ -} - -static void doif(void) -{ - int flab1,flab2; - int ifindent; - - ifindent=stmtindent; /* save the indent of the "if" instruction */ - flab1=getlabel(); /* get label number for false branch */ - test(flab1,TRUE,FALSE); /* get expression, branch to flab1 if false */ - statement(NULL,FALSE); /* if true, do a statement */ - if (matchtoken(tELSE)==0){ /* if...else ? */ - setlabel(flab1); /* no, simple if..., print false label */ - } else { - /* to avoid the "dangling else" error, we want a warning if the "else" - * has a lower indent than the matching "if" */ - if (stmtindent0) - error(217); /* loose indentation */ - flab2=getlabel(); - if ((lastst!=tRETURN) && (lastst!=tGOTO)) - jumplabel(flab2); - setlabel(flab1); /* print false label */ - statement(NULL,FALSE); /* do "else" clause */ - setlabel(flab2); /* print true label */ - } /* endif */ -} - -static void dowhile(void) -{ - int wq[wqSIZE]; /* allocate local queue */ - - addwhile(wq); /* add entry to queue for "break" */ - setlabel(wq[wqLOOP]); /* loop label */ - /* The debugger uses the "break" opcode to be able to "break" out of - * a loop. To make sure that each loop has a break opcode, even for the - * tiniest loop, set it below the top of the loop - */ - setline(TRUE); - test(wq[wqEXIT],TRUE,FALSE); /* branch to wq[wqEXIT] if false */ - statement(NULL,FALSE); /* if so, do a statement */ - jumplabel(wq[wqLOOP]); /* and loop to "while" start */ - setlabel(wq[wqEXIT]); /* exit label */ - delwhile(); /* delete queue entry */ -} - -/* - * Note that "continue" will in this case not jump to the top of the loop, but - * to the end: just before the TRUE-or-FALSE testing code. - */ -static void dodo(void) -{ - int wq[wqSIZE],top; - - addwhile(wq); /* see "dowhile" for more info */ - top=getlabel(); /* make a label first */ - setlabel(top); /* loop label */ - statement(NULL,FALSE); - needtoken(tWHILE); - setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */ - setline(TRUE); - test(wq[wqEXIT],TRUE,FALSE); - jumplabel(top); - setlabel(wq[wqEXIT]); - delwhile(); - needtoken(tTERM); -} - -static void dofor(void) -{ - int wq[wqSIZE],skiplab; - cell save_decl; - int save_nestlevel,index; - int *ptr; - - save_decl=declared; - save_nestlevel=nestlevel; - - addwhile(wq); - skiplab=getlabel(); - needtoken('('); - if (matchtoken(';')==0) { - /* new variable declarations are allowed here */ - if (matchtoken(tNEW)) { - /* The variable in expr1 of the for loop is at a - * 'compound statement' level of it own. - */ - nestlevel++; - declloc(FALSE); /* declare local variable */ - } else { - doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */ - needtoken(';'); - } /* if */ - } /* if */ - /* Adjust the "declared" field in the "while queue", in case that - * local variables were declared in the first expression of the - * "for" loop. These are deleted in separately, so a "break" or a "continue" - * must ignore these fields. - */ - ptr=readwhile(); - assert(ptr!=NULL); - ptr[wqBRK]=(int)declared; - ptr[wqCONT]=(int)declared; - jumplabel(skiplab); /* skip expression 3 1st time */ - setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */ - setline(TRUE); - /* Expressions 2 and 3 are reversed in the generated code: expression 3 - * precedes expression 2. When parsing, the code is buffered and marks for - * the start of each expression are insterted in the buffer. - */ - assert(!staging); - stgset(TRUE); /* start staging */ - assert(stgidx==0); - index=stgidx; - stgmark(sSTARTREORDER); - stgmark((char)(sEXPRSTART+0)); /* mark start of 2nd expression in stage */ - setlabel(skiplab); /* jump to this point after 1st expression */ - if (matchtoken(';')==0) { - test(wq[wqEXIT],FALSE,FALSE); /* expression 2 (jump to wq[wqEXIT] if false) */ - needtoken(';'); - } /* if */ - stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */ - if (matchtoken(')')==0) { - doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 3 */ - needtoken(')'); - } /* if */ - stgmark(sENDREORDER); /* mark end of reversed evaluation */ - stgout(index); - stgset(FALSE); /* stop staging */ - statement(NULL,FALSE); - jumplabel(wq[wqLOOP]); - setlabel(wq[wqEXIT]); - delwhile(); - - assert(nestlevel>=save_nestlevel); - if (nestlevel>save_nestlevel) { - /* Clean up the space and the symbol table for the local - * variable in "expr1". - */ - destructsymbols(&loctab,nestlevel); - modstk((int)(declared-save_decl)*sizeof(cell)); - testsymbols(&loctab,nestlevel,FALSE,TRUE); /* look for unused block locals */ - declared=save_decl; - delete_symbols(&loctab,nestlevel,FALSE,TRUE); - nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */ - } /* if */ -} - -/* The switch statement is incompatible with its C sibling: - * 1. the cases are not drop through - * 2. only one instruction may appear below each case, use a compound - * instruction to execute multiple instructions - * 3. the "case" keyword accepts a comma separated list of values to - * match, it also accepts a range using the syntax "1 .. 4" - * - * SWITCH param - * PRI = expression result - * param = table offset (code segment) - * - */ -static void doswitch(void) -{ - int lbl_table,lbl_exit,lbl_case; - int tok,swdefault,casecount; - cell val; - char *str; - constvalue caselist = { NULL, "", 0, 0}; /* case list starts empty */ - constvalue *cse,*csp; - char labelname[sNAMEMAX+1]; - - needtoken('('); - doexpr(TRUE,FALSE,FALSE,FALSE,NULL,NULL,TRUE);/* evaluate switch expression */ - needtoken(')'); - /* generate the code for the switch statement, the label is the address - * of the case table (to be generated later). - */ - lbl_table=getlabel(); - lbl_case=0; /* just to avoid a compiler warning */ - ffswitch(lbl_table); - - needtoken('{'); - lbl_exit=getlabel(); /* get label number for jumping out of switch */ - swdefault=FALSE; - casecount=0; - do { - tok=lex(&val,&str); /* read in (new) token */ - switch (tok) { - case tCASE: - if (swdefault!=FALSE) - error(15); /* "default" case must be last in switch statement */ - lbl_case=getlabel(); - PUSHSTK_I(sc_allowtags); - sc_allowtags=FALSE; /* do not allow tagnames here */ - do { - casecount++; - - /* ??? enforce/document that, in a switch, a statement cannot start - * with a label. Then, you can search for: - * * the first semicolon (marks the end of a statement) - * * an opening brace (marks the start of a compound statement) - * and search for the right-most colon before that statement - * Now, by replacing the ':' by a special COLON token, you can - * parse all expressions until that special token. - */ - - constexpr(&val,NULL,NULL); - /* Search the insertion point (the table is kept in sorted order, so - * that advanced abstract machines can sift the case table with a - * binary search). Check for duplicate case values at the same time. - */ - for (csp=&caselist, cse=caselist.next; - cse!=NULL && cse->valuenext) - /* nothing */; - if (cse!=NULL && cse->value==val) - error(40,val); /* duplicate "case" label */ - /* Since the label is stored as a string in the "constvalue", the - * size of an identifier must be at least 8, as there are 8 - * hexadecimal digits in a 32-bit number. - */ - #if sNAMEMAX < 8 - #error Length of identifier (sNAMEMAX) too small. - #endif - assert(csp!=NULL); - assert(csp->next==cse); - insert_constval(csp,cse,itoh(lbl_case),val,0); - if (matchtoken(tDBLDOT)) { - cell end; - constexpr(&end,NULL,NULL); - if (end<=val) - error(50); /* invalid range */ - while (++val<=end) { - casecount++; - /* find the new insertion point */ - for (csp=&caselist, cse=caselist.next; - cse!=NULL && cse->valuenext) - /* nothing */; - if (cse!=NULL && cse->value==val) - error(40,val); /* duplicate "case" label */ - assert(csp!=NULL); - assert(csp->next==cse); - insert_constval(csp,cse,itoh(lbl_case),val,0); - } /* if */ - } /* if */ - } while (matchtoken(',')); - needtoken(':'); /* ':' ends the case */ - sc_allowtags=(short)POPSTK_I(); /* reset */ - setlabel(lbl_case); - statement(NULL,FALSE); - jumplabel(lbl_exit); - break; - case tDEFAULT: - if (swdefault!=FALSE) - error(16); /* multiple defaults in switch */ - lbl_case=getlabel(); - setlabel(lbl_case); - needtoken(':'); - swdefault=TRUE; - statement(NULL,FALSE); - /* Jump to lbl_exit, even thouh this is the last clause in the - * switch, because the jump table is generated between the last - * clause of the switch and the exit label. - */ - jumplabel(lbl_exit); - break; - case '}': - /* nothing, but avoid dropping into "default" */ - break; - default: - error(2); - indent_nowarn=TRUE; /* disable this check */ - tok='}'; /* break out of the loop after an error */ - } /* switch */ - } while (tok!='}'); - - #if !defined NDEBUG - /* verify that the case table is sorted (unfortunatly, duplicates can - * occur; there really shouldn't be duplicate cases, but the compiler - * may not crash or drop into an assertion for a user error). */ - for (cse=caselist.next; cse!=NULL && cse->next!=NULL; cse=cse->next) - assert(cse->value <= cse->next->value); - #endif - /* generate the table here, before lbl_exit (general jump target) */ - setlabel(lbl_table); - assert(swdefault==FALSE || swdefault==TRUE); - if (swdefault==FALSE) { - /* store lbl_exit as the "none-matched" label in the switch table */ - strcpy(labelname,itoh(lbl_exit)); - } else { - /* lbl_case holds the label of the "default" clause */ - strcpy(labelname,itoh(lbl_case)); - } /* if */ - ffcase(casecount,labelname,TRUE); - /* generate the rest of the table */ - for (cse=caselist.next; cse!=NULL; cse=cse->next) - ffcase(cse->value,cse->name,FALSE); - - setlabel(lbl_exit); - delete_consttable(&caselist); /* clear list of case labels */ -} - -static void doassert(void) -{ - int flab1,index; - cell cidx; - - if ((sc_debug & sCHKBOUNDS)!=0) { - flab1=getlabel(); /* get label number for "OK" branch */ - test(flab1,FALSE,TRUE); /* get expression and branch to flab1 if true */ - insert_dbgline(fline); /* make sure we can find the correct line number */ - ffabort(xASSERTION); - setlabel(flab1); - } else { - stgset(TRUE); /* start staging */ - stgget(&index,&cidx); /* mark position in code generator */ - do { - expression(NULL,NULL,NULL,FALSE); - stgdel(index,cidx); /* just scrap the code */ - } while (matchtoken(',')); - stgset(FALSE); /* stop staging */ - } /* if */ - needtoken(tTERM); -} - -static void dogoto(void) -{ - char *st; - cell val; - symbol *sym; - - if (lex(&val,&st)==tSYMBOL) { - sym=fetchlab(st); - jumplabel((int)sym->addr); - sym->usage|=uREAD; /* set "uREAD" bit */ - // ??? if the label is defined (check sym->usage & uDEFINE), check - // sym->compound (nesting level of the label) against nestlevel; - // if sym->compound < nestlevel, call the destructor operator - } else { - error(20,st); /* illegal symbol name */ - } /* if */ - needtoken(tTERM); -} - -static void dolabel(void) -{ - char *st; - cell val; - symbol *sym; - - tokeninfo(&val,&st); /* retrieve label name again */ - if (find_constval(&tagname_tab,st,0)!=NULL) - error(221,st); /* label name shadows tagname */ - sym=fetchlab(st); - setlabel((int)sym->addr); - /* since one can jump around variable declarations or out of compound - * blocks, the stack must be manually adjusted - */ - setstk(-declared*sizeof(cell)); - sym->usage|=uDEFINE; /* label is now defined */ -} - -/* fetchlab - * - * Finds a label from the (local) symbol table or adds one to it. - * Labels are local in scope. - * - * Note: The "_usage" bit is set to zero. The routines that call "fetchlab()" - * must set this bit accordingly. - */ -static symbol *fetchlab(char *name) -{ - symbol *sym; - - sym=findloc(name); /* labels are local in scope */ - if (sym){ - if (sym->ident!=iLABEL) - error(19,sym->name); /* not a label: ... */ - } else { - sym=addsym(name,getlabel(),iLABEL,sLOCAL,0,0); - assert(sym!=NULL); /* fatal error 103 must be given on error */ - sym->x.declared=(int)declared; - sym->compound=nestlevel; - } /* if */ - return sym; -} - -/* doreturn - * - * Global references: rettype (altered) - */ -static void doreturn(void) -{ - int tag,ident; - int level; - symbol *sym,*sub; - - if (!matchtoken(tTERM)) { - /* "return " */ - if ((rettype & uRETNONE)!=0) - error(78); /* mix "return;" and "return value;" */ - ident=doexpr(TRUE,FALSE,TRUE,TRUE,&tag,&sym,TRUE); - needtoken(tTERM); - /* see if this function already has a sub type (an array attached) */ - sub=finddepend(curfunc); - assert(sub==NULL || sub->ident==iREFARRAY); - if ((rettype & uRETVALUE)!=0) { - int retarray=(ident==iARRAY || ident==iREFARRAY); - /* there was an earlier "return" statement in this function */ - if (sub==NULL && retarray || sub!=NULL && !retarray) - error(79); /* mixing "return array;" and "return value;" */ - if (retarray && (curfunc->usage & uPUBLIC)!=0) - error(90,curfunc->name); /* public function may not return array */ - } /* if */ - rettype|=uRETVALUE; /* function returns a value */ - /* check tagname with function tagname */ - assert(curfunc!=NULL); - if (!matchtag(curfunc->tag,tag,TRUE)) - error(213); /* tagname mismatch */ - if (ident==iARRAY || ident==iREFARRAY) { - int dim[sDIMEN_MAX],numdim; - cell arraysize; - assert(sym!=NULL); - if (sub!=NULL) { - assert(sub->ident==iREFARRAY); - /* this function has an array attached already; check that the current - * "return" statement returns exactly the same array - */ - level=sym->dim.array.level; - if (sub->dim.array.level!=level) { - error(48); /* array dimensions must match */ - } else { - for (numdim=0; numdim<=level; numdim++) { - dim[numdim]=(int)sub->dim.array.length; - if (sym->dim.array.length!=dim[numdim]) - error(47); /* array sizes must match */ - if (numdimdim.array.level; - for (numdim=0; numdim<=level; numdim++) { - dim[numdim]=(int)sub->dim.array.length; - idxtag[numdim]=sub->x.tags.index; - if (numdimname); - } /* for */ - /* the address of the array is stored in a hidden parameter; the address - * of this parameter is 1 + the number of parameters (times the size of - * a cell) + the size of the stack frame and the return address - * base + 0*sizeof(cell) == previous "base" - * base + 1*sizeof(cell) == function return address - * base + 2*sizeof(cell) == number of arguments - * base + 3*sizeof(cell) == first argument of the function - * ... - * base + ((n-1)+3)*sizeof(cell) == last argument of the function - * base + (n+3)*sizeof(cell) == hidden parameter with array address - */ - assert(curfunc!=NULL); - assert(curfunc->dim.arglist!=NULL); - for (argcount=0; curfunc->dim.arglist[argcount].ident!=0; argcount++) - /* nothing */; - sub=addvariable(curfunc->name,(argcount+3)*sizeof(cell),iREFARRAY,sGLOBAL,curfunc->tag,dim,numdim,idxtag); - sub->parent=curfunc; - } /* if */ - /* get the hidden parameter, copy the array (the array is on the heap; - * it stays on the heap for the moment, and it is removed -usually- at - * the end of the expression/statement, see expression() in SC3.C) - */ - address(sub,sALT); /* ALT = destination */ - arraysize=calc_arraysize(dim,numdim,0); - memcopy(arraysize*sizeof(cell)); /* source already in PRI */ - /* moveto1(); is not necessary, callfunction() does a popreg() */ - } /* if */ - } else { - /* this return statement contains no expression */ - ldconst(0,sPRI); - if ((rettype & uRETVALUE)!=0) { - char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ - assert(curfunc!=NULL); - funcdisplayname(symname,curfunc->name); - error(209,symname); /* function should return a value */ - } /* if */ - rettype|=uRETNONE; /* function does not return anything */ - } /* if */ - destructsymbols(&loctab,0); /* call destructor for *all* locals */ - modstk((int)declared*sizeof(cell)); /* end of function, remove *all* - * local variables */ - ffret(strcmp(curfunc->name,uENTRYFUNC)!=0); -} - -static void dobreak(void) -{ - int *ptr; - - ptr=readwhile(); /* readwhile() gives an error if not in loop */ - needtoken(tTERM); - if (ptr==NULL) - return; - destructsymbols(&loctab,nestlevel); - modstk(((int)declared-ptr[wqBRK])*sizeof(cell)); - jumplabel(ptr[wqEXIT]); -} - -static void docont(void) -{ - int *ptr; - - ptr=readwhile(); /* readwhile() gives an error if not in loop */ - needtoken(tTERM); - if (ptr==NULL) - return; - destructsymbols(&loctab,nestlevel); - modstk(((int)declared-ptr[wqCONT])*sizeof(cell)); - jumplabel(ptr[wqLOOP]); -} - -SC_FUNC void exporttag(int tag) -{ - /* find the tag by value in the table, then set the top bit to mark it - * "public" - */ - if (tag!=0 && (tag & PUBLICTAG)==0) { - constvalue *ptr; - for (ptr=tagname_tab.next; ptr!=NULL && tag!=(int)(ptr->value & TAGMASK); ptr=ptr->next) - /* nothing */; - if (ptr!=NULL) - ptr->value |= PUBLICTAG; - } /* if */ -} - -static void doexit(void) -{ - int tag=0; - - if (matchtoken(tTERM)==0){ - doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE); - needtoken(tTERM); - } else { - ldconst(0,sPRI); - } /* if */ - ldconst(tag,sALT); - exporttag(tag); - destructsymbols(&loctab,0); /* call destructor for *all* locals */ - ffabort(xEXIT); -} - -static void dosleep(void) -{ - int tag=0; - - if (matchtoken(tTERM)==0){ - doexpr(TRUE,FALSE,FALSE,TRUE,&tag,NULL,TRUE); - needtoken(tTERM); - } else { - ldconst(0,sPRI); - } /* if */ - ldconst(tag,sALT); - exporttag(tag); - ffabort(xSLEEP); - - /* for stack usage checking, mark the use of the sleep instruction */ - pc_memflags |= suSLEEP_INSTR; -} - -static void dostate(void) -{ - char name[sNAMEMAX+1]; - constvalue *automaton; - constvalue *state; - constvalue *stlist; - int flabel; - symbol *sym; - #if !defined SC_LIGHT - int length,index,listid,listindex,stateindex; - char *doc; - #endif - - /* check for an optional condition */ - if (matchtoken('(')) { - flabel=getlabel(); /* get label number for "false" branch */ - pc_docexpr=TRUE; /* attach expression as a documentation string */ - test(flabel,FALSE,FALSE); /* get expression, branch to flabel if false */ - pc_docexpr=FALSE; - needtoken(')'); - } else { - flabel=-1; - } /* if */ - - if (!sc_getstateid(&automaton,&state)) { - delete_autolisttable(); - return; - } /* if */ - needtoken(tTERM); - - /* store the new state id */ - assert(state!=NULL); - ldconst(state->value,sPRI); - assert(automaton!=NULL); - assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0); - storereg(automaton->value,sPRI); - - /* find the optional entry() function for the state */ - sym=findglb(uENTRYFUNC,sGLOBAL); - if (sc_status==statWRITE && sym!=NULL && sym->ident==iFUNCTN && sym->states!=NULL) { - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { - assert(strlen(stlist->name)!=0); - if (state_getfsa(stlist->index)==automaton->index && state_inlist(stlist->index,(int)state->value)) - break; /* found! */ - } /* for */ - assert(stlist==NULL || state_inlist(stlist->index,state->value)); - if (stlist!=NULL) { - /* the label to jump to is in stlist->name */ - ffcall(sym,stlist->name,0); - } /* if */ - } /* if */ - - if (flabel>=0) - setlabel(flabel); /* condition was false, jump around the state switch */ - - #if !defined SC_LIGHT - /* mark for documentation */ - if (sc_status==statFIRST) { - char *str; - /* get the last list id attached to the function, this contains the source states */ - assert(curfunc!=NULL); - if (curfunc->states!=NULL) { - stlist=curfunc->states->next; - assert(stlist!=NULL); - while (stlist->next!=NULL) - stlist=stlist->next; - listid=stlist->index; - } else { - listid=-1; - } /* if */ - listindex=0; - length=strlen(name)+70; /* +70 for the fixed part "\n" */ - /* see if there are any condition strings to attach */ - for (index=0; (str=get_autolist(index))!=NULL; index++) - length+=strlen(str); - if ((doc=(char*)malloc(length*sizeof(char)))!=NULL) { - do { - sprintf(doc,"=0) { - /* get the source state */ - stateindex=state_listitem(listid,listindex); - state=state_findid(stateindex); - assert(state!=NULL); - sprintf(doc+strlen(doc)," source=\"%s\"",state->name); - } /* if */ - if (get_autolist(0)!=NULL) { - /* add the condition */ - strcat(doc," condition=\""); - for (index=0; (str=get_autolist(index))!=NULL; index++) { - /* remove the ')' token that may be appended before detecting that the expression has ended */ - if (*str!=')' || *(str+1)!='\0' || get_autolist(index+1)!=NULL) - strcat(doc,str); - } /* for */ - strcat(doc,"\""); - } /* if */ - strcat(doc,"/>\n"); - insert_docstring(doc); - } while (listid>=0 && ++listindex=(wq+wqTABSZ-wqSIZE)) - error(102,"loop table"); /* loop table overflow (too many active loops)*/ - k=0; - while (kwq) - wqptr-=wqSIZE; -} - -static int *readwhile(void) -{ - if (wqptr<=wq){ - error(24); /* out of context */ - return NULL; - } else { - return (wqptr-wqSIZE); - } /* if */ -} - diff --git a/compiler-init/compiler-init/sc2.c b/compiler-init/compiler-init/sc2.c deleted file mode 100644 index 0e399a70..00000000 --- a/compiler-init/compiler-init/sc2.c +++ /dev/null @@ -1,2801 +0,0 @@ -/* Pawn compiler - File input, preprocessing and lexical analysis functions - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc2.c 3590 2006-06-24 14:16:39Z thiadmer $ - */ -#include -#include -#include -#include -#include -#include -#include "lstring.h" -#include "sc.h" -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ - #include -#endif - -#if defined FORTIFY - #include -#endif - -/* flags for litchar() */ -#define RAWMODE 1 -#define UTF8MODE 2 -static cell litchar(const unsigned char **lptr,int flags); -static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag); - -static void substallpatterns(unsigned char *line,int buffersize); -static int match(char *st,int end); -static int alpha(char c); - -#define SKIPMODE 1 /* bit field in "#if" stack */ -#define PARSEMODE 2 /* bit field in "#if" stack */ -#define HANDLED_ELSE 4 /* bit field in "#if" stack */ -#define SKIPPING (skiplevel>0 && (ifstack[skiplevel-1] & SKIPMODE)==SKIPMODE) - -static short icomment; /* currently in multiline comment? */ -static char ifstack[sCOMP_STACK]; /* "#if" stack */ -static short iflevel; /* nesting level if #if/#else/#endif */ -static short skiplevel; /* level at which we started skipping (including nested #if .. #endif) */ -static unsigned char term_expr[] = ""; -static int listline=-1; /* "current line" for the list file */ - - -/* pushstk & popstk - * - * Uses a LIFO stack to store information. The stack is used by doinclude(), - * doswitch() (to hold the state of "swactive") and some other routines. - * - * Porting note: I made the bold assumption that an integer will not be - * larger than a pointer (it may be smaller). That is, the stack element - * is typedef'ed as a pointer type, but I also store integers on it. See - * SC.H for "stkitem" - * - * Global references: stack,stkidx,stktop (private to pushstk(), popstk() - * and clearstk()) - */ -static stkitem *stack=NULL; -static int stkidx=0,stktop=0; - -SC_FUNC void pushstk(stkitem val) -{ - assert(stkidx<=stktop); - if (stkidx==stktop) { - stkitem *newstack; - int newsize= (stktop==0) ? 16 : 2*stktop; - /* try to resize the stack */ - assert(newsize>stktop); - newstack=(stkitem*)malloc(newsize*sizeof(stkitem)); - if (newstack==NULL) - error(102,"parser stack"); /* stack overflow (recursive include?) */ - /* swap the stacks */ - memcpy(newstack,stack,stkidx*sizeof(stkitem)); - if (stack!=NULL) - free(stack); - stack=newstack; - stktop=newsize; - } /* if */ - assert(stkidx'); /* termination character */ - lptr++; - while (*lptr<=' ' && *lptr!='\0') /* skip whitespace after quote */ - lptr++; - } else { - c='\0'; - } /* if */ - - i=0; - while (*lptr!=c && *lptr!='\0' && i0 && name[i-1]<=' ') - i--; /* strip trailing whitespace */ - assert(i>=0 && i are only read from the list of include directories. - */ - result=plungefile(name,(c!='>'),TRUE); - if (result) - add_constant(symname,1,sGLOBAL,0); - else if (!silent) - error(100,name); /* cannot read from ... (fatal error) */ - } /* if */ -} - -/* readline - * - * Reads in a new line from the input file pointed to by "inpf". readline() - * concatenates lines that end with a \ with the next line. If no more data - * can be read from the file, readline() attempts to pop off the previous file - * from the stack. If that fails too, it sets "freading" to 0. - * - * Global references: inpf,fline,inpfname,freading,icomment (altered) - */ -static void readline(unsigned char *line) -{ - int i,num,cont; - unsigned char *ptr; - - if (lptr==term_expr) - return; - num=sLINEMAX; - cont=FALSE; - do { - if (inpf==NULL || pc_eofsrc(inpf)) { - if (cont) - error(49); /* invalid line continuation */ - if (inpf!=NULL && inpf!=inpf_org) - pc_closesrc(inpf); - i=POPSTK_I(); - if (i==-1) { /* All's done; popstk() returns "stack is empty" */ - freading=FALSE; - *line='\0'; - /* when there is nothing more to read, the #if/#else stack should - * be empty and we should not be in a comment - */ - assert(iflevel>=0); - if (iflevel>0) - error(1,"#endif","-end of file-"); - else if (icomment!=0) - error(1,"*/","-end of file-"); - return; - } /* if */ - fline=i; - fcurrent=(short)POPSTK_I(); - icomment=(short)POPSTK_I(); - sc_is_utf8=(short)POPSTK_I(); - iflevel=(short)POPSTK_I(); - skiplevel=iflevel; /* this condition held before including the file */ - assert(!SKIPPING); /* idem ditto */ - curlibrary=(constvalue *)POPSTK_P(); - free(inpfname); /* return memory allocated for the include file name */ - inpfname=(char *)POPSTK_P(); - inpf=(FILE *)POPSTK_P(); - insert_dbgfile(inpfname); - setfiledirect(inpfname); - listline=-1; /* force a #line directive when changing the file */ - } /* if */ - - if (pc_readsrc(inpf,line,num)==NULL) { - *line='\0'; /* delete line */ - cont=FALSE; - } else { - /* check whether to erase leading spaces */ - if (cont) { - unsigned char *ptr=line; - while (*ptr<=' ' && *ptr!='\0') - ptr++; - if (ptr!=line) - memmove(line,ptr,strlen((char*)ptr)+1); - } /* if */ - cont=FALSE; - /* check whether a full line was read */ - if (strchr((char*)line,'\n')==NULL && !pc_eofsrc(inpf)) - error(75); /* line too long */ - /* check if the next line must be concatenated to this line */ - if ((ptr=(unsigned char*)strchr((char*)line,'\n'))==NULL) - ptr=(unsigned char*)strchr((char*)line,'\r'); - if (ptr!=NULL && ptr>line) { - assert(*(ptr+1)=='\0'); /* '\n' or '\r' should be last in the string */ - while (ptr>line && *ptr<=' ') - ptr--; /* skip trailing whitespace */ - if (*ptr=='\\') { - cont=TRUE; - /* set '\a' at the position of '\\' to make it possible to check - * for a line continuation in a single line comment (error 49) - */ - *ptr++='\a'; - *ptr='\0'; /* erase '\n' (and any trailing whitespace) */ - } /* if */ - } /* if */ - num-=strlen((char*)line); - line+=strlen((char*)line); - } /* if */ - fline+=1; - } while (num>=0 && cont); -} - -/* stripcom - * - * Replaces all comments from the line by space characters. It updates - * a global variable ("icomment") for multiline comments. - * - * This routine also supports the C++ extension for single line comments. - * These comments are started with "//" and end at the end of the line. - * - * The function also detects (and manages) "documentation comments". The - * global variable "icomment" is set to 2 for documentation comments. - * - * Global references: icomment (private to "stripcom") - */ -static void stripcom(unsigned char *line) -{ - char c; - #if !defined SC_LIGHT - #define COMMENT_LIMIT 100 - #define COMMENT_MARGIN 40 /* length of the longest word */ - char comment[COMMENT_LIMIT+COMMENT_MARGIN]; - int commentidx=0; - int skipstar=TRUE; - static int prev_singleline=FALSE; - int singleline=prev_singleline; - - prev_singleline=FALSE; /* preset */ - #endif - - while (*line){ - if (icomment!=0) { - if (*line=='*' && *(line+1)=='/') { - #if !defined SC_LIGHT - if (icomment==2) { - assert(commentidx0) - insert_docstring(comment); - } /* if */ - #endif - icomment=0; /* comment has ended */ - *line=' '; /* replace '*' and '/' characters by spaces */ - *(line+1)=' '; - line+=2; - } else { - if (*line=='/' && *(line+1)=='*') - error(216); /* nested comment */ - #if !defined SC_LIGHT - /* collect the comment characters in a string */ - if (icomment==2) { - if (skipstar && (*line!='\0' && *line<=' ' || *line=='*')) { - /* ignore leading whitespace and '*' characters */ - } else if (commentidxCOMMENT_LIMIT && *line!='\0' && *line<=' ') { - comment[commentidx]='\0'; - insert_docstring(comment); - commentidx=0; - } /* if */ - skipstar=FALSE; - } /* if */ - } /* if */ - #endif - *line=' '; /* replace comments by spaces */ - line+=1; - } /* if */ - } else { - if (*line=='/' && *(line+1)=='*'){ - icomment=1; /* start comment */ - #if !defined SC_LIGHT - /* there must be two "*" behind the slash and then white space */ - if (*(line+2)=='*' && *(line+3)<=' ') { - /* if we are not in a function, we must attach the previous block - * to the global documentation - */ - if (curfunc==NULL && get_docstring(0)!=NULL) - sc_attachdocumentation(NULL); - icomment=2; /* documentation comment */ - } /* if */ - commentidx=0; - skipstar=TRUE; - #endif - *line=' '; /* replace '/' and '*' characters by spaces */ - *(line+1)=' '; - line+=2; - if (icomment==2) - *line++=' '; - } else if (*line=='/' && *(line+1)=='/'){ /* comment to end of line */ - if (strchr((char*)line,'\a')!=NULL) - error(49); /* invalid line continuation */ - #if !defined SC_LIGHT - if (*(line+2)=='/' && *(line+3)<=' ') { - /* documentation comment */ - char *str=(char*)line+3; - char *end; - while (*str<=' ' && *str!='\0') - str++; /* skip leading whitespace */ - if ((end=strrchr(str,'\n'))!=NULL) - *end='\0';/* erase trailing '\n' */ - /* if there is a disjunct block, we may need to attach the previous - * block to the global documentation - */ - if (!singleline && curfunc==NULL && get_docstring(0)!=NULL) - sc_attachdocumentation(NULL); - insert_docstring(str); - prev_singleline=TRUE; - } /* if */ - #endif - *line++='\n'; /* put "newline" at first slash */ - *line='\0'; /* put "zero-terminator" at second slash */ - } else { - if (*line=='\"' || *line=='\''){ /* leave literals unaltered */ - c=*line; /* ending quote, single or double */ - line+=1; - while ((*line!=c || *(line-1)==sc_ctrlchar) && *line!='\0') - line+=1; - line+=1; /* skip final quote */ - } else { - line+=1; - } /* if */ - } /* if */ - } /* if */ - } /* while */ - #if !defined SC_LIGHT - if (icomment==2) { - assert(commentidx0) - insert_docstring(comment); - } /* if */ - #endif -} - -/* btoi - * - * Attempts to interpret a numeric symbol as a boolean value. On success - * it returns the number of characters processed (so the line pointer can be - * adjusted) and the value is stored in "val". Otherwise it returns 0 and - * "val" is garbage. - * - * A boolean value must start with "0b" - */ -static int btoi(cell *val,const unsigned char *curptr) -{ - const unsigned char *ptr; - - *val=0; - ptr=curptr; - if (*ptr=='0' && *(ptr+1)=='b') { - ptr+=2; - while (*ptr=='0' || *ptr=='1' || *ptr=='_') { - if (*ptr!='_') - *val=(*val<<1) | (*ptr-'0'); - ptr++; - } /* while */ - } else { - return 0; - } /* if */ - if (alphanum(*ptr)) /* number must be delimited by non-alphanumeric char */ - return 0; - else - return (int)(ptr-curptr); -} - -/* dtoi - * - * Attempts to interpret a numeric symbol as a decimal value. On success - * it returns the number of characters processed and the value is stored in - * "val". Otherwise it returns 0 and "val" is garbage. - */ -static int dtoi(cell *val,const unsigned char *curptr) -{ - const unsigned char *ptr; - - *val=0; - ptr=curptr; - if (!isdigit(*ptr)) /* should start with digit */ - return 0; - while (isdigit(*ptr) || *ptr=='_') { - if (*ptr!='_') - *val=(*val*10)+(*ptr-'0'); - ptr++; - } /* while */ - if (alphanum(*ptr)) /* number must be delimited by non-alphanumerical */ - return 0; - if (*ptr=='.' && isdigit(*(ptr+1))) - return 0; /* but a fractional part must not be present */ - return (int)(ptr-curptr); -} - -/* htoi - * - * Attempts to interpret a numeric symbol as a hexadecimal value. On - * success it returns the number of characters processed and the value is - * stored in "val". Otherwise it return 0 and "val" is garbage. - */ -static int htoi(cell *val,const unsigned char *curptr) -{ - const unsigned char *ptr; - - *val=0; - ptr=curptr; - if (!isdigit(*ptr)) /* should start with digit */ - return 0; - if (*ptr=='0' && *(ptr+1)=='x') { /* C style hexadecimal notation */ - ptr+=2; - while (ishex(*ptr) || *ptr=='_') { - if (*ptr!='_') { - assert(ishex(*ptr)); - *val= *val<<4; - if (isdigit(*ptr)) - *val+= (*ptr-'0'); - else - *val+= (tolower(*ptr)-'a'+10); - } /* if */ - ptr++; - } /* while */ - } else { - return 0; - } /* if */ - if (alphanum(*ptr)) - return 0; - else - return (int)(ptr-curptr); -} - -#if defined __GNUC__ -static double pow10(int value) -{ - double res=1.0; - while (value>=4) { - res*=10000.0; - value-=5; - } /* while */ - while (value>=2) { - res*=100.0; - value-=2; - } /* while */ - while (value>=1) { - res*=10.0; - value-=1; - } /* while */ - return res; -} -#endif - -/* ftoi - * - * Attempts to interpret a numeric symbol as a rational number, either as - * IEEE 754 single/double precision floating point or as a fixed point integer. - * On success it returns the number of characters processed and the value is - * stored in "val". Otherwise it returns 0 and "val" is unchanged. - * - * Pawn has stricter definition for rational numbers than most: - * o the value must start with a digit; ".5" is not a valid number, you - * should write "0.5" - * o a period must appear in the value, even if an exponent is given; "2e3" - * is not a valid number, you should write "2.0e3" - * o at least one digit must follow the period; "6." is not a valid number, - * you should write "6.0" - */ -static int ftoi(cell *val,const unsigned char *curptr) -{ - const unsigned char *ptr; - double fnum,ffrac,fmult; - unsigned long dnum,dbase; - int i, ignore; - - assert(rational_digits>=0 && rational_digits<9); - for (i=0,dbase=1; i0 && !ignore) { - error(222); /* number of digits exceeds rational number precision */ - ignore=TRUE; - } /* if */ - } /* if */ - ptr++; - } /* while */ - fnum += ffrac*fmult; /* form the number so far */ - if (*ptr=='e') { /* optional fractional part */ - int exp,sign; - ptr++; - if (*ptr=='-') { - sign=-1; - ptr++; - } else { - sign=1; - } /* if */ - if (!isdigit(*ptr)) /* 'e' should be followed by a digit */ - return 0; - exp=0; - while (isdigit(*ptr)) { - exp=(exp*10)+(*ptr-'0'); - ptr++; - } /* while */ - #if defined __GNUC__ - fmult=pow10(exp*sign); - #else - fmult=pow(10,exp*sign); - #endif - fnum *= fmult; - dnum *= (unsigned long)(fmult+0.5); - } /* if */ - - /* decide how to store the number */ - if (sc_rationaltag==0) { - error(70); /* rational number support was not enabled */ - *val=0; - } else if (rational_digits==0) { - /* floating point */ - #if PAWN_CELL_SIZE==32 - float value=(float)fnum; - *val=*((cell *)&value); - #if !defined NDEBUG - /* I assume that the C/C++ compiler stores "float" values in IEEE 754 - * format (as mandated in the ANSI standard). Test this assumption - * anyway. - * Note: problems have been reported with GCC 3.2.x, version 3.3.x works. - */ - { float test1 = 0.0, test2 = 50.0, test3 = -50.0; - uint32_t bit = 1; - /* test 0.0 == all bits 0 */ - assert(*(uint32_t*)&test1==0x00000000L); - /* test sign & magnitude format */ - assert(((*(uint32_t*)&test2) ^ (*(uint32_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1))); - /* test a known value */ - assert(*(uint32_t*)&test2==0x42480000L); - } - #endif - #elif PAWN_CELL_SIZE==64 - *val=*((cell *)&fnum); - #if !defined NDEBUG - /* I assume that the C/C++ compiler stores "double" values in IEEE 754 - * format (as mandated in the ANSI standard). - */ - { float test1 = 0.0, test2 = 50.0, test3 = -50.0; - uint64_t bit = 1; - /* test 0.0 == all bits 0 */ - assert(*(uint64_t*)&test1==0x00000000L); - /* test sign & magnitude format */ - assert(((*(uint64_t*)&test2) ^ (*(uint64_t*)&test3)) == (bit << (PAWN_CELL_SIZE-1))); - } - #endif - #else - #error Unsupported cell size - #endif - } else { - /* fixed point */ - *val=(cell)dnum; - } /* if */ - - return (int)(ptr-curptr); -} - -/* number - * - * Reads in a number (binary, decimal or hexadecimal). It returns the number - * of characters processed or 0 if the symbol couldn't be interpreted as a - * number (in this case the argument "val" remains unchanged). This routine - * relies on the 'early dropout' implementation of the logical or (||) - * operator. - * - * Note: the routine doesn't check for a sign (+ or -). The - is checked - * for at "hier2()" (in fact, it is viewed as an operator, not as a - * sign) and the + is invalid (as in K&R C, and unlike ANSI C). - */ -static int number(cell *val,const unsigned char *curptr) -{ - int i; - cell value; - - if ((i=btoi(&value,curptr))!=0 /* binary? */ - || (i=htoi(&value,curptr))!=0 /* hexadecimal? */ - || (i=dtoi(&value,curptr))!=0) /* decimal? */ - { - *val=value; - return i; - } else { - return 0; /* else not a number */ - } /* if */ -} - -static void chrcat(char *str,char chr) -{ - str=strchr(str,'\0'); - *str++=chr; - *str='\0'; -} - -static int preproc_expr(cell *val,int *tag) -{ - int result; - int index; - cell code_index; - char *term; - - /* Disable staging; it should be disabled already because - * expressions may not be cut off half-way between conditional - * compilations. Reset the staging index, but keep the code - * index. - */ - if (stgget(&index,&code_index)) { - error(57); /* unfinished expression */ - stgdel(0,code_index); - stgset(FALSE); - } /* if */ - assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must point inside the string */ - #if !defined NO_DEFINE - /* preprocess the string */ - substallpatterns(pline,sLINEMAX); - assert((lptr-pline)<(int)strlen((char*)pline)); /* lptr must STILL point inside the string */ - #endif - /* append a special symbol to the string, so the expression - * analyzer won't try to read a next line when it encounters - * an end-of-line - */ - assert(strlen((char*)pline)=0); - if (iflevel>=sCOMP_STACK) - error(102,"Conditional compilation stack"); /* table overflow */ - iflevel++; - if (SKIPPING) - break; /* break out of switch */ - skiplevel=iflevel; - preproc_expr(&val,NULL); /* get value (or 0 on error) */ - ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE); - check_empty(lptr); - break; - case tpELSE: - case tpELSEIF: - ret=CMD_IF; - assert(iflevel>=0); - if (iflevel==0) { - error(26); /* no matching #if */ - errorset(sRESET,0); - } else { - /* check for earlier #else */ - if ((ifstack[iflevel-1] & HANDLED_ELSE)==HANDLED_ELSE) { - if (tok==tpELSEIF) - error(61); /* #elseif directive may not follow an #else */ - else - error(60); /* multiple #else directives between #if ... #endif */ - errorset(sRESET,0); - } else { - assert(iflevel>0); - /* if there has been a "parse mode" on this level, set "skip mode", - * otherwise, clear "skip mode" - */ - if ((ifstack[iflevel-1] & PARSEMODE)==PARSEMODE) { - /* there has been a parse mode already on this level, so skip the rest */ - ifstack[iflevel-1] |= (char)SKIPMODE; - } else { - /* previous conditions were all FALSE */ - if (tok==tpELSEIF) { - /* get new expression */ - preproc_expr(&val,NULL); /* get value (or 0 on error) */ - ifstack[iflevel-1]=(char)(val ? PARSEMODE : SKIPMODE); - } else { - /* a simple #else, clear skip mode */ - ifstack[iflevel-1] &= (char)~SKIPMODE; - } /* if */ - } /* if */ - } /* if */ - } /* if */ - check_empty(lptr); - break; - case tpENDIF: - ret=CMD_IF; - if (iflevel==0){ - error(26); /* no matching "#if" */ - errorset(sRESET,0); - } else { - iflevel--; - if (iflevel0) { - free(inpfname); - inpfname=duplicatestring(pathname); - if (inpfname==NULL) - error(103); /* insufficient memory */ - } /* if */ - } /* if */ - check_empty(lptr); - break; - case tpLINE: - if (!SKIPPING) { - if (lex(&val,&str)!=tNUMBER) - error(8); /* invalid/non-constant expression */ - fline=(int)val; - } /* if */ - check_empty(lptr); - break; - case tpASSERT: - if (!SKIPPING && (sc_debug & sCHKBOUNDS)!=0) { - for (str=(char*)lptr; *str<=' ' && *str!='\0'; str++) - /* nothing */; /* save start of expression */ - preproc_expr(&val,NULL); /* get constant expression (or 0 on error) */ - if (!val) - error(110,str); /* assertion failed */ - check_empty(lptr); - } /* if */ - break; - case tpPRAGMA: - if (!SKIPPING) { - if (lex(&val,&str)==tSYMBOL) { - if (strcmp(str,"amxlimit")==0) { - preproc_expr(&pc_amxlimit,NULL); - } else if (strcmp(str,"amxram")==0) { - preproc_expr(&pc_amxram,NULL); - } else if (strcmp(str,"codepage")==0) { - char name[sNAMEMAX+1]; - while (*lptr<=' ' && *lptr!='\0') - lptr++; - if (*lptr=='"') { - lptr=getstring((unsigned char*)name,sizeof name,lptr); - } else { - int i; - for (i=0; i9) { - error(68); /* invalid rational number precision */ - digits=0; - } /* if */ - if (*lptr==')') - lptr++; - } /* if */ - /* add the tag (make it public) and check the values */ - i=pc_addtag(name); - exporttag(i); - if (sc_rationaltag==0 || (sc_rationaltag==i && rational_digits==(int)digits)) { - sc_rationaltag=i; - rational_digits=(int)digits; - } else { - error(69); /* rational number format already set, can only be set once */ - } /* if */ - } else if (strcmp(str,"semicolon")==0) { - cell val; - preproc_expr(&val,NULL); - sc_needsemicolon=(int)val; - } else if (strcmp(str,"tabsize")==0) { - cell val; - preproc_expr(&val,NULL); - sc_tabsize=(int)val; - } else if (strcmp(str,"align")==0) { - sc_alignnext=TRUE; - } else if (strcmp(str,"unused")==0) { - char name[sNAMEMAX+1]; - int i,comma; - symbol *sym; - do { - /* get the name */ - while (*lptr<=' ' && *lptr!='\0') - lptr++; - for (i=0; iusage |= uREAD; - if (sym->ident==iVARIABLE || sym->ident==iREFERENCE - || sym->ident==iARRAY || sym->ident==iREFARRAY) - sym->usage |= uWRITTEN; - } else { - error(17,name); /* undefined symbol */ - } /* if */ - /* see if a comma follows the name */ - while (*lptr<=' ' && *lptr!='\0') - lptr++; - comma= (*lptr==','); - if (comma) - lptr++; - } while (comma); - } else { - error(207); /* unknown #pragma */ - } /* if */ - } else { - error(207); /* unknown #pragma */ - } /* if */ - check_empty(lptr); - } /* if */ - break; - case tpENDINPUT: - case tpENDSCRPT: - if (!SKIPPING) { - check_empty(lptr); - assert(inpf!=NULL); - if (inpf!=inpf_org) - pc_closesrc(inpf); - inpf=NULL; - } /* if */ - break; -#if !defined NOEMIT - case tpEMIT: { - /* write opcode to output file */ - char name[40]; - int i; - while (*lptr<=' ' && *lptr!='\0') - lptr++; - for (i=0; i<40 && (isalpha(*lptr) || *lptr=='.'); i++,lptr++) - name[i]=(char)tolower(*lptr); - name[i]='\0'; - stgwrite("\t"); - stgwrite(name); - stgwrite(" "); - code_idx+=opcodes(1); - /* write parameter (if any) */ - while (*lptr<=' ' && *lptr!='\0') - lptr++; - if (*lptr!='\0') { - symbol *sym; - tok=lex(&val,&str); - switch (tok) { - case tNUMBER: - case tRATIONAL: - outval(val,FALSE); - code_idx+=opargs(1); - break; - case tSYMBOL: - sym=findloc(str); - if (sym==NULL) - sym=findglb(str,sSTATEVAR); - if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) { - error(17,str); /* undefined symbol */ - } else { - outval(sym->addr,FALSE); - /* mark symbol as "used", unknown whether for read or write */ - markusage(sym,uREAD | uWRITTEN); - code_idx+=opargs(1); - } /* if */ - break; - default: { - char s2[20]; - extern char *sc_tokens[];/* forward declaration */ - if (tok<256) - sprintf(s2,"%c",(char)tok); - else - strcpy(s2,sc_tokens[tok-tFIRST]); - error(1,sc_tokens[tSYMBOL-tFIRST],s2); - break; - } /* case */ - } /* switch */ - } /* if */ - stgwrite("\n"); - check_empty(lptr); - break; - } /* case */ -#endif -#if !defined NO_DEFINE - case tpDEFINE: { - ret=CMD_DEFINE; - if (!SKIPPING) { - char *pattern,*substitution; - const unsigned char *start,*end; - int count,prefixlen; - stringpair *def; - /* find the pattern to match */ - while (*lptr<=' ' && *lptr!='\0') - lptr++; - start=lptr; /* save starting point of the match pattern */ - count=0; - while (*lptr>' ' && *lptr!='\0') { - litchar(&lptr,0); /* litchar() advances "lptr" and handles escape characters */ - count++; - } /* while */ - end=lptr; - /* check pattern to match */ - if (!alpha(*start)) { - error(74); /* pattern must start with an alphabetic character */ - break; - } /* if */ - /* store matched pattern */ - pattern=(char*)malloc(count+1); - if (pattern==NULL) - error(103); /* insufficient memory */ - lptr=start; - count=0; - while (lptr!=end) { - assert(lptr=2 && isdigit(pattern[count-1]) && pattern[count-2]=='%') - pattern[count-2]='\0'; - /* find substitution string */ - while (*lptr<=' ' && *lptr!='\0') - lptr++; - start=lptr; /* save starting point of the match pattern */ - count=0; - end=NULL; - while (*lptr!='\0') { - /* keep position of the start of trailing whitespace */ - if (*lptr<=' ') { - if (end==NULL) - end=lptr; - } else { - end=NULL; - } /* if */ - count++; - lptr++; - } /* while */ - if (end==NULL) - end=lptr; - /* store matched substitution */ - substitution=(char*)malloc(count+1); /* +1 for '\0' */ - if (substitution==NULL) - error(103); /* insufficient memory */ - lptr=start; - count=0; - while (lptr!=end) { - assert(lptr0); - if ((def=find_subst(pattern,prefixlen))!=NULL) { - if (strcmp(def->first,pattern)!=0 || strcmp(def->second,substitution)!=0) - error(201,pattern); /* redefinition of macro (non-identical) */ - delete_subst(pattern,prefixlen); - } /* if */ - /* add the pattern/substitution pair to the list */ - assert(strlen(pattern)>0); - insert_subst(pattern,substitution,prefixlen); - free(pattern); - free(substitution); - } /* if */ - break; - } /* case */ - case tpUNDEF: - if (!SKIPPING) { - if (lex(&val,&str)==tSYMBOL) { - ret=delete_subst(str,strlen(str)); - if (!ret) { - /* also undefine normal constants */ - symbol *sym=findconst(str,NULL); - if (sym!=NULL && (sym->usage & (uENUMROOT | uENUMFIELD))==0) { - delete_symbol(&glbtab,sym); - ret=TRUE; - } /* if */ - } /* if */ - if (!ret) - error(17,str); /* undefined symbol */ - } else { - error(20,str); /* invalid symbol name */ - } /* if */ - check_empty(lptr); - } /* if */ - break; -#endif - case tpERROR: - while (*lptr<=' ' && *lptr!='\0') - lptr++; - if (!SKIPPING) - error(111,lptr); /* user error */ - break; - default: - error(31); /* unknown compiler directive */ - ret=SKIPPING ? CMD_CONDFALSE : CMD_NONE; /* process as normal line */ - } /* switch */ - return ret; -} - -#if !defined NO_DEFINE -static int is_startstring(const unsigned char *string) -{ - if (*string=='\"' || *string=='\'') - return TRUE; /* "..." */ - - if (*string=='!') { - string++; - if (*string=='\"' || *string=='\'') - return TRUE; /* !"..." */ - if (*string==sc_ctrlchar) { - string++; - if (*string=='\"' || *string=='\'') - return TRUE; /* !\"..." */ - } /* if */ - } else if (*string==sc_ctrlchar) { - string++; - if (*string=='\"' || *string=='\'') - return TRUE; /* \"..." */ - if (*string=='!') { - string++; - if (*string=='\"' || *string=='\'') - return TRUE; /* \!"..." */ - } /* if */ - } /* if */ - - return FALSE; -} - -static const unsigned char *skipstring(const unsigned char *string) -{ - char endquote; - int flags=0; - - while (*string=='!' || *string==sc_ctrlchar) { - if (*string==sc_ctrlchar) - flags=RAWMODE; - string++; - } /* while */ - - endquote=*string; - assert(endquote=='"' || endquote=='\''); - string++; /* skip open quote */ - while (*string!=endquote && *string!='\0') - litchar(&string,flags); - return string; -} - -static const unsigned char *skippgroup(const unsigned char *string) -{ - int nest=0; - char open=*string; - char close; - - switch (open) { - case '(': - close=')'; - break; - case '{': - close='}'; - break; - case '[': - close=']'; - break; - case '<': - close='>'; - break; - default: - assert(0); - close='\0'; /* only to avoid a compiler warning */ - }/* switch */ - - string++; - while (*string!=close || nest>0) { - if (*string==open) - nest++; - else if (*string==close) - nest--; - else if (is_startstring(string)) - string=skipstring(string); - if (*string=='\0') - break; - string++; - } /* while */ - return string; -} - -static char *strdel(char *str,size_t len) -{ - size_t length=strlen(str); - if (len>length) - len=length; - memmove(str, str+len, length-len+1); /* include EOS byte */ - return str; -} - -static char *strins(char *dest,char *src,size_t srclen) -{ - size_t destlen=strlen(dest); - assert(srclen<=strlen(src)); - memmove(dest+srclen, dest, destlen+1);/* include EOS byte */ - memcpy(dest, src, srclen); - return dest; -} - -static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char *substitution) -{ - int prefixlen; - const unsigned char *p,*s,*e; - unsigned char *args[10]; - int match,arg,len; - - memset(args,0,sizeof args); - - /* check the length of the prefix */ - for (prefixlen=0,s=(unsigned char*)pattern; alphanum(*s); prefixlen++,s++) - /* nothing */; - assert(prefixlen>0); - assert(strncmp((char*)line,pattern,prefixlen)==0); - - /* pattern prefix matches; match the rest of the pattern, gather - * the parameters - */ - s=line+prefixlen; - p=(unsigned char*)pattern+prefixlen; - match=TRUE; /* so far, pattern matches */ - while (match && *s!='\0' && *p!='\0') { - if (*p=='%') { - p++; /* skip '%' */ - if (isdigit(*p)) { - arg=*p-'0'; - assert(arg>=0 && arg<=9); - p++; /* skip parameter id */ - assert(*p!='\0'); - /* match the source string up to the character after the digit - * (skipping strings in the process - */ - e=s; - while (*e!=*p && *e!='\0' && *e!='\n') { - if (is_startstring(e)) /* skip strings */ - e=skipstring(e); - else if (strchr("({[",*e)!=NULL) /* skip parenthized groups */ - e=skippgroup(e); - if (*e!='\0') - e++; /* skip non-alphapetic character (or closing quote of - * a string, or the closing paranthese of a group) */ - } /* while */ - /* store the parameter (overrule any earlier) */ - if (args[arg]!=NULL) - free(args[arg]); - len=(int)(e-s); - args[arg]=(unsigned char*)malloc(len+1); - if (args[arg]==NULL) - error(103); /* insufficient memory */ - strlcpy((char*)args[arg],(char*)s,len+1); - /* character behind the pattern was matched too */ - if (*e==*p) { - s=e+1; - } else if (*e=='\n' && *p==';' && *(p+1)=='\0' && !sc_needsemicolon) { - s=e; /* allow a trailing ; in the pattern match to end of line */ - } else { - assert(*e=='\0' || *e=='\n'); - match=FALSE; - s=e; - } /* if */ - p++; - } else { - match=FALSE; - } /* if */ - } else if (*p==';' && *(p+1)=='\0' && !sc_needsemicolon) { - /* source may be ';' or end of the line */ - while (*s<=' ' && *s!='\0') - s++; /* skip white space */ - if (*s!=';' && *s!='\0') - match=FALSE; - p++; /* skip the semicolon in the pattern */ - } else { - cell ch; - /* skip whitespace between two non-alphanumeric characters, except - * for two identical symbols - */ - assert((char*)p>pattern); - if (!alphanum(*p) && *(p-1)!=*p) - while (*s<=' ' && *s!='\0') - s++; /* skip white space */ - ch=litchar(&p,0); /* this increments "p" */ - if (*s!=ch) - match=FALSE; - else - s++; /* this character matches */ - } /* if */ - } /* while */ - - if (match && *p=='\0') { - /* if the last character to match is an alphanumeric character, the - * current character in the source may not be alphanumeric - */ - assert(p>(unsigned char*)pattern); - if (alphanum(*(p-1)) && alphanum(*s)) - match=FALSE; - } /* if */ - - if (match) { - /* calculate the length of the substituted string */ - for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) { - if (*e=='%' && isdigit(*(e+1))) { - arg=*(e+1)-'0'; - assert(arg>=0 && arg<=9); - if (args[arg]!=NULL) - len+=strlen((char*)args[arg]); - else - len+=2; /* copy '%' plus digit */ - e++; /* skip %, digit is skipped later */ - } else { - len++; - } /* if */ - } /* for */ - /* check length of the string after substitution */ - if (strlen((char*)line) + len - (int)(s-line) > buffersize) { - error(75); /* line too long */ - } else { - /* substitute pattern */ - strdel((char*)line,(int)(s-line)); - for (e=(unsigned char*)substitution,s=line; *e!='\0'; e++) { - if (*e=='%' && isdigit(*(e+1))) { - arg=*(e+1)-'0'; - assert(arg>=0 && arg<=9); - if (args[arg]!=NULL) { - strins((char*)s,(char*)args[arg],strlen((char*)args[arg])); - s+=strlen((char*)args[arg]); - } else { - error(236); /* parameter does not exist, incorrect #define pattern */ - strins((char*)s,(char*)e,2); - s+=2; - } /* if */ - e++; /* skip %, digit is skipped later */ - } else { - strins((char*)s,(char*)e,1); - s++; - } /* if */ - } /* for */ - } /* if */ - } /* if */ - - for (arg=0; arg<10; arg++) - if (args[arg]!=NULL) - free(args[arg]); - - return match; -} - -static void substallpatterns(unsigned char *line,int buffersize) -{ - unsigned char *start, *end; - int prefixlen; - stringpair *subst; - - start=line; - while (*start!='\0') { - /* find the start of a prefix (skip all non-alphabetic characters), - * also skip strings - */ - while (!alpha(*start) && *start!='\0') { - /* skip strings */ - if (is_startstring(start)) { - start=(unsigned char *)skipstring(start); - if (*start=='\0') - break; /* abort loop on error */ - } /* if */ - start++; /* skip non-alphapetic character (or closing quote of a string) */ - } /* while */ - if (*start=='\0') - break; /* abort loop on error */ - /* if matching the operator "defined", skip it plus the symbol behind it */ - if (strncmp((char*)start,"defined",7)==0 && *(start+7)<=' ') { - start+=7; /* skip "defined" */ - /* skip white space & parantheses */ - while (*start<=' ' && *start!='\0' || *start=='(') - start++; - /* skip the symbol behind it */ - while (alphanum(*start)) - start++; - /* drop back into the main loop */ - continue; - } /* if */ - /* get the prefix (length), look for a matching definition */ - prefixlen=0; - end=start; - while (alphanum(*end)) { - prefixlen++; - end++; - } /* while */ - assert(prefixlen>0); - subst=find_subst((char*)start,prefixlen); - if (subst!=NULL) { - /* properly match the pattern and substitute */ - if (!substpattern(start,buffersize-(int)(start-line),subst->first,subst->second)) - start=end; /* match failed, skip this prefix */ - /* match succeeded: do not update "start", because the substitution text - * may be matched by other macros - */ - } else { - start=end; /* no macro with this prefix, skip this prefix */ - } /* if */ - } /* while */ -} -#endif - -/* preprocess - * - * Reads a line by readline() into "pline" and performs basic preprocessing: - * deleting comments, skipping lines with false "#if.." code and recognizing - * other compiler directives. There is an indirect recursion: lex() calls - * preprocess() if a new line must be read, preprocess() calls command(), - * which at his turn calls lex() to identify the token. - * - * Global references: lptr (altered) - * pline (altered) - * freading (referred to only) - */ -SC_FUNC void preprocess(void) -{ - int iscommand; - - if (!freading) - return; - do { - readline(pline); - stripcom(pline); /* ??? no need for this when reading back from list file (in the second pass) */ - lptr=pline; /* set "line pointer" to start of the parsing buffer */ - iscommand=command(); - if (iscommand!=CMD_NONE) - errorset(sRESET,0); /* reset error flag ("panic mode") on empty line or directive */ - #if !defined NO_DEFINE - if (iscommand==CMD_NONE) { - assert(lptr!=term_expr); - substallpatterns(pline,sLINEMAX); - lptr=pline; /* reset "line pointer" to start of the parsing buffer */ - } /* if */ - #endif - if (sc_status==statFIRST && sc_listing && freading - && (iscommand==CMD_NONE || iscommand==CMD_EMPTYLINE || iscommand==CMD_DIRECTIVE)) - { - listline++; - if (fline!=listline) { - listline=fline; - setlinedirect(fline); - } /* if */ - if (iscommand==CMD_EMPTYLINE) - pc_writeasm(outf,"\n"); - else - pc_writeasm(outf,(char*)pline); - } /* if */ - } while (iscommand!=CMD_NONE && iscommand!=CMD_TERM && freading); /* enddo */ -} - -static const unsigned char *unpackedstring(const unsigned char *lptr,int flags) -{ - while (*lptr!='\"' && *lptr!='\0') { - if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ - lptr++; - continue; - } /* if */ - litadd(litchar(&lptr,flags | UTF8MODE)); /* litchar() alters "lptr" */ - } /* while */ - litadd(0); /* terminate string */ - return lptr; -} - -static const unsigned char *packedstring(const unsigned char *lptr,int flags) -{ - int i; - ucell val,c; - - i=sizeof(ucell)-(sCHARBITS/8); /* start at most significant byte */ - val=0; - while (*lptr!='\"' && *lptr!='\0') { - if (*lptr=='\a') { /* ignore '\a' (which was inserted at a line concatenation) */ - lptr++; - continue; - } /* if */ - c=litchar(&lptr,flags); /* litchar() alters "lptr" */ - if (c>=(ucell)(1 << sCHARBITS)) - error(43); /* character constant exceeds range */ - val |= (c << 8*i); - if (i==0) { - litadd(val); - val=0; - } /* if */ - i=(i+sizeof(ucell)-(sCHARBITS/8)) % sizeof(ucell); - } /* if */ - /* save last code; make sure there is at least one terminating zero character */ - if (i!=(int)(sizeof(ucell)-(sCHARBITS/8))) - litadd(val); /* at least one zero character in "val" */ - else - litadd(0); /* add full cell of zeros */ - return lptr; -} - -/* lex(lexvalue,lexsym) Lexical Analysis - * - * lex() first deletes leading white space, then checks for multi-character - * operators, keywords (including most compiler directives), numbers, - * labels, symbols and literals (literal characters are converted to a number - * and are returned as such). If every check fails, the line must contain - * a single-character operator. So, lex() returns this character. In the other - * case (something did match), lex() returns the number of the token. All - * these tokens have been assigned numbers above 255. - * - * Some tokens have "attributes": - * tNUMBER the value of the number is return in "lexvalue". - * tRATIONAL the value is in IEEE 754 encoding or in fixed point - * encoding in "lexvalue". - * tSYMBOL the first sNAMEMAX characters of the symbol are - * stored in a buffer, a pointer to this buffer is - * returned in "lexsym". - * tLABEL the first sNAMEMAX characters of the label are - * stored in a buffer, a pointer to this buffer is - * returned in "lexsym". - * tSTRING the string is stored in the literal pool, the index - * in the literal pool to this string is stored in - * "lexvalue". - * - * lex() stores all information (the token found and possibly its attribute) - * in global variables. This allows a token to be examined twice. If "_pushed" - * is true, this information is returned. - * - * Global references: lptr (altered) - * fline (referred to only) - * litidx (referred to only) - * _lextok, _lexval, _lexstr - * _pushed - */ - -static int _pushed; -static int _lextok; -static cell _lexval; -static char _lexstr[sLINEMAX+1]; -static int _lexnewline; - -SC_FUNC void lexinit(void) -{ - stkidx=0; /* index for pushstk() and popstk() */ - iflevel=0; /* preprocessor: nesting of "#if" is currently 0 */ - skiplevel=0; /* preprocessor: not currently skipping */ - icomment=0; /* currently not in a multiline comment */ - _pushed=FALSE; /* no token pushed back into lex */ - _lexnewline=FALSE; -} - -char *sc_tokens[] = { - "*=", "/=", "%=", "+=", "-=", "<<=", ">>>=", ">>=", "&=", "^=", "|=", - "||", "&&", "==", "!=", "<=", ">=", "<<", ">>>", ">>", "++", "--", - "...", "..", "::", - "assert", "break", "case", "char", "const", "continue", "default", - "defined", "do", "else", "enum", "exit", "for", "forward", "goto", - "if", "native", "new", "decl", "operator", "public", "return", "sizeof", - "sleep", "state", "static", "stock", "switch", "tagof", "while", - "#assert", "#define", "#else", "#elseif", "#emit", "#endif", "#endinput", - "#endscript", "#error", "#file", "#if", "#include", "#line", "#pragma", - "#tryinclude", "#undef", - ";", ";", "-integer value-", "-rational value-", "-identifier-", - "-label-", "-string-" - }; - -SC_FUNC int lex(cell *lexvalue,char **lexsym) -{ - int i,toolong,newline,stringflags; - char **tokptr; - const unsigned char *starttoken; - - if (_pushed) { - _pushed=FALSE; /* reset "_pushed" flag */ - *lexvalue=_lexval; - *lexsym=_lexstr; - return _lextok; - } /* if */ - - _lextok=0; /* preset all values */ - _lexval=0; - _lexstr[0]='\0'; - *lexvalue=_lexval; - *lexsym=_lexstr; - _lexnewline=FALSE; - if (!freading) - return 0; - - newline= (lptr==pline); /* does lptr point to start of line buffer */ - while (*lptr<=' ') { /* delete leading white space */ - if (*lptr=='\0') { - preprocess(); /* preprocess resets "lptr" */ - if (!freading) - return 0; - if (lptr==term_expr) /* special sequence to terminate a pending expression */ - return (_lextok=tENDEXPR); - _lexnewline=TRUE; /* set this after preprocess(), because - * preprocess() calls lex() recursively */ - newline=TRUE; - } else { - lptr+=1; - } /* if */ - } /* while */ - if (newline) { - stmtindent=0; - for (i=0; i<(int)(lptr-pline); i++) - if (pline[i]=='\t' && sc_tabsize>0) - stmtindent += (int)(sc_tabsize - (stmtindent+sc_tabsize) % sc_tabsize); - else - stmtindent++; - } /* if */ - - i=tFIRST; - tokptr=sc_tokens; - while (i<=tMIDDLE) { /* match multi-character operators */ - if (*lptr==**tokptr && match(*tokptr,FALSE)) { - _lextok=i; - if (pc_docexpr) /* optionally concatenate to documentation string */ - insert_autolist(*tokptr); - return _lextok; - } /* if */ - i+=1; - tokptr+=1; - } /* while */ - while (i<=tLAST) { /* match reserved words and compiler directives */ - if (*lptr==**tokptr && match(*tokptr,TRUE)) { - _lextok=i; - errorset(sRESET,0); /* reset error flag (clear the "panic mode")*/ - if (pc_docexpr) /* optionally concatenate to documentation string */ - insert_autolist(*tokptr); - return _lextok; - } /* if */ - i+=1; - tokptr+=1; - } /* while */ - - starttoken=lptr; /* save start pointer (for concatenating to documentation string) */ - if ((i=number(&_lexval,lptr))!=0) { /* number */ - _lextok=tNUMBER; - *lexvalue=_lexval; - lptr+=i; - } else if ((i=ftoi(&_lexval,lptr))!=0) { - _lextok=tRATIONAL; - *lexvalue=_lexval; - lptr+=i; - } else if (alpha(*lptr)) { /* symbol or label */ - /* Note: only sNAMEMAX characters are significant. The compiler - * generates a warning if a symbol exceeds this length. - */ - _lextok=tSYMBOL; - i=0; - toolong=0; - while (alphanum(*lptr)){ - _lexstr[i]=*lptr; - lptr+=1; - if (i=litmax) { - cell *p; - - litmax+=sDEF_LITMAX; - p=(cell *)realloc(litq,litmax*sizeof(cell)); - if (p==NULL) - error(102,"literal table"); /* literal table overflow (fatal error) */ - litq=p; - } /* if */ -} - -/* litadd - * - * Adds a value at the end of the literal queue. The literal queue is used - * for literal strings used in functions and for initializing array variables. - * - * Global references: litidx (altered) - * litq (altered) - */ -SC_FUNC void litadd(cell value) -{ - chk_grow_litq(); - assert(litidx=0 && pos<=litidx); - memmove(litq+(pos+1),litq+pos,(litidx-pos)*sizeof(cell)); - litidx++; - litq[pos]=value; -} - -/* litchar - * - * Return current literal character and increase the pointer to point - * just behind this literal character. - * - * Note: standard "escape sequences" are suported, but the backslash may be - * replaced by another character; the syntax '\ddd' is supported, - * but ddd must be decimal! - */ -static cell litchar(const unsigned char **lptr,int flags) -{ - cell c=0; - const unsigned char *cptr; - - cptr=*lptr; - if ((flags & RAWMODE)!=0 || *cptr!=sc_ctrlchar) { /* no escape character */ - #if !defined NO_UTF8 - if (sc_is_utf8 && (flags & UTF8MODE)!=0) { - c=get_utf8_char(cptr,&cptr); - assert(c>=0); /* file was already scanned for conformance to UTF-8 */ - } else { - #endif - #if !defined NO_CODEPAGE - c=cp_translate(cptr,&cptr); - #else - c=*cptr; - cptr+=1; - #endif - #if !defined NO_UTF8 - } /* if */ - #endif - } else { - cptr+=1; - if (*cptr==sc_ctrlchar) { - c=*cptr; /* \\ == \ (the escape character itself) */ - cptr+=1; - } else { - switch (*cptr) { - case 'a': /* \a == audible alarm */ - c=7; - cptr+=1; - break; - case 'b': /* \b == backspace */ - c=8; - cptr+=1; - break; - case 'e': /* \e == escape */ - c=27; - cptr+=1; - break; - case 'f': /* \f == form feed */ - c=12; - cptr+=1; - break; - case 'n': /* \n == NewLine character */ - c=10; - cptr+=1; - break; - case 'r': /* \r == carriage return */ - c=13; - cptr+=1; - break; - case 't': /* \t == horizontal TAB */ - c=9; - cptr+=1; - break; - case 'v': /* \v == vertical TAB */ - c=11; - cptr+=1; - break; - case 'x': - cptr+=1; - c=0; - while (ishex(*cptr)) { - if (isdigit(*cptr)) - c=(c<<4)+(*cptr-'0'); - else - c=(c<<4)+(tolower(*cptr)-'a'+10); - cptr++; - } /* while */ - if (*cptr==';') - cptr++; /* swallow a trailing ';' */ - break; - case '\'': /* \' == ' (single quote) */ - case '"': /* \" == " (single quote) */ - case '%': /* \% == % (percent) */ - c=*cptr; - cptr+=1; - break; - default: - if (isdigit(*cptr)) { /* \ddd */ - c=0; - while (*cptr>='0' && *cptr<='9') /* decimal! */ - c=c*10 + *cptr++ - '0'; - if (*cptr==';') - cptr++; /* swallow a trailing ';' */ - } else { - error(27); /* invalid character constant */ - } /* if */ - } /* switch */ - } /* if */ - } /* if */ - *lptr=cptr; - assert(c>=0); - return c; -} - -/* alpha - * - * Test if character "c" is alphabetic ("a".."z"), an underscore ("_") - * or an "at" sign ("@"). The "@" is an extension to standard C. - */ -static int alpha(char c) -{ - return (isalpha(c) || c=='_' || c==PUBLIC_CHAR); -} - -/* alphanum - * - * Test if character "c" is alphanumeric ("a".."z", "0".."9", "_" or "@") - */ -SC_FUNC int alphanum(char c) -{ - return (alpha(c) || isdigit(c)); -} - -/* ishex - * - * Test if character "c" is a hexadecimal digit ("0".."9" or "a".."f"). - */ -SC_FUNC int ishex(char c) -{ - return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); -} - -/* The local variable table must be searched backwards, so that the deepest - * nesting of local variables is searched first. The simplest way to do - * this is to insert all new items at the head of the list. - * In the global list, the symbols are kept in sorted order, so that the - * public functions are written in sorted order. - */ -static symbol *add_symbol(symbol *root,symbol *entry,int sort) -{ - symbol *newsym; - - if (sort) - while (root->next!=NULL && strcmp(entry->name,root->next->name)>0) - root=root->next; - - if ((newsym=(symbol *)malloc(sizeof(symbol)))==NULL) { - error(103); - return NULL; - } /* if */ - memcpy(newsym,entry,sizeof(symbol)); - newsym->next=root->next; - root->next=newsym; - return newsym; -} - -static void free_symbol(symbol *sym) -{ - arginfo *arg; - - /* free all sub-symbol allocated memory blocks, depending on the - * kind of the symbol - */ - assert(sym!=NULL); - if (sym->ident==iFUNCTN) { - /* run through the argument list; "default array" arguments - * must be freed explicitly; the tag list must also be freed */ - assert(sym->dim.arglist!=NULL); - for (arg=sym->dim.arglist; arg->ident!=0; arg++) { - if (arg->ident==iREFARRAY && arg->hasdefault) - free(arg->defvalue.array.data); - else if (arg->ident==iVARIABLE - && ((arg->hasdefault & uSIZEOF)!=0 || (arg->hasdefault & uTAGOF)!=0)) - free(arg->defvalue.size.symname); - assert(arg->tags!=NULL); - free(arg->tags); - } /* for */ - free(sym->dim.arglist); - if (sym->states!=NULL) { - delete_consttable(sym->states); - free(sym->states); - } /* if */ - } else if (sym->ident==iVARIABLE || sym->ident==iARRAY) { - if (sym->states!=NULL) { - delete_consttable(sym->states); - free(sym->states); - } /* if */ - } else if (sym->ident==iCONSTEXPR && (sym->usage & uENUMROOT)==uENUMROOT) { - /* free the constant list of an enum root */ - assert(sym->dim.enumlist!=NULL); - delete_consttable(sym->dim.enumlist); - free(sym->dim.enumlist); - } /* if */ - assert(sym->refer!=NULL); - free(sym->refer); - if (sym->documentation!=NULL) - free(sym->documentation); - free(sym); -} - -SC_FUNC void delete_symbol(symbol *root,symbol *sym) -{ - /* find the symbol and its predecessor - * (this function assumes that you will never delete a symbol that is not - * in the table pointed at by "root") - */ - assert(root!=sym); - while (root->next!=sym) { - root=root->next; - assert(root!=NULL); - } /* while */ - - /* unlink it, then free it */ - root->next=sym->next; - free_symbol(sym); -} - -SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_functions) -{ - symbol *sym,*parent_sym; - constvalue *stateptr; - int mustdelete; - - /* erase only the symbols with a deeper nesting level than the - * specified nesting level */ - while (root->next!=NULL) { - sym=root->next; - if (sym->compoundident) { - case iLABEL: - mustdelete=delete_labels; - break; - case iVARIABLE: - case iARRAY: - /* do not delete global variables if functions are preserved */ - mustdelete=delete_functions; - break; - case iREFERENCE: - /* always delete references (only exist as function parameters) */ - mustdelete=TRUE; - break; - case iREFARRAY: - /* a global iREFARRAY symbol is the return value of a function: delete - * this only if "globals" must be deleted; other iREFARRAY instances - * (locals) are also deleted - */ - mustdelete=delete_functions; - for (parent_sym=sym->parent; parent_sym!=NULL && parent_sym->ident!=iFUNCTN; parent_sym=parent_sym->parent) - assert(parent_sym->ident==iREFARRAY); - assert(parent_sym==NULL || (parent_sym->ident==iFUNCTN && parent_sym->parent==NULL)); - if (parent_sym==NULL || parent_sym->ident!=iFUNCTN) - mustdelete=TRUE; - break; - case iCONSTEXPR: - /* delete constants, except predefined constants */ - mustdelete=delete_functions || (sym->usage & uPREDEF)==0; - break; - case iFUNCTN: - /* optionally preserve globals (variables & functions), but - * NOT native functions - */ - mustdelete=delete_functions || (sym->usage & uNATIVE)!=0; - assert(sym->parent==NULL); - break; - case iARRAYCELL: - case iARRAYCHAR: - case iEXPRESSION: - case iVARARGS: - default: - assert(0); - break; - } /* switch */ - if (mustdelete) { - root->next=sym->next; - free_symbol(sym); - } else { - /* if the function was prototyped, but not implemented in this source, - * mark it as such, so that its use can be flagged - */ - if (sym->ident==iFUNCTN && (sym->usage & uDEFINE)==0) - sym->usage |= uMISSING; - if (sym->ident==iFUNCTN || sym->ident==iVARIABLE || sym->ident==iARRAY) - sym->usage &= ~uDEFINE; /* clear "defined" flag */ - /* set all states as "undefined" too */ - if (sym->states!=NULL) - for (stateptr=sym->states->next; stateptr!=NULL; stateptr=stateptr->next) - stateptr->value=0; - /* for user defined operators, also remove the "prototyped" flag, as - * user-defined operators *must* be declared before use - */ - if (sym->ident==iFUNCTN && !alpha(*sym->name)) - sym->usage &= ~uPROTOTYPED; - root=sym; /* skip the symbol */ - } /* if */ - } /* if */ -} - -/* The purpose of the hash is to reduce the frequency of a "name" - * comparison (which is costly). There is little interest in avoiding - * clusters in similar names, which is why this function is plain simple. - */ -SC_FUNC uint32_t namehash(const char *name) -{ - const unsigned char *ptr=(const unsigned char *)name; - int len=strlen(name); - if (len==0) - return 0L; - assert(len<256); - return (len<<24Lu) + (ptr[0]<<16Lu) + (ptr[len-1]<<8Lu) + (ptr[len>>1Lu]); -} - -static symbol *find_symbol(const symbol *root,const char *name,int fnumber,int automaton,int *cmptag) -{ - symbol *firstmatch=NULL; - symbol *sym=root->next; - int count=0; - unsigned long hash=namehash(name); - while (sym!=NULL) { - if (hash==sym->hash && strcmp(name,sym->name)==0 /* check name */ - && (sym->parent==NULL || sym->ident==iCONSTEXPR) /* sub-types (hierarchical types) are skipped, except for enum fields */ - && (sym->fnumber<0 || sym->fnumber==fnumber)) /* check file number for scope */ - { - assert(sym->states==NULL || sym->states->next!=NULL); /* first element of the state list is the "root" */ - if (sym->ident==iFUNCTN - || automaton<0 && sym->states==NULL - || automaton>=0 && sym->states!=NULL && state_getfsa(sym->states->next->index)==automaton) - { - if (cmptag==NULL) - return sym; /* return first match */ - /* return closest match or first match; count number of matches */ - if (firstmatch==NULL) - firstmatch=sym; - assert(cmptag!=NULL); - if (*cmptag==0) - count++; - if (*cmptag==sym->tag) { - *cmptag=1; /* good match found, set number of matches to 1 */ - return sym; - } /* if */ - } /* if */ - } /* */ - sym=sym->next; - } /* while */ - if (cmptag!=NULL && firstmatch!=NULL) - *cmptag=count; - return firstmatch; -} - -static symbol *find_symbol_child(const symbol *root,const symbol *sym) -{ - symbol *ptr=root->next; - while (ptr!=NULL) { - if (ptr->parent==sym) - return ptr; - ptr=ptr->next; - } /* while */ - return NULL; -} - -/* Adds "bywhom" to the list of referrers of "entry". Typically, - * bywhom will be the function that uses a variable or that calls - * the function. - */ -SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom) -{ - int count; - - assert(bywhom!=NULL); /* it makes no sense to add a "void" referrer */ - assert(entry!=NULL); - assert(entry->refer!=NULL); - - /* see if it is already there */ - for (count=0; countnumrefers && entry->refer[count]!=bywhom; count++) - /* nothing */; - if (countnumrefers) { - assert(entry->refer[count]==bywhom); - return TRUE; - } /* if */ - - /* see if there is an empty spot in the referrer list */ - for (count=0; countnumrefers && entry->refer[count]!=NULL; count++) - /* nothing */; - assert(count <= entry->numrefers); - if (count==entry->numrefers) { - symbol **refer; - int newsize=2*entry->numrefers; - assert(newsize>0); - /* grow the referrer list */ - refer=(symbol**)realloc(entry->refer,newsize*sizeof(symbol*)); - if (refer==NULL) - return FALSE; /* insufficient memory */ - /* initialize the new entries */ - entry->refer=refer; - for (count=entry->numrefers; countrefer[count]=NULL; - count=entry->numrefers; /* first empty spot */ - entry->numrefers=newsize; - } /* if */ - - /* add the referrer */ - assert(entry->refer[count]==NULL); - entry->refer[count]=bywhom; - return TRUE; -} - -SC_FUNC void markusage(symbol *sym,int usage) -{ - assert(sym!=NULL); - sym->usage |= (char)usage; - if ((usage & uWRITTEN)!=0) - sym->lnumber=fline; - /* check if (global) reference must be added to the symbol */ - if ((usage & (uREAD | uWRITTEN))!=0) { - /* only do this for global symbols */ - if (sym->vclass==sGLOBAL) { - /* "curfunc" should always be valid, since statements may not occurs - * outside functions; in the case of syntax errors, however, the - * compiler may arrive through this function - */ - if (curfunc!=NULL) - refer_symbol(sym,curfunc); - } /* if */ - } /* if */ -} - - -/* findglb - * - * Returns a pointer to the global symbol (if found) or NULL (if not found) - */ -SC_FUNC symbol *findglb(const char *name,int filter) -{ - /* find a symbol with a matching automaton first */ - symbol *sym=NULL; - - if (filter>sGLOBAL && sc_curstates>0) { - /* find a symbol whose state list matches the current fsa */ - sym=find_symbol(&glbtab,name,fcurrent,state_getfsa(sc_curstates),NULL); - if (sym!=NULL && sym->ident!=iFUNCTN) { - /* if sym!=NULL, we found a variable in the automaton; now we should - * also verify whether there is an intersection between the symbol's - * state list and the current state list - */ - assert(sym->states!=NULL && sym->states->next!=NULL); - if (!state_conflict_id(sc_curstates,sym->states->next->index)) - sym=NULL; - } /* if */ - } /* if */ - - /* if no symbol with a matching automaton exists, find a variable/function - * that has no state(s) attached to it - */ - if (sym==NULL) - sym=find_symbol(&glbtab,name,fcurrent,-1,NULL); - return sym; -} - -/* findloc - * - * Returns a pointer to the local symbol (if found) or NULL (if not found). - * See add_symbol() how the deepest nesting level is searched first. - */ -SC_FUNC symbol *findloc(const char *name) -{ - return find_symbol(&loctab,name,-1,-1,NULL); -} - -SC_FUNC symbol *findconst(const char *name,int *cmptag) -{ - symbol *sym; - - sym=find_symbol(&loctab,name,-1,-1,cmptag); /* try local symbols first */ - if (sym==NULL || sym->ident!=iCONSTEXPR) /* not found, or not a constant */ - sym=find_symbol(&glbtab,name,fcurrent,-1,cmptag); - if (sym==NULL || sym->ident!=iCONSTEXPR) - return NULL; - assert(sym->parent==NULL || (sym->usage & uENUMFIELD)!=0); - /* ^^^ constants have no hierarchy, but enumeration fields may have a parent */ - return sym; -} - -SC_FUNC symbol *finddepend(const symbol *parent) -{ - symbol *sym; - - sym=find_symbol_child(&loctab,parent); /* try local symbols first */ - if (sym==NULL) /* not found */ - sym=find_symbol_child(&glbtab,parent); - return sym; -} - -/* addsym - * - * Adds a symbol to the symbol table (either global or local variables, - * or global and local constants). - */ -SC_FUNC symbol *addsym(const char *name,cell addr,int ident,int vclass,int tag,int usage) -{ - symbol entry, **refer; - - /* labels may only be defined once */ - assert(ident!=iLABEL || findloc(name)==NULL); - - /* create an empty referrer list */ - if ((refer=(symbol**)malloc(sizeof(symbol*)))==NULL) { - error(103); /* insufficient memory */ - return NULL; - } /* if */ - *refer=NULL; - - /* first fill in the entry */ - memset(&entry,0,sizeof entry); - strcpy(entry.name,name); - entry.hash=namehash(name); - entry.addr=addr; - entry.codeaddr=code_idx; - entry.vclass=(char)vclass; - entry.ident=(char)ident; - entry.tag=tag; - entry.usage=(char)usage; - entry.fnumber=-1; /* assume global visibility (ignored for local symbols) */ - entry.lnumber=fline; - entry.numrefers=1; - entry.refer=refer; - - /* then insert it in the list */ - if (vclass==sGLOBAL) - return add_symbol(&glbtab,&entry,TRUE); - else - return add_symbol(&loctab,&entry,FALSE); -} - -SC_FUNC symbol *addvariable(const char *name,cell addr,int ident,int vclass,int tag, - int dim[],int numdim,int idxtag[]) -{ - symbol *sym; - - /* global variables may only be defined once - * One complication is that functions returning arrays declare an array - * with the same name as the function, so the assertion must allow for - * this special case. Another complication is that variables may be - * "redeclared" if they are local to an automaton (and findglb() will find - * the symbol without states if no symbol with states exists). - */ - assert(vclass!=sGLOBAL || (sym=findglb(name,sGLOBAL))==NULL || (sym->usage & uDEFINE)==0 - || sym->ident==iFUNCTN && sym==curfunc - || sym->states==NULL && sc_curstates>0); - - if (ident==iARRAY || ident==iREFARRAY) { - symbol *parent=NULL,*top; - int level; - sym=NULL; /* to avoid a compiler warning */ - for (level=0; leveldim.array.length=dim[level]; - top->dim.array.level=(short)(numdim-level-1); - top->x.tags.index=idxtag[level]; - top->parent=parent; - parent=top; - if (level==0) - sym=top; - } /* for */ - } else { - sym=addsym(name,addr,ident,vclass,tag,uDEFINE); - } /* if */ - return sym; -} - -/* getlabel - * - * Returns te next internal label number. The global variable sc_labnum is - * initialized to zero. - */ -SC_FUNC int getlabel(void) -{ - return sc_labnum++; -} - -/* itoh - * - * Converts a number to a hexadecimal string and returns a pointer to that - * string. This function is NOT re-entrant. - */ -SC_FUNC char *itoh(ucell val) -{ -static char itohstr[30]; - char *ptr; - int i,nibble[16]; /* a 64-bit hexadecimal cell has 16 nibbles */ - int max; - - #if PAWN_CELL_SIZE==16 - max=4; - #elif PAWN_CELL_SIZE==32 - max=8; - #elif PAWN_CELL_SIZE==64 - max=16; - #else - #error Unsupported cell size - #endif - ptr=itohstr; - for (i=0; i>=4; - } /* endfor */ - i=max-1; - while (nibble[i]==0 && i>0) /* search for highest non-zero nibble */ - i-=1; - while (i>=0){ - if (nibble[i]>=10) - *ptr++=(char)('a'+(nibble[i]-10)); - else - *ptr++=(char)('0'+nibble[i]); - i-=1; - } /* while */ - *ptr='\0'; /* and a zero-terminator */ - return itohstr; -} - diff --git a/compiler-init/compiler-init/sc3.c b/compiler-init/compiler-init/sc3.c deleted file mode 100644 index 65fec149..00000000 --- a/compiler-init/compiler-init/sc3.c +++ /dev/null @@ -1,2453 +0,0 @@ -/* Pawn compiler - Recursive descend expresion parser - * - * Copyright (c) ITB CompuPhase, 1997-2005 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc3.c 3598 2006-07-04 13:44:04Z thiadmer $ - */ -#include -#include -#include /* for _MAX_PATH */ -#include -#if defined FORTIFY - #include -#endif -#include "sc.h" - -static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval, - int (*hier)(value*),value *lval); -static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval); -static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval, - char *forcetag,int chkbitwise); -static int plnge1(int (*hier)(value *lval),value *lval); -static void plnge2(void (*oper)(void), - int (*hier)(value *lval), - value *lval1,value *lval2); -static cell calc(cell left,void (*oper)(),cell right,char *boolresult); -static int hier14(value *lval); -static int hier13(value *lval); -static int hier12(value *lval); -static int hier11(value *lval); -static int hier10(value *lval); -static int hier9(value *lval); -static int hier8(value *lval); -static int hier7(value *lval); -static int hier6(value *lval); -static int hier5(value *lval); -static int hier4(value *lval); -static int hier3(value *lval); -static int hier2(value *lval); -static int hier1(value *lval1); -static int primary(value *lval); -static void clear_value(value *lval); -static void callfunction(symbol *sym,value *lval_result,int matchparanthesis); -static int dbltest(void (*oper)(),value *lval1,value *lval2); -static int commutative(void (*oper)()); -static int constant(value *lval); - -static char lastsymbol[sNAMEMAX+1]; /* name of last function/variable */ -static int bitwise_opercount; /* count of bitwise operators in an expression */ -static int decl_heap=0; - -/* Function addresses of binary operators for signed operations */ -static void (*op1[17])(void) = { - os_mult,os_div,os_mod, /* hier3, index 0 */ - ob_add,ob_sub, /* hier4, index 3 */ - ob_sal,os_sar,ou_sar, /* hier5, index 5 */ - ob_and, /* hier6, index 8 */ - ob_xor, /* hier7, index 9 */ - ob_or, /* hier8, index 10 */ - os_le,os_ge,os_lt,os_gt, /* hier9, index 11 */ - ob_eq,ob_ne, /* hier10, index 15 */ -}; -/* These two functions are defined because the functions inc() and dec() in - * SC4.C have a different prototype than the other code generation functions. - * The arrays for user-defined functions use the function pointers for - * identifying what kind of operation is requested; these functions must all - * have the same prototype. As inc() and dec() are special cases already, it - * is simplest to add two "do-nothing" functions. - */ -static void user_inc(void) {} -static void user_dec(void) {} - -/* - * Searches for a binary operator a list of operators. The list is stored in - * the array "list". The last entry in the list should be set to 0. - * - * The index of an operator in "list" (if found) is returned in "opidx". If - * no operator is found, nextop() returns 0. - * - * If an operator is found in the expression, it cannot be used in a function - * call with omitted parantheses. Mark this... - * - * Global references: sc_allowproccall (modified) - */ -static int nextop(int *opidx,int *list) -{ - *opidx=0; - while (*list){ - if (matchtoken(*list)){ - sc_allowproccall=FALSE; - return TRUE; /* found! */ - } else { - list+=1; - *opidx+=1; - } /* if */ - } /* while */ - return FALSE; /* entire list scanned, nothing found */ -} - -SC_FUNC int check_userop(void (*oper)(void),int tag1,int tag2,int numparam, - value *lval,int *resulttag) -{ -static char *binoperstr[] = { "*", "/", "%", "+", "-", "", "", "", - "", "", "", "<=", ">=", "<", ">", "==", "!=" }; -static int binoper_savepri[] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, FALSE, - TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }; -static char *unoperstr[] = { "!", "-", "++", "--" }; -static void (*unopers[])(void) = { lneg, neg, user_inc, user_dec }; - char opername[4] = "", symbolname[sNAMEMAX+1]; - int i,swapparams,savepri,savealt; - int paramspassed; - symbol *sym; - - /* since user-defined operators on untagged operands are forbidden, we have - * a quick exit. - */ - assert(numparam==1 || numparam==2); - if (tag1==0 && (numparam==1 || tag2==0)) - return FALSE; - - savepri=savealt=FALSE; - /* find the name with the operator */ - if (numparam==2) { - if (oper==NULL) { - /* assignment operator: a special case */ - strcpy(opername,"="); - if (lval!=NULL && (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR)) - savealt=TRUE; - } else { - assert( (sizeof binoperstr / sizeof binoperstr[0]) == (sizeof op1 / sizeof op1[0]) ); - for (i=0; iusage & uDEFINE)==0*/) { /* ??? should not check uDEFINE; first pass clears these bits */ - /* check for commutative operators */ - if (tag1==tag2 || oper==NULL || !commutative(oper)) - return FALSE; /* not commutative, cannot swap operands */ - /* if arrived here, the operator is commutative and the tags are different, - * swap tags and try again - */ - assert(numparam==2); /* commutative operator must be a binary operator */ - operator_symname(symbolname,opername,tag2,tag1,numparam,tag1); - swapparams=TRUE; - sym=findglb(symbolname,sGLOBAL); - if (sym==NULL /*|| (sym->usage & uDEFINE)==0*/) - return FALSE; - } /* if */ - - /* check existance and the proper declaration of this function */ - if ((sym->usage & uMISSING)!=0 || (sym->usage & uPROTOTYPED)==0) { - char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ - funcdisplayname(symname,sym->name); - if ((sym->usage & uMISSING)!=0) - error(4,symname); /* function not defined */ - if ((sym->usage & uPROTOTYPED)==0) - error(71,symname); /* operator must be declared before use */ - } /* if */ - - /* we don't want to use the redefined operator in the function that - * redefines the operator itself, otherwise the snippet below gives - * an unexpected recursion: - * fixed:operator+(fixed:a, fixed:b) - * return a + b - */ - if (sym==curfunc) - return FALSE; - - /* for increment and decrement operators, the symbol must first be loaded - * (and stored back afterwards) - */ - if (oper==user_inc || oper==user_dec) { - assert(!savepri); - assert(lval!=NULL); - if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR) - pushreg(sPRI); /* save current address in PRI */ - rvalue(lval); /* get the symbol's value in PRI */ - } /* if */ - - assert(!savepri || !savealt); /* either one MAY be set, but not both */ - if (savepri) { - /* the chained comparison operators require that the ALT register is - * unmodified, so we save it here; actually, we save PRI because the normal - * instruction sequence (without user operator) swaps PRI and ALT - */ - pushreg(sPRI); /* right-hand operand is in PRI */ - } else if (savealt) { - /* for the assignment operator, ALT may contain an address at which the - * result must be stored; this address must be preserved accross the - * call - */ - assert(lval!=NULL); /* this was checked earlier */ - assert(lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); /* checked earlier */ - pushreg(sALT); - } /* if */ - - /* push parameters, call the function */ - paramspassed= (oper==NULL) ? 1 : numparam; - switch (paramspassed) { - case 1: - pushreg(sPRI); - break; - case 2: - /* note that 1) a function expects that the parameters are pushed - * in reversed order, and 2) the left operand is in the secondary register - * and the right operand is in the primary register */ - if (swapparams) { - pushreg(sALT); - pushreg(sPRI); - } else { - pushreg(sPRI); - pushreg(sALT); - } /* if */ - break; - default: - assert(0); - } /* switch */ - markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ - pushval((cell)paramspassed*sizeof(cell)); - assert(sym->ident==iFUNCTN); - ffcall(sym,NULL,paramspassed); - if (sc_status!=statSKIP) - markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ - if ((sym->usage & uNATIVE)!=0 && sym->x.lib!=NULL) - sym->x.lib->value += 1; /* increment "usage count" of the library */ - sideeffect=TRUE; /* assume functions carry out a side-effect */ - assert(resulttag!=NULL); - *resulttag=sym->tag; /* save tag of the called function */ - - if (savepri || savealt) - popreg(sALT); /* restore the saved PRI/ALT that into ALT */ - if (oper==user_inc || oper==user_dec) { - assert(lval!=NULL); - if (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR) - popreg(sALT); /* restore address (in ALT) */ - store(lval); /* store PRI in the symbol */ - moveto1(); /* make sure PRI is restored on exit */ - } /* if */ - return TRUE; -} - -SC_FUNC int matchtag(int formaltag,int actualtag,int allowcoerce) -{ - if (formaltag!=actualtag) { - /* if the formal tag is zero and the actual tag is not "fixed", the actual - * tag is "coerced" to zero - */ - if (!allowcoerce || formaltag!=0 || (actualtag & FIXEDTAG)!=0) - return FALSE; - } /* if */ - return TRUE; -} - -/* - * The AMX pseudo-processor has no direct support for logical (boolean) - * operations. These have to be done via comparing and jumping. Since we are - * already jumping through the code, we might as well implement an "early - * drop-out" evaluation (also called "short-circuit"). This conforms to - * standard C: - * - * expr1 || expr2 expr2 will only be evaluated if expr1 is false. - * expr1 && expr2 expr2 will only be evaluated if expr1 is true. - * - * expr1 || expr2 && expr3 expr2 will only be evaluated if expr1 is false - * and expr3 will only be evaluated if expr1 is - * false and expr2 is true. - * - * Code generation for the last example proceeds thus: - * - * evaluate expr1 - * operator || found - * jump to "l1" if result of expr1 not equal to 0 - * evaluate expr2 - * -> operator && found; skip to higher level in hierarchy diagram - * jump to "l2" if result of expr2 equal to 0 - * evaluate expr3 - * jump to "l2" if result of expr3 equal to 0 - * set expression result to 1 (true) - * jump to "l3" - * l2: set expression result to 0 (false) - * l3: - * <- drop back to previous hierarchy level - * jump to "l1" if result of expr2 && expr3 not equal to 0 - * set expression result to 0 (false) - * jump to "l4" - * l1: set expression result to 1 (true) - * l4: - * - */ - -/* Skim over terms adjoining || and && operators - * dropval The value of the expression after "dropping out". An "or" drops - * out when the left hand is TRUE, so dropval must be 1 on "or" - * expressions. - * endval The value of the expression when no expression drops out. In an - * "or" expression, this happens when both the left hand and the - * right hand are FALSE, so endval must be 0 for "or" expressions. - */ -static int skim(int *opstr,void (*testfunc)(int),int dropval,int endval, - int (*hier)(value*),value *lval) -{ - int lvalue,hits,droplab,endlab,opidx; - int allconst,foundop; - cell constval; - int index; - cell cidx; - - stgget(&index,&cidx); /* mark position in code generator */ - hits=FALSE; /* no logical operators "hit" yet */ - allconst=TRUE; /* assume all values "const" */ - constval=0; - droplab=0; /* to avoid a compiler warning */ - for ( ;; ) { - lvalue=plnge1(hier,lval); /* evaluate left expression */ - - allconst= allconst && (lval->ident==iCONSTEXPR); - if (allconst) { - if (hits) { - /* one operator was already found */ - if (testfunc==jmp_ne0) - lval->constval= lval->constval || constval; - else - lval->constval= lval->constval && constval; - } /* if */ - constval=lval->constval; /* save result accumulated so far */ - } /* if */ - - foundop=nextop(&opidx,opstr); - if ((foundop || hits) && (lval->ident==iARRAY || lval->ident==iREFARRAY)) - error(33, lval->sym ? (lval->sym->name ? lval->sym->name : "-unknown") : "-unknown-"); /* array was not indexed in an expression */ - if (foundop) { - if (!hits) { - /* this is the first operator in the list */ - hits=TRUE; - droplab=getlabel(); - } /* if */ - dropout(lvalue,testfunc,droplab,lval); - } else if (hits) { /* no (more) identical operators */ - dropout(lvalue,testfunc,droplab,lval); /* found at least one operator! */ - ldconst(endval,sPRI); - jumplabel(endlab=getlabel()); - setlabel(droplab); - ldconst(dropval,sPRI); - setlabel(endlab); - lval->sym=NULL; - lval->tag=pc_addtag("bool"); /* force tag to be "bool" */ - if (allconst) { - lval->ident=iCONSTEXPR; - lval->constval=constval; - stgdel(index,cidx); /* scratch generated code and calculate */ - } else { - lval->ident=iEXPRESSION; - lval->constval=0; - } /* if */ - return FALSE; - } else { - return lvalue; /* none of the operators in "opstr" were found */ - } /* if */ - - } /* while */ -} - -/* - * Reads into the primary register the variable pointed to by lval if - * plunging through the hierarchy levels detected an lvalue. Otherwise - * if a constant was detected, it is loaded. If there is no constant and - * no lvalue, the primary register must already contain the expression - * result. - * - * After that, the compare routines "jmp_ne0" or "jmp_eq0" are called, which - * compare the primary register against 0, and jump to the "early drop-out" - * label "exit1" if the condition is true. - */ -static void dropout(int lvalue,void (*testfunc)(int val),int exit1,value *lval) -{ - if (lvalue) - rvalue(lval); - else if (lval->ident==iCONSTEXPR) - ldconst(lval->constval,sPRI); - (*testfunc)(exit1); -} - -static void checkfunction(value *lval) -{ - symbol *sym=lval->sym; - - if (sym==NULL || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) - return; /* no known symbol, or not a function result */ - - if ((sym->usage & uDEFINE)!=0) { - /* function is defined, can now check the return value (but make an - * exception for directly recursive functions) - */ - if (sym!=curfunc && (sym->usage & uRETVALUE)==0) { - char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ - funcdisplayname(symname,sym->name); - error(209,symname); /* function should return a value */ - } /* if */ - } else { - /* function not yet defined, set */ - sym->usage|=uRETVALUE; /* make sure that a future implementation of - * the function uses "return " */ - } /* if */ -} - -/* - * Plunge to a lower level - */ -static int plnge(int *opstr,int opoff,int (*hier)(value *lval),value *lval, - char *forcetag,int chkbitwise) -{ - int lvalue,opidx; - int count; - value lval2 = {0}; - - lvalue=plnge1(hier,lval); - if (nextop(&opidx,opstr)==0) - return lvalue; /* no operator in "opstr" found */ - if (lvalue) - rvalue(lval); - count=0; - do { - if (chkbitwise && count++>0 && bitwise_opercount!=0) - error(212); - opidx+=opoff; /* add offset to index returned by nextop() */ - plnge2(op1[opidx],hier,lval,&lval2); - if (op1[opidx]==ob_and || op1[opidx]==ob_or) - bitwise_opercount++; - if (forcetag!=NULL) - lval->tag=pc_addtag(forcetag); - } while (nextop(&opidx,opstr)); /* do */ - return FALSE; /* result of expression is not an lvalue */ -} - -/* plnge_rel - * - * Binary plunge to lower level; this is very simular to plnge, but - * it has special code generation sequences for chained operations. - */ -static int plnge_rel(int *opstr,int opoff,int (*hier)(value *lval),value *lval) -{ - int lvalue,opidx; - value lval2={0}; - int count; - - /* this function should only be called for relational operators */ - assert(op1[opoff]==os_le); - lvalue=plnge1(hier,lval); - if (nextop(&opidx,opstr)==0) - return lvalue; /* no operator in "opstr" found */ - if (lvalue) - rvalue(lval); - count=0; - lval->boolresult=TRUE; - do { - /* same check as in plnge(), but "chkbitwise" is always TRUE */ - if (count>0 && bitwise_opercount!=0) - error(212); - if (count>0) { - relop_prefix(); - *lval=lval2; /* copy right hand expression of the previous iteration */ - } /* if */ - opidx+=opoff; - plnge2(op1[opidx],hier,lval,&lval2); - if (count++>0) - relop_suffix(); - } while (nextop(&opidx,opstr)); /* enddo */ - lval->constval=lval->boolresult; - lval->tag=pc_addtag("bool"); /* force tag to be "bool" */ - return FALSE; /* result of expression is not an lvalue */ -} - -/* plnge1 - * - * Unary plunge to lower level - * Called by: skim(), plnge(), plnge2(), plnge_rel(), hier14() and hier13() - */ -static int plnge1(int (*hier)(value *lval),value *lval) -{ - int lvalue,index; - cell cidx; - - stgget(&index,&cidx); /* mark position in code generator */ - lvalue=(*hier)(lval); - if (lval->ident==iCONSTEXPR) - stgdel(index,cidx); /* load constant later */ - return lvalue; -} - -/* plnge2 - * - * Binary plunge to lower level - * Called by: plnge(), plnge_rel(), hier14() and hier1() - */ -static void plnge2(void (*oper)(void), - int (*hier)(value *lval), - value *lval1,value *lval2) -{ - int index; - cell cidx; - - stgget(&index,&cidx); /* mark position in code generator */ - if (lval1->ident==iCONSTEXPR) { /* constant on left side; it is not yet loaded */ - if (plnge1(hier,lval2)) - rvalue(lval2); /* load lvalue now */ - else if (lval2->ident==iCONSTEXPR) - ldconst(lval2->constval<constval<ident==iCONSTEXPR) { /* constant on right side */ - if (commutative(oper)) { /* test for commutative operators */ - value lvaltmp = {0}; - stgdel(index,cidx); /* scratch pushreg() and constant fetch (then - * fetch the constant again */ - ldconst(lval2->constval<constval<ident==iARRAY || lval1->ident==iREFARRAY) { - char *ptr=(lval1->sym!=NULL) ? lval1->sym->name : "-unknown-"; - error(33,ptr); /* array must be indexed */ - } else if (lval2->ident==iARRAY || lval2->ident==iREFARRAY) { - char *ptr=(lval2->sym!=NULL) ? lval2->sym->name : "-unknown-"; - error(33,ptr); /* array must be indexed */ - } /* if */ - /* ??? ^^^ should do same kind of error checking with functions */ - - /* check whether an "operator" function is defined for the tag names - * (a constant expression cannot be optimized in that case) - */ - if (check_userop(oper,lval1->tag,lval2->tag,2,NULL,&lval1->tag)) { - lval1->ident=iEXPRESSION; - lval1->constval=0; - } else if (lval1->ident==iCONSTEXPR && lval2->ident==iCONSTEXPR) { - /* only constant expression if both constant */ - stgdel(index,cidx); /* scratch generated code and calculate */ - if (!matchtag(lval1->tag,lval2->tag,FALSE)) - error(213); /* tagname mismatch */ - lval1->constval=calc(lval1->constval,oper,lval2->constval,&lval1->boolresult); - } else { - if (!matchtag(lval1->tag,lval2->tag,FALSE)) - error(213); /* tagname mismatch */ - (*oper)(); /* do the (signed) operation */ - lval1->ident=iEXPRESSION; - } /* if */ - } /* if */ -} - -static cell truemodulus(cell a,cell b) -{ - return (a % b + b) % b; -} - -static cell calc(cell left,void (*oper)(),cell right,char *boolresult) -{ - if (oper==ob_or) - return (left | right); - else if (oper==ob_xor) - return (left ^ right); - else if (oper==ob_and) - return (left & right); - else if (oper==ob_eq) - return (left == right); - else if (oper==ob_ne) - return (left != right); - else if (oper==os_le) - return *boolresult &= (char)(left <= right), right; - else if (oper==os_ge) - return *boolresult &= (char)(left >= right), right; - else if (oper==os_lt) - return *boolresult &= (char)(left < right), right; - else if (oper==os_gt) - return *boolresult &= (char)(left > right), right; - else if (oper==os_sar) - return (left >> (int)right); - else if (oper==ou_sar) - return ((ucell)left >> (ucell)right); - else if (oper==ob_sal) - return ((ucell)left << (int)right); - else if (oper==ob_add) - return (left + right); - else if (oper==ob_sub) - return (left - right); - else if (oper==os_mult) - return (left * right); - else if (oper==os_div) - return (left - truemodulus(left,right)) / right; - else if (oper==os_mod) - return truemodulus(left,right); - else - error(29); /* invalid expression, assumed 0 (this should never occur) */ - return 0; -} - -SC_FUNC int expression(cell *val,int *tag,symbol **symptr,int chkfuncresult) -{ - int locheap=decl_heap; - value lval={0}; - - if (hier14(&lval)) - rvalue(&lval); - /* scrap any arrays left on the heap */ - assert(decl_heap>=locheap); - modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ - decl_heap=locheap; - - if (lval.ident==iCONSTEXPR && val!=NULL) /* constant expression */ - *val=lval.constval; - if (tag!=NULL) - *tag=lval.tag; - if (symptr!=NULL) - *symptr=lval.sym; - if (chkfuncresult) - checkfunction(&lval); - return lval.ident; -} - -SC_FUNC int sc_getstateid(constvalue **automaton,constvalue **state) -{ - char name[sNAMEMAX+1]; - cell val; - char *str; - int fsa,islabel; - - assert(automaton!=NULL); - assert(state!=NULL); - if (!(islabel=matchtoken(tLABEL)) && !needtoken(tSYMBOL)) - return 0; - - tokeninfo(&val,&str); - assert(strlen(str)index>0); - assert(strlen(str)index==0); - } /* if */ - assert(*automaton!=NULL); - fsa=(*automaton)->index; - - assert(*automaton!=NULL); - *state=state_find(name,fsa); - if (*state==NULL) { - char *fsaname=(*automaton)->name; - if (*fsaname=='\0') - fsaname="

"; - error(87,name,fsaname); /* unknown state for automaton */ - return 0; - } /* if */ - - return 1; -} - -SC_FUNC cell array_totalsize(symbol *sym) -{ - cell length; - - assert(sym!=NULL); - assert(sym->ident==iARRAY || sym->ident==iREFARRAY); - length=sym->dim.array.length; - if (sym->dim.array.level > 0) { - cell sublength=array_totalsize(finddepend(sym)); - if (sublength>0) - length=length+length*sublength; - else - length=0; - } /* if */ - return length; -} - -static cell array_levelsize(symbol *sym,int level) -{ - assert(sym!=NULL); - assert(sym->ident==iARRAY || sym->ident==iREFARRAY); - assert(level <= sym->dim.array.level); - while (level-- > 0) { - sym=finddepend(sym); - assert(sym!=NULL); - } /* if */ - return sym->dim.array.length; -} - -/* hier14 - * - * Lowest hierarchy level (except for the , operator). - * - * Global references: sc_intest (reffered to only) - * sc_allowproccall (modified) - */ -static int hier14(value *lval1) -{ - int lvalue; - value lval2={0},lval3={0}; - void (*oper)(void); - int tok,level,i; - cell val; - char *st; - int bwcount,leftarray; - cell arrayidx1[sDIMEN_MAX],arrayidx2[sDIMEN_MAX]; /* last used array indices */ - cell *org_arrayidx; - - bwcount=bitwise_opercount; - bitwise_opercount=0; - /* initialize the index arrays with unlikely constant indices; note that - * these indices will only be changed when the array is indexed with a - * constant, and that negative array indices are invalid (so actually, any - * negative value would do). - */ - for (i=0; iarrayidx; /* save current pointer, to reset later */ - if (lval1->arrayidx==NULL) - lval1->arrayidx=arrayidx1; - lvalue=plnge1(hier13,lval1); - if (lval1->ident!=iARRAYCELL && lval1->ident!=iARRAYCHAR) - lval1->arrayidx=NULL; - if (lval1->ident==iCONSTEXPR) /* load constant here */ - ldconst(lval1->constval,sPRI); - tok=lex(&val,&st); - switch (tok) { - case taOR: - oper=ob_or; - break; - case taXOR: - oper=ob_xor; - break; - case taAND: - oper=ob_and; - break; - case taADD: - oper=ob_add; - break; - case taSUB: - oper=ob_sub; - break; - case taMULT: - oper=os_mult; - break; - case taDIV: - oper=os_div; - break; - case taMOD: - oper=os_mod; - break; - case taSHRU: - oper=ou_sar; - break; - case taSHR: - oper=os_sar; - break; - case taSHL: - oper=ob_sal; - break; - case '=': /* simple assignment */ - oper=NULL; - if (sc_intest) - error(211); /* possibly unintended assignment */ - break; - default: - lexpush(); - bitwise_opercount=bwcount; - lval1->arrayidx=org_arrayidx; /* restore array index pointer */ - return lvalue; - } /* switch */ - - /* if we get here, it was an assignment; first check a few special cases - * and then the general */ - if (lval1->ident==iARRAYCHAR) { - /* special case, assignment to packed character in a cell is permitted */ - lvalue=TRUE; - } else if (lval1->ident==iARRAY || lval1->ident==iREFARRAY) { - /* array assignment is permitted too (with restrictions) */ - if (oper) - return error(23); /* array assignment must be simple assigment */ - assert(lval1->sym!=NULL); - if (array_totalsize(lval1->sym)==0) - return error(46,lval1->sym->name); /* unknown array size */ - lvalue=TRUE; - } /* if */ - - /* operand on left side of assignment must be lvalue */ - if (!lvalue) - return error(22); /* must be lvalue */ - /* may not change "constant" parameters */ - assert(lval1->sym!=NULL); - if ((lval1->sym->usage & uCONST)!=0) - return error(22); /* assignment to const argument */ - sc_allowproccall=FALSE; /* may no longer use "procedure call" syntax */ - - lval3=*lval1; /* save symbol to enable storage of expresion result */ - lval1->arrayidx=org_arrayidx; /* restore array index pointer */ - if (lval1->ident==iARRAYCELL || lval1->ident==iARRAYCHAR - || lval1->ident==iARRAY || lval1->ident==iREFARRAY) - { - /* if indirect fetch: save PRI (cell address) */ - if (oper) { - pushreg(sPRI); - rvalue(lval1); - } /* if */ - lval2.arrayidx=arrayidx2; - plnge2(oper,hier14,lval1,&lval2); - if (lval2.ident!=iARRAYCELL && lval2.ident!=iARRAYCHAR) - lval2.arrayidx=NULL; - if (oper) - popreg(sALT); - if (!oper && lval3.arrayidx!=NULL && lval2.arrayidx!=NULL - && lval3.ident==lval2.ident && lval3.sym==lval2.sym) - { - int same=TRUE; - assert(lval2.arrayidx==arrayidx2); - for (i=0; iname); /* self-assignment */ - } /* if */ - } else { - if (oper){ - rvalue(lval1); - plnge2(oper,hier14,lval1,&lval2); - } else { - /* if direct fetch and simple assignment: no "push" - * and "pop" needed -> call hier14() directly, */ - if (hier14(&lval2)) - rvalue(&lval2); /* instead of plnge2(). */ - else if (lval2.ident==iVARIABLE) - lval2.ident=iEXPRESSION;/* mark as "rvalue" if it is not an "lvalue" */ - checkfunction(&lval2); - /* check whether lval2 and lval3 (old lval1) refer to the same variable */ - if (lval2.ident==iVARIABLE && lval3.ident==lval2.ident && lval3.sym==lval2.sym) { - assert(lval3.sym!=NULL); - error(226,lval3.sym->name); /* self-assignment */ - } /* if */ - } /* if */ - } /* if */ - /* Array elements are sometimes considered as sub-arrays --when the - * array index is an enumeration field and the enumeration size is greater - * than 1. If the expression on the right side of the assignment is a cell, - * or if an operation is in effect, this does not apply. - */ - leftarray= lval3.ident==iARRAY || lval3.ident==iREFARRAY - || ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR) - && lval3.constval>1 && lval3.sym->dim.array.level==0 - && !oper && (lval2.ident==iARRAY || lval2.ident==iREFARRAY)); - if (leftarray) { - /* Left operand is an array, right operand should be an array variable - * of the same size and the same dimension, an array literal (of the - * same size) or a literal string. For single-dimensional arrays without - * tag for the index, it is permitted to assign a smaller array into a - * larger one (without warning). This is to make it easier to work with - * strings. - */ - int exactmatch=TRUE; - int idxtag=0; - int ltlength=(int)lval3.sym->dim.array.length; - if ((lval3.ident==iARRAYCELL || lval3.ident==iARRAYCHAR) - && lval3.constval>0 && lval3.sym->dim.array.level==0) - { - ltlength=(int)lval3.constval; - } /* if */ - if (lval2.ident!=iARRAY && lval2.ident!=iREFARRAY - && (lval2.sym==NULL || lval2.constval<=0)) - error(33,lval3.sym->name); /* array must be indexed */ - if (lval2.sym!=NULL) { - if (lval2.constval==0) { - val=lval2.sym->dim.array.length;/* array variable */ - } else { - val=lval2.constval; - if (lval2.sym->dim.array.level!=0) - error(28,lval2.sym->name); - } /* if */ - level=lval2.sym->dim.array.level; - idxtag=lval2.sym->x.tags.index; - if (level==0 && idxtag==0 && lval3.sym->x.tags.index==0) - exactmatch=FALSE; - } else { - val=lval2.constval; /* literal array */ - level=0; - /* If val is negative, it means that lval2 is a literal string. - * The string array size may be smaller than the destination - * array, provided that the destination array does not have an - * index tag. - */ - if (val<0) { - val=-val; - if (lval3.sym->x.tags.index==0) - exactmatch=FALSE; - } /* if */ - } /* if */ - if (lval3.sym->dim.array.level!=level) - return error(48); /* array dimensions must match */ - else if (ltlengthval || val==0) - return error(47); /* array sizes must match */ - else if (lval3.ident!=iARRAYCELL && !matchtag(lval3.sym->x.tags.index,idxtag,TRUE)) - error(229,(lval2.sym!=NULL) ? lval2.sym->name : lval3.sym->name); /* index tag mismatch */ - if (level>0) { - /* check the sizes of all sublevels too */ - symbol *sym1 = lval3.sym; - symbol *sym2 = lval2.sym; - int i; - assert(sym1!=NULL && sym2!=NULL); - /* ^^^ sym2 must be valid, because only variables can be - * multi-dimensional (there are no multi-dimensional literals), - * sym1 must be valid because it must be an lvalue - */ - assert(exactmatch); - for (i=0; idim.array.length!=sym2->dim.array.length) - error(47); /* array sizes must match */ - else if (!matchtag(sym1->x.tags.index,sym2->x.tags.index,TRUE)) - error(229,sym2->name); /* index tag mismatch */ - } /* for */ - /* get the total size in cells of the multi-dimensional array */ - val=array_totalsize(lval3.sym); - assert(val>0); /* already checked */ - } /* if */ - } else { - /* left operand is not an array, right operand should then not be either */ - if (lval2.ident==iARRAY || lval2.ident==iREFARRAY) - error(6); /* must be assigned to an array */ - } /* if */ - if (leftarray) { - memcopy(val*sizeof(cell)); - } else { - check_userop(NULL,lval2.tag,lval3.tag,2,&lval3,&lval2.tag); - store(&lval3); /* now, store the expression result */ - } /* if */ - if (!oper && !matchtag(lval3.tag,lval2.tag,TRUE)) - error(213); /* tagname mismatch (if "oper", warning already given in plunge2()) */ - if (lval3.sym) - markusage(lval3.sym,uWRITTEN); - sideeffect=TRUE; - bitwise_opercount=bwcount; - lval1->ident=iEXPRESSION; - return FALSE; /* expression result is never an lvalue */ -} - -static int hier13(value *lval) -{ - int lvalue=plnge1(hier12,lval); - if (matchtoken('?')) { - int flab1=getlabel(); - int flab2=getlabel(); - value lval2={0}; - int array1,array2; - - if (lvalue) { - rvalue(lval); - } else if (lval->ident==iCONSTEXPR) { - ldconst(lval->constval,sPRI); - error(lval->constval ? 206 : 205); /* redundant test */ - } /* if */ - jmp_eq0(flab1); /* go to second expression if primary register==0 */ - PUSHSTK_I(sc_allowtags); - sc_allowtags=FALSE; /* do not allow tagnames here (colon is a special token) */ - if (hier13(lval)) - rvalue(lval); - if (lval->ident==iCONSTEXPR) /* load constant here */ - ldconst(lval->constval,sPRI); - sc_allowtags=(short)POPSTK_I(); /* restore */ - jumplabel(flab2); - setlabel(flab1); - needtoken(':'); - if (hier13(&lval2)) - rvalue(&lval2); - if (lval2.ident==iCONSTEXPR) /* load constant here */ - ldconst(lval2.constval,sPRI); - array1= (lval->ident==iARRAY || lval->ident==iREFARRAY); - array2= (lval2.ident==iARRAY || lval2.ident==iREFARRAY); - if (array1 && !array2) { - char *ptr=(lval->sym->name!=NULL) ? lval->sym->name : "-unknown-"; - error(33,ptr); /* array must be indexed */ - } else if (!array1 && array2) { - char *ptr=(lval2.sym->name!=NULL) ? lval2.sym->name : "-unknown-"; - error(33,ptr); /* array must be indexed */ - } /* if */ - /* ??? if both are arrays, should check dimensions */ - if (!matchtag(lval->tag,lval2.tag,FALSE)) - error(213); /* tagname mismatch ('true' and 'false' expressions) */ - setlabel(flab2); - if (lval->ident==iARRAY) - lval->ident=iREFARRAY; /* iARRAY becomes iREFARRAY */ - else if (lval->ident!=iREFARRAY) - lval->ident=iEXPRESSION; /* iREFARRAY stays iREFARRAY, rest becomes iEXPRESSION */ - return FALSE; /* conditional expression is no lvalue */ - } else { - return lvalue; - } /* if */ -} - -/* the order of the operators in these lists is important and must be - * the same as the order of the operators in the array "op1" - */ -static int list3[] = {'*','/','%',0}; -static int list4[] = {'+','-',0}; -static int list5[] = {tSHL,tSHR,tSHRU,0}; -static int list6[] = {'&',0}; -static int list7[] = {'^',0}; -static int list8[] = {'|',0}; -static int list9[] = {tlLE,tlGE,'<','>',0}; -static int list10[] = {tlEQ,tlNE,0}; -static int list11[] = {tlAND,0}; -static int list12[] = {tlOR,0}; - -static int hier12(value *lval) -{ - return skim(list12,jmp_ne0,1,0,hier11,lval); -} - -static int hier11(value *lval) -{ - return skim(list11,jmp_eq0,0,1,hier10,lval); -} - -static int hier10(value *lval) -{ /* ==, != */ - return plnge(list10,15,hier9,lval,"bool",TRUE); -} /* ^ this variable is the starting index in the op1[] - * array of the operators of this hierarchy level */ - -static int hier9(value *lval) -{ /* <=, >=, <, > */ - return plnge_rel(list9,11,hier8,lval); -} - -static int hier8(value *lval) -{ /* | */ - return plnge(list8,10,hier7,lval,NULL,FALSE); -} - -static int hier7(value *lval) -{ /* ^ */ - return plnge(list7,9,hier6,lval,NULL,FALSE); -} - -static int hier6(value *lval) -{ /* & */ - return plnge(list6,8,hier5,lval,NULL,FALSE); -} - -static int hier5(value *lval) -{ /* <<, >>, >>> */ - return plnge(list5,5,hier4,lval,NULL,FALSE); -} - -static int hier4(value *lval) -{ /* +, - */ - return plnge(list4,3,hier3,lval,NULL,FALSE); -} - -static int hier3(value *lval) -{ /* *, /, % */ - return plnge(list3,0,hier2,lval,NULL,FALSE); -} - -static int hier2(value *lval) -{ - int lvalue,tok; - int tag,paranthese; - cell val; - char *st; - symbol *sym; - int saveresult; - - tok=lex(&val,&st); - switch (tok) { - case tINC: /* ++lval */ - if (!hier2(lval)) - return error(22); /* must be lvalue */ - assert(lval->sym!=NULL); - if ((lval->sym->usage & uCONST)!=0) - return error(22); /* assignment to const argument */ - if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag)) - inc(lval); /* increase variable first */ - rvalue(lval); /* and read the result into PRI */ - sideeffect=TRUE; - return FALSE; /* result is no longer lvalue */ - case tDEC: /* --lval */ - if (!hier2(lval)) - return error(22); /* must be lvalue */ - assert(lval->sym!=NULL); - if ((lval->sym->usage & uCONST)!=0) - return error(22); /* assignment to const argument */ - if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag)) - dec(lval); /* decrease variable first */ - rvalue(lval); /* and read the result into PRI */ - sideeffect=TRUE; - return FALSE; /* result is no longer lvalue */ - case '~': /* ~ (one's complement) */ - if (hier2(lval)) - rvalue(lval); - invert(); /* bitwise NOT */ - lval->constval=~lval->constval; - return FALSE; - case '!': /* ! (logical negate) */ - if (hier2(lval)) - rvalue(lval); - if (check_userop(lneg,lval->tag,0,1,NULL,&lval->tag)) { - lval->ident=iEXPRESSION; - lval->constval=0; - } else { - lneg(); /* 0 -> 1, !0 -> 0 */ - lval->constval=!lval->constval; - lval->tag=pc_addtag("bool"); - } /* if */ - return FALSE; - case '-': /* unary - (two's complement) */ - if (hier2(lval)) - rvalue(lval); - /* make a special check for a constant expression with the tag of a - * rational number, so that we can simple swap the sign of that constant. - */ - if (lval->ident==iCONSTEXPR && lval->tag==sc_rationaltag && sc_rationaltag!=0) { - if (rational_digits==0) { - #if PAWN_CELL_SIZE==32 - float *f = (float *)&lval->constval; - #elif PAWN_CELL_SIZE==64 - double *f = (double *)&lval->constval; - #else - #error Unsupported cell size - #endif - *f= - *f; /* this modifies lval->constval */ - } else { - /* the negation of a fixed point number is just an integer negation */ - lval->constval=-lval->constval; - } /* if */ - } else if (check_userop(neg,lval->tag,0,1,NULL,&lval->tag)) { - lval->ident=iEXPRESSION; - lval->constval=0; - } else { - neg(); /* arithmic negation */ - lval->constval=-lval->constval; - } /* if */ - return FALSE; - case tLABEL: /* tagname override */ - tag=pc_addtag(st); - lval->cmptag=tag; - lvalue=hier2(lval); - lval->tag=tag; - return lvalue; - case tDEFINED: - paranthese=0; - while (matchtoken('(')) - paranthese++; - tok=lex(&val,&st); - if (tok!=tSYMBOL) - return error(20,st); /* illegal symbol name */ - sym=findloc(st); - if (sym==NULL) - sym=findglb(st,sSTATEVAR); - if (sym!=NULL && sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) - sym=NULL; /* symbol is not a function, it is in the table, but not "defined" */ - val= (sym!=NULL); - if (!val && find_subst(st,strlen(st))!=NULL) - val=1; - clear_value(lval); - lval->ident=iCONSTEXPR; - lval->constval= val; - lval->tag=pc_addtag("bool"); - ldconst(lval->constval,sPRI); - while (paranthese--) - needtoken(')'); - return FALSE; - case tSIZEOF: - paranthese=0; - while (matchtoken('(')) - paranthese++; - tok=lex(&val,&st); - if (tok!=tSYMBOL) - return error(20,st); /* illegal symbol name */ - sym=findloc(st); - if (sym==NULL) - sym=findglb(st,sSTATEVAR); - if (sym==NULL) - return error(17,st); /* undefined symbol */ - if (sym->ident==iCONSTEXPR) - error(39); /* constant symbol has no size */ - else if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) - error(72); /* "function" symbol has no size */ - else if ((sym->usage & uDEFINE)==0) - return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ - clear_value(lval); - lval->ident=iCONSTEXPR; - lval->constval=1; /* preset */ - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - int level; - symbol *idxsym=NULL; - symbol *subsym=sym; - for (level=0; matchtoken('['); level++) { - idxsym=NULL; - if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) { - char *idxname; - int cmptag=subsym->x.tags.index; - tokeninfo(&val,&idxname); - if ((idxsym=findconst(idxname,&cmptag))==NULL) - error(80,idxname); /* unknown symbol, or non-constant */ - else if (cmptag>1) - error(91,idxname); /* ambiguous constant */ - } /* if */ - needtoken(']'); - if (subsym!=NULL) - subsym=finddepend(subsym); - } /* for */ - if (level>sym->dim.array.level+1) - error(28,sym->name); /* invalid subscript */ - else if (level==sym->dim.array.level+1) - lval->constval= (idxsym!=NULL && idxsym->dim.array.length>0) ? idxsym->dim.array.length : 1; - else - lval->constval=array_levelsize(sym,level); - if (lval->constval==0 && strchr((char *)lptr,PREPROC_TERM)==NULL) - error(224,st); /* indeterminate array size in "sizeof" expression */ - } /* if */ - ldconst(lval->constval,sPRI); - while (paranthese--) - needtoken(')'); - return FALSE; - case tTAGOF: - paranthese=0; - while (matchtoken('(')) - paranthese++; - tok=lex(&val,&st); - if (tok!=tSYMBOL && tok!=tLABEL) - return error(20,st); /* illegal symbol name */ - if (tok==tLABEL) { - constvalue *tagsym=find_constval(&tagname_tab,st,0); - tag=(int)((tagsym!=NULL) ? tagsym->value : 0); - } else { - sym=findloc(st); - if (sym==NULL) - sym=findglb(st,sSTATEVAR); - if (sym==NULL) - return error(17,st); /* undefined symbol */ - if ((sym->usage & uDEFINE)==0) - return error(17,st); /* undefined symbol (symbol is in the table, but it is "used" only) */ - tag=sym->tag; - } /* if */ - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - int level; - symbol *idxsym=NULL; - symbol *subsym=sym; - for (level=0; matchtoken('['); level++) { - idxsym=NULL; - if (subsym!=NULL && level==subsym->dim.array.level && matchtoken(tSYMBOL)) { - char *idxname; - int cmptag=subsym->x.tags.index; - tokeninfo(&val,&idxname); - if ((idxsym=findconst(idxname,&cmptag))==NULL) - error(80,idxname); /* unknown symbol, or non-constant */ - else if (cmptag>1) - error(91,idxname); /* ambiguous constant */ - } /* if */ - needtoken(']'); - if (subsym!=NULL) - subsym=finddepend(subsym); - } /* for */ - if (level>sym->dim.array.level+1) - error(28,sym->name); /* invalid subscript */ - else if (level==sym->dim.array.level+1 && idxsym!=NULL) - tag= idxsym->x.tags.index; - } /* if */ - exporttag(tag); - clear_value(lval); - lval->ident=iCONSTEXPR; - lval->constval=tag | PUBLICTAG; - ldconst(lval->constval,sPRI); - while (paranthese--) - needtoken(')'); - return FALSE; - case tSTATE: { - constvalue *automaton; - constvalue *state; - if (sc_getstateid(&automaton,&state)) { - assert(automaton!=NULL); - assert(automaton->index==0 && automaton->name[0]=='\0' || automaton->index>0); - loadreg(automaton->value,sALT); - assert(state!=NULL); - ldconst(state->value,sPRI); - ob_eq(); - clear_value(lval); - lval->ident=iEXPRESSION; - lval->tag=pc_addtag("bool"); - } /* if */ - return FALSE; - } /* case */ - default: - lexpush(); - lvalue=hier1(lval); - /* check for postfix operators */ - if (matchtoken(';')) { - /* Found a ';', do not look further for postfix operators */ - lexpush(); /* push ';' back after successful match */ - return lvalue; - } else if (matchtoken(tTERM)) { - /* Found a newline that ends a statement (this is the case when - * semicolons are optional). Note that an explicit semicolon was - * handled above. This case is similar, except that the token must - * not be pushed back. - */ - return lvalue; - } else { - tok=lex(&val,&st); - switch (tok) { - case tINC: /* lval++ */ - if (!lvalue) - return error(22); /* must be lvalue */ - assert(lval->sym!=NULL); - if ((lval->sym->usage & uCONST)!=0) - return error(22); /* assignment to const argument */ - /* on incrementing array cells, the address in PRI must be saved for - * incremening the value, whereas the current value must be in PRI - * on exit. - */ - saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); - if (saveresult) - pushreg(sPRI); /* save address in PRI */ - rvalue(lval); /* read current value into PRI */ - if (saveresult) - swap1(); /* save PRI on the stack, restore address in PRI */ - if (!check_userop(user_inc,lval->tag,0,1,lval,&lval->tag)) - inc(lval); /* increase variable afterwards */ - if (saveresult) - popreg(sPRI); /* restore PRI (result of rvalue()) */ - sideeffect=TRUE; - return FALSE; /* result is no longer lvalue */ - case tDEC: /* lval-- */ - if (!lvalue) - return error(22); /* must be lvalue */ - assert(lval->sym!=NULL); - if ((lval->sym->usage & uCONST)!=0) - return error(22); /* assignment to const argument */ - saveresult= (lval->ident==iARRAYCELL || lval->ident==iARRAYCHAR); - if (saveresult) - pushreg(sPRI); /* save address in PRI */ - rvalue(lval); /* read current value into PRI */ - if (saveresult) - swap1(); /* save PRI on the stack, restore address in PRI */ - if (!check_userop(user_dec,lval->tag,0,1,lval,&lval->tag)) - dec(lval); /* decrease variable afterwards */ - if (saveresult) - popreg(sPRI); /* restore PRI (result of rvalue()) */ - sideeffect=TRUE; - return FALSE; - case tCHAR: /* char (compute required # of cells */ - if (lval->ident==iCONSTEXPR) { - lval->constval *= sCHARBITS/8; /* from char to bytes */ - lval->constval = (lval->constval + sizeof(cell)-1) / sizeof(cell); - } else { - if (lvalue) - rvalue(lval); /* fetch value if not already in PRI */ - char2addr(); /* from characters to bytes */ - addconst(sizeof(cell)-1); /* make sure the value is rounded up */ - addr2cell(); /* truncate to number of cells */ - } /* if */ - return FALSE; - default: - lexpush(); - return lvalue; - } /* switch */ - } /* if */ - } /* switch */ -} - -/* hier1 - * - * The highest hierarchy level: it looks for pointer and array indices - * and function calls. - * Generates code to fetch a pointer value if it is indexed and code to - * add to the pointer value or the array address (the address is already - * read at primary()). It also generates code to fetch a function address - * if that hasn't already been done at primary() (check lval[4]) and calls - * callfunction() to call the function. - */ -static int hier1(value *lval1) -{ - int lvalue,index,tok,symtok; - cell val,cidx; - value lval2={0}; - char *st; - char close; - symbol *sym; - symbol dummysymbol,*cursym; /* for changing the index tags in case of enumerated pseudo-arrays */ - - lvalue=primary(lval1); - symtok=tokeninfo(&val,&st); /* get token read by primary() */ - cursym=lval1->sym; -restart: - sym=cursym; - if (matchtoken('[') || matchtoken('{') || matchtoken('(')) { - tok=tokeninfo(&val,&st); /* get token read by matchtoken() */ - if (sym==NULL && symtok!=tSYMBOL) { - /* we do not have a valid symbol and we appear not to have read a valid - * symbol name (so it is unlikely that we would have read a name of an - * undefined symbol) */ - error(29); /* expression error, assumed 0 */ - lexpush(); /* analyse '(', '{' or '[' again later */ - return FALSE; - } /* if */ - if (tok=='[' || tok=='{') { /* subscript */ - close = (char)((tok=='[') ? ']' : '}'); - if (sym==NULL) { /* sym==NULL if lval is a constant or a literal */ - error(28,""); /* cannot subscript */ - needtoken(close); - return FALSE; - } else if (sym->ident!=iARRAY && sym->ident!=iREFARRAY){ - error(28,sym->name); /* cannot subscript, variable is not an array */ - needtoken(close); - return FALSE; - } else if (sym->dim.array.level>0 && close!=']') { - error(51); /* invalid subscript, must use [ ] */ - needtoken(close); - return FALSE; - } /* if */ - /* set the tag to match (enumeration fields as indices) */ - lval2.cmptag=sym->x.tags.index; - stgget(&index,&cidx); /* mark position in code generator */ - pushreg(sPRI); /* save base address of the array */ - if (hier14(&lval2)) /* create expression for the array index */ - rvalue(&lval2); - if (lval2.ident==iARRAY || lval2.ident==iREFARRAY) - error(33,lval2.sym->name); /* array must be indexed */ - needtoken(close); - if (!matchtag(sym->x.tags.index,lval2.tag,TRUE)) - error(213); - if (lval2.ident==iCONSTEXPR) { /* constant expression */ - stgdel(index,cidx); /* scratch generated code */ - if (lval1->arrayidx!=NULL) { /* keep constant index, for checking */ - assert(sym->dim.array.level>=0 && sym->dim.array.levelarrayidx[sym->dim.array.level]=lval2.constval; - } /* if */ - if (close==']') { - /* normal array index */ - if (lval2.constval<0 || sym->dim.array.length!=0 && sym->dim.array.length<=lval2.constval) - error(32,sym->name); /* array index out of bounds */ - if (lval2.constval!=0) { - /* don't add offsets for zero subscripts */ - #if PAWN_CELL_SIZE==16 - ldconst(lval2.constval<<1,sALT); - #elif PAWN_CELL_SIZE==32 - ldconst(lval2.constval<<2,sALT); - #elif PAWN_CELL_SIZE==64 - ldconst(lval2.constval<<3,sALT); - #else - #error Unsupported cell size - #endif - ob_add(); - } /* if */ - } else { - /* character index */ - if (lval2.constval<0 || sym->dim.array.length!=0 - && sym->dim.array.length*((8*sizeof(cell))/sCHARBITS)<=(ucell)lval2.constval) - error(32,sym->name); /* array index out of bounds */ - if (lval2.constval!=0) { - /* don't add offsets for zero subscripts */ - #if sCHARBITS==16 - ldconst(lval2.constval<<1,sALT);/* 16-bit character */ - #else - ldconst(lval2.constval,sALT); /* 8-bit character */ - #endif - ob_add(); - } /* if */ - charalign(); /* align character index into array */ - } /* if */ - /* if the array index is a field from an enumeration, get the tag name - * from the field and save the size of the field too. - */ - assert(lval2.sym==NULL || lval2.sym->dim.array.level==0); - if (lval2.sym!=NULL && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) { - lval1->tag=lval2.sym->x.tags.index; - lval1->constval=lval2.sym->dim.array.length; - } /* if */ - } else { - /* array index is not constant */ - lval1->arrayidx=NULL; /* reset, so won't be checked */ - if (close==']') { - if (sym->dim.array.length!=0) - ffbounds(sym->dim.array.length-1); /* run time check for array bounds */ - cell2addr(); /* normal array index */ - } else { - if (sym->dim.array.length!=0) - ffbounds(sym->dim.array.length*(32/sCHARBITS)-1); - char2addr(); /* character array index */ - } /* if */ - popreg(sALT); - ob_add(); /* base address was popped into secondary register */ - if (close!=']') - charalign(); /* align character index into array */ - } /* if */ - /* the indexed item may be another array (multi-dimensional arrays) */ - assert(cursym==sym && sym!=NULL); /* should still be set */ - if (sym->dim.array.level>0) { - assert(close==']'); /* checked earlier */ - assert(cursym==lval1->sym); - /* read the offset to the subarray and add it to the current address */ - lval1->ident=iARRAYCELL; - pushreg(sPRI); /* the optimizer makes this to a MOVE.alt */ - rvalue(lval1); - popreg(sALT); - ob_add(); - /* adjust the "value" structure and find the referenced array */ - lval1->ident=iREFARRAY; - lval1->sym=finddepend(sym); - assert(lval1->sym!=NULL); - assert(lval1->sym->dim.array.level==sym->dim.array.level-1); - cursym=lval1->sym; - /* try to parse subsequent array indices */ - lvalue=FALSE; /* for now, a iREFARRAY is no lvalue */ - goto restart; - } /* if */ - assert(sym->dim.array.level==0); - /* set type to fetch... INDIRECTLY */ - lval1->ident= (char)((close==']') ? iARRAYCELL : iARRAYCHAR); - /* if the array index is a field from an enumeration, get the tag name - * from the field and save the size of the field too. Otherwise, the - * tag is the one from the array symbol. - */ - if (lval2.ident==iCONSTEXPR && lval2.sym!=NULL - && lval2.sym->dim.array.length>0 && sym->dim.array.level==0) - { - lval1->tag=lval2.sym->x.tags.index; - lval1->constval=lval2.sym->dim.array.length; - if (lval2.tag==sym->x.tags.index && lval1->constval>1 && matchtoken('[')) { - /* an array indexed with an enumeration field may be considered a sub-array */ - lexpush(); - lvalue=FALSE; /* for now, a iREFARRAY is no lvalue */ - lval1->ident=iREFARRAY; - /* initialize a dummy symbol, which is a copy of the current symbol, - * but with an adjusted index tag - */ - assert(sym!=NULL); - dummysymbol=*sym; - /* get the tag of the root of the enumeration */ - assert(lval2.sym!=NULL); - dummysymbol.x.tags.index=lval2.sym->x.tags.field; - dummysymbol.dim.array.length=lval2.sym->dim.array.length; - cursym=&dummysymbol; - /* recurse */ - goto restart; - } /* if */ - } else { - assert(sym!=NULL); - if (cursym!=&dummysymbol) - lval1->tag=sym->tag; - lval1->constval=0; - } /* if */ - /* a cell in an array is an lvalue, a character in an array is not - * always a *valid* lvalue */ - return TRUE; - } else { /* tok=='(' -> function(...) */ - assert(tok=='('); - if (sym==NULL - || (sym->ident!=iFUNCTN && sym->ident!=iREFFUNC)) - { - if (sym==NULL && sc_status==statFIRST) { - /* could be a "use before declaration"; in that case, create a stub - * function so that the usage can be marked. - */ - sym=fetchfunc(lastsymbol,0); - if (sym==NULL) - error(103); /* insufficient memory */ - markusage(sym,uREAD); - } else { - return error(12); /* invalid function call */ - } /* if */ - } else if ((sym->usage & uMISSING)!=0) { - char symname[2*sNAMEMAX+16]; /* allow space for user defined operators */ - funcdisplayname(symname,sym->name); - error(4,symname); /* function not defined */ - } /* if */ - callfunction(sym,lval1,TRUE); - return FALSE; /* result of function call is no lvalue */ - } /* if */ - } /* if */ - if (sym!=NULL && lval1->ident==iFUNCTN) { - assert(sym->ident==iFUNCTN); - if (sc_allowproccall) { - callfunction(sym,lval1,FALSE); - } else { - lval1->sym=NULL; - lval1->ident=iEXPRESSION; - lval1->constval=0; - lval1->tag=0; - error(76); /* invalid function call, or syntax error */ - } /* if */ - return FALSE; - } /* if */ - return lvalue; -} - -/* primary - * - * Returns 1 if the operand is an lvalue (everything except arrays, functions - * constants and -of course- errors). - * Generates code to fetch the address of arrays. Code for constants is - * already generated by constant(). - * This routine first clears the entire lval array (all fields are set to 0). - * - * Global references: sc_intest (may be altered, but restored upon termination) - */ -static int primary(value *lval) -{ - char *st; - int lvalue,tok; - cell val; - symbol *sym; - - if (matchtoken('(')){ /* sub-expression - (expression,...) */ - PUSHSTK_I(sc_intest); - PUSHSTK_I(sc_allowtags); - - sc_intest=FALSE; /* no longer in "test" expression */ - sc_allowtags=TRUE; /* allow tagnames to be used in parenthesized expressions */ - sc_allowproccall=FALSE; - do - lvalue=hier14(lval); - while (matchtoken(',')); - needtoken(')'); - lexclr(FALSE); /* clear lex() push-back, it should have been - * cleared already by needtoken() */ - sc_allowtags=(short)POPSTK_I(); - sc_intest=(short)POPSTK_I(); - return lvalue; - } /* if */ - - clear_value(lval); /* clear lval */ - tok=lex(&val,&st); - if (tok==tSYMBOL) { - /* lastsymbol is char[sNAMEMAX+1], lex() should have truncated any symbol - * to sNAMEMAX significant characters */ - assert(strlen(st)ident==iLABEL) { - error(29); /* expression error, assumed 0 */ - ldconst(0,sPRI); /* load 0 */ - return FALSE; /* return 0 for labels (expression error) */ - } /* if */ - lval->sym=sym; - lval->ident=sym->ident; - lval->tag=sym->tag; - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - address(sym,sPRI); /* get starting address in primary register */ - return FALSE; /* return 0 for array (not lvalue) */ - } else { - return TRUE; /* return 1 if lvalue (not label or array) */ - } /* if */ - } /* if */ - /* now try a global variable */ - if ((sym=findglb(st,sSTATEVAR))!=0) { - if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) { - /* if the function is only in the table because it was inserted as a - * stub in the first pass (i.e. it was "used" but never declared or - * implemented, issue an error - */ - if ((sym->usage & uPROTOTYPED)==0) - error(17,st); - } else { - if ((sym->usage & uDEFINE)==0) - error(17,st); - lval->sym=sym; - lval->ident=sym->ident; - lval->tag=sym->tag; - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - address(sym,sPRI); /* get starting address in primary register */ - return FALSE; /* return 0 for array (not lvalue) */ - } else { - return TRUE; /* return 1 if lvalue (not function or array) */ - } /* if */ - } /* if */ - } else { - if (!sc_allowproccall) - return error(17,st); /* undefined symbol */ - /* an unknown symbol, but used in a way compatible with the "procedure - * call" syntax. So assume that the symbol refers to a function. - */ - assert(sc_status==statFIRST); - sym=fetchfunc(st,0); - if (sym==NULL) - error(103); /* insufficient memory */ - } /* if */ - assert(sym!=NULL); - assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC); - lval->sym=sym; - lval->ident=sym->ident; - lval->tag=sym->tag; - return FALSE; /* return 0 for function (not an lvalue) */ - } /* if */ - lexpush(); /* push the token, it is analyzed by constant() */ - if (constant(lval)==0) { - error(29); /* expression error, assumed 0 */ - ldconst(0,sPRI); /* load 0 */ - } /* if */ - return FALSE; /* return 0 for constants (or errors) */ -} - -static void clear_value(value *lval) -{ - lval->sym=NULL; - lval->constval=0L; - lval->tag=0; - lval->ident=0; - lval->boolresult=FALSE; - /* do not clear lval->arrayidx, it is preset in hier14() */ - /* do not clear lval->cmptag */ -} - -static void setdefarray(cell *string,cell size,cell array_sz,cell *dataaddr,int fconst) -{ - /* The routine must copy the default array data onto the heap, as to avoid - * that a function can change the default value. An optimization is that - * the default array data is "dumped" into the data segment only once (on the - * first use). - */ - assert(string!=NULL); - assert(size>0); - /* check whether to dump the default array */ - assert(dataaddr!=NULL); - if (sc_status==statWRITE && *dataaddr<0) { - int i; - *dataaddr=(litidx+glb_declared)*sizeof(cell); - for (i=0; i=size); - modheap((int)array_sz*sizeof(cell)); - /* ??? should perhaps fill with zeros first */ - memcopy(size*sizeof(cell)); - moveto1(); - } /* if */ -} - -static int findnamedarg(arginfo *arg,char *name) -{ - int i; - - for (i=0; arg[i].ident!=0 && arg[i].ident!=iVARARGS; i++) - if (strcmp(arg[i].name,name)==0) - return i; - return -1; -} - -static int checktag(int tags[],int numtags,int exprtag) -{ - int i; - - assert(tags!=0); - assert(numtags>0); - for (i=0; iident=iEXPRESSION; /* preset, may be changed later */ - lval_result->constval=0; - lval_result->tag=sym->tag; - /* check whether this is a function that returns an array */ - symret=finddepend(sym); - assert(symret==NULL || symret->ident==iREFARRAY); - if (symret!=NULL) { - int retsize; - /* allocate space on the heap for the array, and pass the pointer to the - * reserved memory block as a hidden parameter - */ - retsize=(int)array_totalsize(symret); - assert(retsize>0); - modheap(retsize*sizeof(cell));/* address is in ALT */ - pushreg(sALT); /* pass ALT as the last (hidden) parameter */ - decl_heap+=retsize; - /* also mark the ident of the result as "array" */ - lval_result->ident=iREFARRAY; - lval_result->sym=symret; - } /* if */ - locheap=decl_heap; - - nesting++; - assert(nest_stkusage>=0); - #if !defined NDEBUG - if (nesting==1) - assert(nest_stkusage==0); - #endif - sc_allowproccall=FALSE; /* parameters may not use procedure call syntax */ - - if ((sym->flags & flgDEPRICATED)!=0) { - char *ptr= (sym->documentation!=NULL) ? sym->documentation : ""; - error(234,sym->name,ptr); /* depricated (probably a native function) */ - } /* if */ - - /* run through the arguments */ - arg=sym->dim.arglist; - assert(arg!=NULL); - stgmark(sSTARTREORDER); - memset(arglist,ARG_UNHANDLED,sizeof arglist); - if (matchparanthesis) { - /* Opening brace was already parsed, if closing brace follows, this - * call passes no parameters. - */ - close=matchtoken(')'); - } else { - /* When we find an end of line here, it may be a function call passing - * no parameters, or it may be that the first parameter is on a line - * below. But as a parameter can be anything, this is difficult to check. - * The only simple check that we have is the use of "named parameters". - */ - close=matchtoken(tTERM); - if (close) { - close=!matchtoken('.'); - if (!close) - lexpush(); /* reset the '.' */ - } /* if */ - } /* if */ - if (!close) { - do { - if (matchtoken('.')) { - namedparams=TRUE; - if (needtoken(tSYMBOL)) - tokeninfo(&lexval,&lexstr); - else - lexstr=""; - argpos=findnamedarg(arg,lexstr); - if (argpos<0) { - error(17,lexstr); /* undefined symbol */ - break; /* exit loop, argpos is invalid */ - } /* if */ - needtoken('='); - argidx=argpos; - } else { - if (namedparams) - error(44); /* positional parameters must precede named parameters */ - argpos=nargs; - } /* if */ - /* the number of arguments this was already checked at the declaration - * of the function; check it again for functions with a variable - * argument list - */ - if (argpos>=sMAXARGS) - error(45); /* too many function arguments */ - stgmark((char)(sEXPRSTART+argpos));/* mark beginning of new expression in stage */ - if (arglist[argpos]!=ARG_UNHANDLED) - error(58); /* argument already set */ - if (matchtoken('_')) { - arglist[argpos]=ARG_IGNORED; /* flag argument as "present, but ignored" */ - if (arg[argidx].ident==0 || arg[argidx].ident==iVARARGS) { - error(202); /* argument count mismatch */ - } else if (!arg[argidx].hasdefault) { - error(34,nargs+1); /* argument has no default value */ - } /* if */ - if (arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS) - argidx++; - /* The rest of the code to handle default values is at the bottom - * of this routine where default values for unspecified parameters - * are (also) handled. Note that above, the argument is flagged as - * ARG_IGNORED. - */ - } else { - arglist[argpos]=ARG_DONE; /* flag argument as "present" */ - if (arg[argidx].numtags==1) /* set the expected tag, if any */ - lval.cmptag=arg[argidx].tags[0]; - lvalue=hier14(&lval); - assert(sc_status==statFIRST || arg[argidx].tags!=NULL); - switch (arg[argidx].ident) { - case 0: - error(202); /* argument count mismatch */ - break; - case iVARARGS: - /* always pass by reference */ - if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) { - assert(lval.sym!=NULL); - if ((lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) { - /* treat a "const" variable passed to a function with a non-const - * "variable argument list" as a constant here */ - if (!lvalue) { - error(22); /* need lvalue */ - } else { - rvalue(&lval); /* get value in PRI */ - setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; - nest_stkusage++; - } /* if */ - } else if (lvalue) { - address(lval.sym,sPRI); - } else { - setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; - nest_stkusage++; - } /* if */ - } else if (lval.ident==iCONSTEXPR || lval.ident==iEXPRESSION - || lval.ident==iARRAYCHAR) - { - /* fetch value if needed */ - if (lval.ident==iARRAYCHAR) - rvalue(&lval); - /* allocate a cell on the heap and store the - * value (already in PRI) there */ - setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; - nest_stkusage++; - } /* if */ - /* ??? handle const array passed by reference */ - /* otherwise, the address is already in PRI */ - if (lval.sym!=NULL) - markusage(lval.sym,uWRITTEN); - if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) - error(213); - if (lval.tag!=0) - append_constval(&taglst,arg[argidx].name,lval.tag,0); - break; - case iVARIABLE: - if (lval.ident==iLABEL || lval.ident==iFUNCTN || lval.ident==iREFFUNC - || lval.ident==iARRAY || lval.ident==iREFARRAY) - error(35,argidx+1); /* argument type mismatch */ - if (lvalue) - rvalue(&lval); /* get value (direct or indirect) */ - /* otherwise, the expression result is already in PRI */ - assert(arg[argidx].numtags>0); - check_userop(NULL,lval.tag,arg[argidx].tags[0],2,NULL,&lval.tag); - if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) - error(213); - if (lval.tag!=0) - append_constval(&taglst,arg[argidx].name,lval.tag,0); - argidx++; /* argument done */ - break; - case iREFERENCE: - if (!lvalue || lval.ident==iARRAYCHAR) - error(35,argidx+1); /* argument type mismatch */ - if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) - error(35,argidx+1); /* argument type mismatch */ - if (lval.ident==iVARIABLE || lval.ident==iREFERENCE) { - if (lvalue) { - assert(lval.sym!=NULL); - address(lval.sym,sPRI); - } else { - setheap_pri(); /* address of the value on the heap in PRI */ - heapalloc++; - nest_stkusage++; - } /* if */ - } /* if */ - /* otherwise, the address is already in PRI */ - if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) - error(213); - if (lval.tag!=0) - append_constval(&taglst,arg[argidx].name,lval.tag,0); - argidx++; /* argument done */ - if (lval.sym!=NULL) - markusage(lval.sym,uWRITTEN); - break; - case iREFARRAY: - if (lval.ident!=iARRAY && lval.ident!=iREFARRAY - && lval.ident!=iARRAYCELL) - { - error(35,argidx+1); /* argument type mismatch */ - break; - } /* if */ - if (lval.sym!=NULL && (lval.sym->usage & uCONST)!=0 && (arg[argidx].usage & uCONST)==0) - error(35,argidx+1); /* argument type mismatch */ - /* Verify that the dimensions match with those in arg[argidx]. - * A literal array always has a single dimension. - * An iARRAYCELL parameter is also assumed to have a single dimension. - */ - if (lval.sym==NULL || lval.ident==iARRAYCELL) { - if (arg[argidx].numdim!=1) { - error(48); /* array dimensions must match */ - } else if (arg[argidx].dim[0]!=0) { - assert(arg[argidx].dim[0]>0); - if (lval.ident==iARRAYCELL) { - error(47); /* array sizes must match */ - } else { - assert(lval.constval!=0); /* literal array must have a size */ - /* A literal array must have exactly the same size as the - * function argument; a literal string may be smaller than - * the function argument. - */ - if (lval.constval>0 && arg[argidx].dim[0]!=lval.constval - || lval.constval<0 && arg[argidx].dim[0] < -lval.constval) - error(47); /* array sizes must match */ - } /* if */ - } /* if */ - if (lval.ident!=iARRAYCELL) { - /* save array size, for default values with uSIZEOF flag */ - cell array_sz=lval.constval; - assert(array_sz!=0);/* literal array must have a size */ - if (array_sz<0) - array_sz= -array_sz; - append_constval(&arrayszlst,arg[argidx].name,array_sz,0); - } /* if */ - } else { - symbol *sym=lval.sym; - short level=0; - assert(sym!=NULL); - if (sym->dim.array.level+1!=arg[argidx].numdim) - error(48); /* array dimensions must match */ - /* the lengths for all dimensions must match, unless the dimension - * length was defined at zero (which means "undefined") - */ - while (sym->dim.array.level>0) { - assert(leveldim.array.length!=arg[argidx].dim[level]) - error(47); /* array sizes must match */ - else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) - error(229,sym->name); /* index tag mismatch */ - append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); - sym=finddepend(sym); - assert(sym!=NULL); - level++; - } /* if */ - /* the last dimension is checked too, again, unless it is zero */ - assert(leveldim.array.length!=arg[argidx].dim[level]) - error(47); /* array sizes must match */ - else if (!matchtag(arg[argidx].idxtag[level],sym->x.tags.index,TRUE)) - error(229,sym->name); /* index tag mismatch */ - append_constval(&arrayszlst,arg[argidx].name,sym->dim.array.length,level); - } /* if */ - /* address already in PRI */ - if (!checktag(arg[argidx].tags,arg[argidx].numtags,lval.tag)) - error(213); - if (lval.tag!=0) - append_constval(&taglst,arg[argidx].name,lval.tag,0); - // ??? set uWRITTEN? - argidx++; /* argument done */ - break; - } /* switch */ - pushreg(sPRI); /* store the function argument on the stack */ - markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ - nest_stkusage++; - } /* if */ - assert(arglist[argpos]!=ARG_UNHANDLED); - nargs++; - if (matchparanthesis) { - close=matchtoken(')'); - if (!close) /* if not paranthese... */ - if (!needtoken(',')) /* ...should be comma... */ - break; /* ...but abort loop if neither */ - } else { - close=!matchtoken(','); - if (close) { /* if not comma... */ - if (needtoken(tTERM)==1)/* ...must be end of statement */ - lexpush(); /* push again, because end of statement is analised later */ - } /* if */ - } /* if */ - } while (!close && freading && !matchtoken(tENDEXPR)); /* do */ - } /* if */ - /* check remaining function arguments (they may have default values) */ - for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) { - if (arglist[argidx]==ARG_DONE) - continue; /* already seen and handled this argument */ - /* in this first stage, we also skip the arguments with uSIZEOF and uTAGOF; - * these are handled last - */ - if ((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0) { - assert(arg[argidx].ident==iVARIABLE); - continue; - } /* if */ - stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */ - if (arg[argidx].hasdefault) { - if (arg[argidx].ident==iREFARRAY) { - short level; - setdefarray(arg[argidx].defvalue.array.data, - arg[argidx].defvalue.array.size, - arg[argidx].defvalue.array.arraysize, - &arg[argidx].defvalue.array.addr, - (arg[argidx].usage & uCONST)!=0); - if ((arg[argidx].usage & uCONST)==0) { - heapalloc+=arg[argidx].defvalue.array.arraysize; - nest_stkusage+=arg[argidx].defvalue.array.arraysize; - } /* if */ - /* keep the lengths of all dimensions of a multi-dimensional default array */ - assert(arg[argidx].numdim>0); - if (arg[argidx].numdim==1) { - append_constval(&arrayszlst,arg[argidx].name,arg[argidx].defvalue.array.arraysize,0); - } else { - for (level=0; level0); - check_userop(NULL,arg[argidx].defvalue_tag,arg[argidx].tags[0],2,NULL,&dummytag); - assert(dummytag==arg[argidx].tags[0]); - } /* if */ - pushreg(sPRI); /* store the function argument on the stack */ - markexpr(sPARM,NULL,0); /* mark the end of a sub-expression */ - nest_stkusage++; - } else { - error(202,argidx); /* argument count mismatch */ - } /* if */ - if (arglist[argidx]==ARG_UNHANDLED) - nargs++; - arglist[argidx]=ARG_DONE; - } /* for */ - /* now a second loop to catch the arguments with default values that are - * the "sizeof" or "tagof" of other arguments - */ - for (argidx=0; arg[argidx].ident!=0 && arg[argidx].ident!=iVARARGS; argidx++) { - constvalue *asz; - cell array_sz; - if (arglist[argidx]==ARG_DONE) - continue; /* already seen and handled this argument */ - stgmark((char)(sEXPRSTART+argidx));/* mark beginning of new expression in stage */ - assert(arg[argidx].ident==iVARIABLE); /* if "sizeof", must be single cell */ - /* if unseen, must be "sizeof" or "tagof" */ - assert((arg[argidx].hasdefault & uSIZEOF)!=0 || (arg[argidx].hasdefault & uTAGOF)!=0); - if ((arg[argidx].hasdefault & uSIZEOF)!=0) { - /* find the argument; if it isn't found, the argument's default value - * was a "sizeof" of a non-array (a warning for this was already given - * when declaring the function) - */ - asz=find_constval(&arrayszlst,arg[argidx].defvalue.size.symname, - arg[argidx].defvalue.size.level); - if (asz!=NULL) { - array_sz=asz->value; - if (array_sz==0) - error(224,arg[argidx].name); /* indeterminate array size in "sizeof" expression */ - } else { - array_sz=1; - } /* if */ - } else { - asz=find_constval(&taglst,arg[argidx].defvalue.size.symname, - arg[argidx].defvalue.size.level); - if (asz != NULL) { - exporttag(asz->value); - array_sz=asz->value | PUBLICTAG; /* must be set, because it just was exported */ - } else { - array_sz=0; - } /* if */ - } /* if */ - ldconst(array_sz,sPRI); - pushreg(sPRI); /* store the function argument on the stack */ - markexpr(sPARM,NULL,0); - nest_stkusage++; - if (arglist[argidx]==ARG_UNHANDLED) - nargs++; - arglist[argidx]=ARG_DONE; - } /* for */ - stgmark(sENDREORDER); /* mark end of reversed evaluation */ - pushval((cell)nargs*sizeof(cell)); - nest_stkusage++; - ffcall(sym,NULL,nargs); - if (sc_status!=statSKIP) - markusage(sym,uREAD); /* do not mark as "used" when this call itself is skipped */ - if ((sym->usage & uNATIVE)!=0 &&sym->x.lib!=NULL) - sym->x.lib->value += 1; /* increment "usage count" of the library */ - modheap(-heapalloc*sizeof(cell)); - if (symret!=NULL) - popreg(sPRI); /* pop hidden parameter as function result */ - sideeffect=TRUE; /* assume functions carry out a side-effect */ - delete_consttable(&arrayszlst); /* clear list of array sizes */ - delete_consttable(&taglst); /* clear list of parameter tags */ - - /* maintain max. amount of memory used */ - { - long totalsize; - totalsize=declared+decl_heap+1; /* local variables & return value size, - * +1 for PROC opcode */ - if (lval_result->ident==iREFARRAY) - totalsize++; /* add hidden parameter (on the stack) */ - if ((sym->usage & uNATIVE)==0) - totalsize++; /* add "call" opcode */ - totalsize+=nest_stkusage; - assert(curfunc!=NULL); - if (curfunc->x.stacksizex.stacksize=totalsize; - nest_stkusage-=nargs+heapalloc+1; /* stack/heap space, +1 for argcount param */ - /* if there is a syntax error in the script, the stack calculation is - * probably incorrect; but we may not allow it to drop below zero - */ - if (nest_stkusage<0) - nest_stkusage=0; - } - - /* scrap any arrays left on the heap, with the exception of the array that - * this function has as a result (in other words, scrap all arrays on the - * heap that caused by expressions in the function arguments) - */ - assert(decl_heap>=locheap); - modheap((locheap-decl_heap)*sizeof(cell)); /* remove heap space, so negative delta */ - decl_heap=locheap; - nesting--; -} - -/* dbltest - * - * Returns a non-zero value if lval1 an array and lval2 is not an array and - * the operation is addition or subtraction. - * - * Returns the "shift" count (1 for 16-bit, 2 for 32-bit) to align a cell - * to an array offset. - */ -static int dbltest(void (*oper)(),value *lval1,value *lval2) -{ - if ((oper!=ob_add) && (oper!=ob_sub)) - return 0; - if (lval1->ident!=iARRAY) - return 0; - if (lval2->ident==iARRAY) - return 0; - return sizeof(cell)/2; /* 1 for 16-bit, 2 for 32-bit */ -} - -/* commutative - * - * Test whether an operator is commutative, i.e. x oper y == y oper x. - * Commutative operators are: + (addition) - * * (multiplication) - * == (equality) - * != (inequality) - * & (bitwise and) - * ^ (bitwise xor) - * | (bitwise or) - * - * If in an expression, code for the left operand has been generated and - * the right operand is a constant and the operator is commutative, the - * precautionary "push" of the primary register is scrapped and the constant - * is read into the secondary register immediately. - */ -static int commutative(void (*oper)()) -{ - return oper==ob_add || oper==os_mult - || oper==ob_eq || oper==ob_ne - || oper==ob_and || oper==ob_xor || oper==ob_or; -} - -/* constant - * - * Generates code to fetch a number, a literal character (which is returned - * by lex() as a number as well) or a literal string (lex() stores the - * strings in the literal queue). If the operand was a number, it is stored - * in lval->constval. - * - * The function returns 1 if the token was a constant or a string, 0 - * otherwise. - */ -static int constant(value *lval) -{ - int tok,index,ident; - cell val,item,cidx; - char *st; - symbol *sym; - int cmptag=lval->cmptag; - - tok=lex(&val,&st); - if (tok==tSYMBOL && (sym=findconst(st,&cmptag))!=0) { - if (cmptag>1) - error(91,sym->name); /* ambiguity: multiple matching constants (different tags) */ - lval->constval=sym->addr; - ldconst(lval->constval,sPRI); - lval->ident=iCONSTEXPR; - lval->tag=sym->tag; - lval->sym=sym; - markusage(sym,uREAD); - } else if (tok==tNUMBER) { - lval->constval=val; - ldconst(lval->constval,sPRI); - lval->ident=iCONSTEXPR; - } else if (tok==tRATIONAL) { - lval->constval=val; - ldconst(lval->constval,sPRI); - lval->ident=iCONSTEXPR; - lval->tag=sc_rationaltag; - } else if (tok==tSTRING) { - /* lex() stores starting index of string in the literal table in 'val' */ - ldconst((val+glb_declared)*sizeof(cell),sPRI); - lval->ident=iARRAY; /* pretend this is a global array */ - lval->constval=val-litidx; /* constval == the negative value of the - * size of the literal array; using a negative - * value distinguishes between literal arrays - * and literal strings (this was done for - * array assignment). */ - } else if (tok=='{') { - int tag,lasttag=-1; - val=litidx; - do { - /* cannot call constexpr() here, because "staging" is already turned - * on at this point */ - assert(staging); - stgget(&index,&cidx); /* mark position in code generator */ - ident=expression(&item,&tag,NULL,FALSE); - stgdel(index,cidx); /* scratch generated code */ - if (ident!=iCONSTEXPR) - error(8); /* must be constant expression */ - if (lasttag<0) - lasttag=tag; - else if (!matchtag(lasttag,tag,FALSE)) - error(213); /* tagname mismatch */ - litadd(item); /* store expression result in literal table */ - } while (matchtoken(',')); - if (!needtoken('}')) - lexclr(FALSE); - ldconst((val+glb_declared)*sizeof(cell),sPRI); - lval->ident=iARRAY; /* pretend this is a global array */ - lval->constval=litidx-val; /* constval == the size of the literal array */ - } else { - return FALSE; /* no, it cannot be interpreted as a constant */ - } /* if */ - return TRUE; /* yes, it was a constant value */ -} - diff --git a/compiler-init/compiler-init/sc4.c b/compiler-init/compiler-init/sc4.c deleted file mode 100644 index 70e39dde..00000000 --- a/compiler-init/compiler-init/sc4.c +++ /dev/null @@ -1,1331 +0,0 @@ -/* Pawn compiler - code generation (unoptimized "assembler" code) - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc4.c 3579 2006-06-06 13:35:29Z thiadmer $ - */ -#include -#include -#include -#include /* for _MAX_PATH */ -#include -#if defined FORTIFY - #include -#endif -#include "sc.h" - -static int fcurseg; /* the file number (fcurrent) for the active segment */ - - -/* When a subroutine returns to address 0, the AMX must halt. In earlier - * releases, the RET and RETN opcodes checked for the special case 0 address. - * Today, the compiler simply generates a HALT instruction at address 0. So - * a subroutine can savely return to 0, and then encounter a HALT. - */ -SC_FUNC void writeleader(symbol *root) -{ - int lbl_nostate,lbl_table; - int statecount; - symbol *sym; - constvalue *fsa, *state, *stlist; - int fsa_id,listid; - char lbl_default[sNAMEMAX+1]; - - assert(code_idx==0); - - begcseg(); - stgwrite(";program exit point\n"); - stgwrite("\thalt 0\n\n"); - code_idx+=opcodes(1)+opargs(1); /* calculate code length */ - - /* check whether there are any functions that have states */ - for (sym=root->next; sym!=NULL; sym=sym->next) - if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) - break; - if (sym==NULL) - return; /* no function has states, nothing to do next */ - - /* generate an error function that is called for an undefined state */ - stgwrite("\n;exit point for functions called from the wrong state\n"); - lbl_nostate=getlabel(); - setlabel(lbl_nostate); - stgwrite("\thalt "); - outval(AMX_ERR_INVSTATE,TRUE); - code_idx+=opcodes(1)+opargs(1); /* calculate code length */ - - /* write the "state-selectors" table with all automatons (update the - * automatons structure too, as we are now assigning the address to - * each automaton state-selector variable) - */ - assert(glb_declared==0); - begdseg(); - for (fsa=sc_automaton_tab.next; fsa!=NULL; fsa=fsa->next) { - defstorage(); - stgwrite("0\t; automaton "); - if (strlen(fsa->name)==0) - stgwrite("(anonymous)"); - else - stgwrite(fsa->name); - stgwrite("\n"); - fsa->value=glb_declared*sizeof(cell); - glb_declared++; - } /* for */ - - /* write stubs and jump tables for all state functions */ - begcseg(); - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->ident==iFUNCTN && (sym->usage & (uPUBLIC | uREAD))!=0 && sym->states!=NULL) { - stlist=sym->states->next; - assert(stlist!=NULL); /* there should be at least one state item */ - listid=stlist->index; - assert(listid==-1 || listid>0); - if (listid==-1 && stlist->next!=NULL) { - /* first index is the "fallback", take the next one (if available) */ - stlist=stlist->next; - listid=stlist->index; - } /* if */ - if (listid==-1) { - /* first index is the fallback, there is no second... */ - strcpy(stlist->name,"0"); /* insert dummy label number */ - /* this is an error, but we postpone adding the error message until the - * function definition - */ - continue; - } /* if */ - /* generate label numbers for all statelist ids */ - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { - assert(strlen(stlist->name)==0); - strcpy(stlist->name,itoh(getlabel())); - } /* for */ - if (strcmp(sym->name,uENTRYFUNC)==0) - continue; /* do not generate stubs for this special function */ - sym->addr=code_idx; /* fix the function address now */ - /* get automaton id for this function */ - assert(listid>0); - fsa_id=state_getfsa(listid); - assert(fsa_id>=0); /* automaton 0 exists */ - fsa=automaton_findid(fsa_id); - /* count the number of states actually used; at the sane time, check - * whether there is a default state function - */ - statecount=0; - strcpy(lbl_default,itoh(lbl_nostate)); - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { - if (stlist->index==-1) { - assert(strlen(stlist->name)name); - } else { - statecount+=state_count(stlist->index); - } /* if */ - } /* for */ - /* generate a stub entry for the functions */ - stgwrite("\tload.pri "); - outval(fsa->value,FALSE); - stgwrite("\t; "); - stgwrite(sym->name); - stgwrite("\n"); - code_idx+=opcodes(1)+opargs(1); /* calculate code length */ - lbl_table=getlabel(); - ffswitch(lbl_table); - /* generate the jump table */ - setlabel(lbl_table); - ffcase(statecount,lbl_default,TRUE); - for (state=sc_state_tab.next; state!=NULL; state=state->next) { - if (state->index==fsa_id) { - /* find the label for this list id */ - for (stlist=sym->states->next; stlist!=NULL; stlist=stlist->next) { - if (stlist->index!=-1 && state_inlist(stlist->index,(int)state->value)) { - ffcase(state->value,stlist->name,FALSE); - break; - } /* if */ - } /* for */ - if (stlist==NULL && strtol(lbl_default,NULL,16)==lbl_nostate) - error(230,state->name,sym->name); /* unimplemented state, no fallback */ - } /* if (state belongs to automaton of function) */ - } /* for (state) */ - stgwrite("\n"); - } /* if (is function, used & having states) */ - } /* for (sym) */ -} - -/* writetrailer - * Not much left of this once important function. - * - * Global references: pc_stksize (referred to only) - * sc_dataalign (referred to only) - * code_idx (altered) - * glb_declared (altered) - */ -SC_FUNC void writetrailer(void) -{ - assert(sc_dataalign % opcodes(1) == 0); /* alignment must be a multiple of - * the opcode size */ - assert(sc_dataalign!=0); - - /* pad code to align data segment */ - if ((code_idx % sc_dataalign)!=0) { - begcseg(); - while ((code_idx % sc_dataalign)!=0) - nooperation(); - } /* if */ - - /* pad data segment to align the stack and the heap */ - assert(litidx==0); /* literal queue should have been emptied */ - assert(sc_dataalign % sizeof(cell) == 0); - if (((glb_declared*sizeof(cell)) % sc_dataalign)!=0) { - begdseg(); - defstorage(); - while (((glb_declared*sizeof(cell)) % sc_dataalign)!=0) { - stgwrite("0 "); - glb_declared++; - } /* while */ - } /* if */ - - stgwrite("\nSTKSIZE "); /* write stack size (align stack top) */ - outval(pc_stksize - (pc_stksize % sc_dataalign), TRUE); -} - -/* - * Start (or restart) the CODE segment. - * - * In fact, the code and data segment specifiers are purely informational; - * the "DUMP" instruction itself already specifies that the following values - * should go to the data segment. All other instructions go to the code - * segment. - * - * Global references: curseg - * fcurrent - */ -SC_FUNC void begcseg(void) -{ - if (curseg!=sIN_CSEG || fcurrent!=fcurseg) { - stgwrite("\n"); - stgwrite("CODE "); - outval(fcurrent,FALSE); - stgwrite("\t; "); - outval(code_idx,TRUE); - curseg=sIN_CSEG; - fcurseg=fcurrent; - } /* endif */ -} - -/* - * Start (or restart) the DATA segment. - * - * Global references: curseg - */ -SC_FUNC void begdseg(void) -{ - if (curseg!=sIN_DSEG || fcurrent!=fcurseg) { - stgwrite("\n"); - stgwrite("DATA "); - outval(fcurrent,FALSE); - stgwrite("\t; "); - outval((glb_declared-litidx)*sizeof(cell),TRUE); - curseg=sIN_DSEG; - fcurseg=fcurrent; - } /* if */ -} - -SC_FUNC void setline(int chkbounds) -{ - if ((sc_debug & sSYMBOLIC)!=0 || chkbounds && (sc_debug & sCHKBOUNDS)!=0) { - /* generate a "break" (start statement) opcode rather than a "line" opcode - * because earlier versions of Small/Pawn have an incompatible version of the - * line opcode - */ - stgwrite("\tbreak\t; "); - outval(code_idx,TRUE); - code_idx+=opcodes(1); - } /* if */ -} - -SC_FUNC void setfiledirect(char *name) -{ - if (sc_status==statFIRST && sc_listing) { - assert(name!=NULL); - pc_writeasm(outf,"#file "); - pc_writeasm(outf,name); - pc_writeasm(outf,"\n"); - } /* if */ -} - -SC_FUNC void setlinedirect(int line) -{ - if (sc_status==statFIRST && sc_listing) { - char string[40]; - sprintf(string,"#line %d\n",line); - pc_writeasm(outf,string); - } /* if */ -} - -/* setlabel - * - * Post a code label (specified as a number), on a new line. - */ -SC_FUNC void setlabel(int number) -{ - assert(number>=0); - stgwrite("l."); - stgwrite((char *)itoh(number)); - /* To assist verification of the assembled code, put the address of the - * label as a comment. However, labels that occur inside an expression - * may move (through optimization or through re-ordering). So write the - * address only if it is known to accurate. - */ - if (!staging) { - stgwrite("\t\t; "); - outval(code_idx,FALSE); - } /* if */ - stgwrite("\n"); -} - -/* Write a token that signifies the start or end of an expression or special - * statement. This allows several simple optimizations by the peephole - * optimizer. - */ -SC_FUNC void markexpr(optmark type,const char *name,cell offset) -{ - switch (type) { - case sEXPR: - stgwrite("\t;$exp\n"); - break; - case sPARM: - stgwrite("\t;$par\n"); - break; - case sLDECL: - assert(name!=NULL); - stgwrite("\t;$lcl "); - stgwrite(name); - stgwrite(" "); - outval(offset,TRUE); - break; - default: - assert(0); - } /* switch */ -} - -/* startfunc - declare a CODE entry point (function start) - * - * Global references: funcstatus (referred to only) - */ -SC_FUNC void startfunc(char *fname) -{ - stgwrite("\tproc"); - if (sc_asmfile) { - char symname[2*sNAMEMAX+16]; - funcdisplayname(symname,fname); - stgwrite("\t; "); - stgwrite(symname); - } /* if */ - stgwrite("\n"); - code_idx+=opcodes(1); -} - -/* endfunc - * - * Declare a CODE ending point (function end) - */ -SC_FUNC void endfunc(void) -{ - stgwrite("\n"); /* skip a line */ -} - -/* alignframe - * - * Aligns the frame (and the stack) of the current function to a multiple - * of the specified byte count. Two caveats: the alignment ("numbytes") should - * be a power of 2, and this alignment must be done right after the frame - * is set up (before the first variable is declared) - */ -SC_FUNC void alignframe(int numbytes) -{ - #if !defined NDEBUG - /* "numbytes" should be a power of 2 for this code to work */ - int i,count=0; - for (i=0; isym; - if (lval->ident==iARRAYCELL) { - /* indirect fetch, address already in PRI */ - stgwrite("\tload.i\n"); - code_idx+=opcodes(1); - } else if (lval->ident==iARRAYCHAR) { - /* indirect fetch of a character from a pack, address already in PRI */ - stgwrite("\tlodb.i "); - outval(sCHARBITS/8,TRUE); /* read one or two bytes */ - code_idx+=opcodes(1)+opargs(1); - } else if (lval->ident==iREFERENCE) { - /* indirect fetch, but address not yet in PRI */ - assert(sym!=NULL); - assert(sym->vclass==sLOCAL);/* global references don't exist in Pawn */ - if (sym->vclass==sLOCAL) - stgwrite("\tlref.s.pri "); - else - stgwrite("\tlref.pri "); - outval(sym->addr,TRUE); - markusage(sym,uREAD); - code_idx+=opcodes(1)+opargs(1); - } else { - /* direct or stack relative fetch */ - assert(sym!=NULL); - if (sym->vclass==sLOCAL) - stgwrite("\tload.s.pri "); - else - stgwrite("\tload.pri "); - outval(sym->addr,TRUE); - markusage(sym,uREAD); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* Get the address of a symbol into the primary or alternate register (used - * for arrays, and for passing arguments by reference). - */ -SC_FUNC void address(symbol *sym,regid reg) -{ - assert(sym!=NULL); - assert(reg==sPRI || reg==sALT); - /* the symbol can be a local array, a global array, or an array - * that is passed by reference. - */ - if (sym->ident==iREFARRAY || sym->ident==iREFERENCE) { - /* reference to a variable or to an array; currently this is - * always a local variable */ - switch (reg) { - case sPRI: - stgwrite("\tload.s.pri "); - break; - case sALT: - stgwrite("\tload.s.alt "); - break; - } /* switch */ - } else { - /* a local array or local variable */ - switch (reg) { - case sPRI: - if (sym->vclass==sLOCAL) - stgwrite("\taddr.pri "); - else - stgwrite("\tconst.pri "); - break; - case sALT: - if (sym->vclass==sLOCAL) - stgwrite("\taddr.alt "); - else - stgwrite("\tconst.alt "); - break; - } /* switch */ - } /* if */ - outval(sym->addr,TRUE); - markusage(sym,uREAD); - code_idx+=opcodes(1)+opargs(1); -} - -/* store - * - * Saves the contents of "primary" into a memory cell, either directly - * or indirectly (at the address given in the alternate register). - */ -SC_FUNC void store(value *lval) -{ - symbol *sym; - - sym=lval->sym; - if (lval->ident==iARRAYCELL) { - /* store at address in ALT */ - stgwrite("\tstor.i\n"); - code_idx+=opcodes(1); - } else if (lval->ident==iARRAYCHAR) { - /* store at address in ALT */ - stgwrite("\tstrb.i "); - outval(sCHARBITS/8,TRUE); /* write one or two bytes */ - code_idx+=opcodes(1)+opargs(1); - } else if (lval->ident==iREFERENCE) { - assert(sym!=NULL); - if (sym->vclass==sLOCAL) - stgwrite("\tsref.s.pri "); - else - stgwrite("\tsref.pri "); - outval(sym->addr,TRUE); - code_idx+=opcodes(1)+opargs(1); - } else { - assert(sym!=NULL); - markusage(sym,uWRITTEN); - if (sym->vclass==sLOCAL) - stgwrite("\tstor.s.pri "); - else - stgwrite("\tstor.pri "); - outval(sym->addr,TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* Get a cell from a fixed address in memory */ -SC_FUNC void loadreg(cell address,regid reg) -{ - assert(reg==sPRI || reg==sALT); - if (reg==sPRI) - stgwrite("\tload.pri "); - else - stgwrite("\tload.alt "); - outval(address,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* Store a cell into a fixed address in memory */ -SC_FUNC void storereg(cell address,regid reg) -{ - assert(reg==sPRI || reg==sALT); - if (reg==sPRI) - stgwrite("\tstor.pri "); - else - stgwrite("\tstor.alt "); - outval(address,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* source must in PRI, destination address in ALT. The "size" - * parameter is in bytes, not cells. - */ -SC_FUNC void memcopy(cell size) -{ - stgwrite("\tmovs "); - outval(size,TRUE); - - code_idx+=opcodes(1)+opargs(1); -} - -/* Address of the source must already have been loaded in PRI - * "size" is the size in bytes (not cells). - */ -SC_FUNC void copyarray(symbol *sym,cell size) -{ - assert(sym!=NULL); - /* the symbol can be a local array, a global array, or an array - * that is passed by reference. - */ - if (sym->ident==iREFARRAY) { - /* reference to an array; currently this is always a local variable */ - assert(sym->vclass==sLOCAL); /* symbol must be stack relative */ - stgwrite("\tload.s.alt "); - } else { - /* a local or global array */ - if (sym->vclass==sLOCAL) - stgwrite("\taddr.alt "); - else - stgwrite("\tconst.alt "); - } /* if */ - outval(sym->addr,TRUE); - markusage(sym,uWRITTEN); - - code_idx+=opcodes(1)+opargs(1); - memcopy(size); -} - -SC_FUNC void fillarray(symbol *sym,cell size,cell value) -{ - ldconst(value,sPRI); /* load value in PRI */ - - assert(sym!=NULL); - /* the symbol can be a local array, a global array, or an array - * that is passed by reference. - */ - if (sym->ident==iREFARRAY) { - /* reference to an array; currently this is always a local variable */ - assert(sym->vclass==sLOCAL); /* symbol must be stack relative */ - stgwrite("\tload.s.alt "); - } else { - /* a local or global array */ - if (sym->vclass==sLOCAL) - stgwrite("\taddr.alt "); - else - stgwrite("\tconst.alt "); - } /* if */ - outval(sym->addr,TRUE); - markusage(sym,uWRITTEN); - - assert(size>0); - stgwrite("\tfill "); - outval(size,TRUE); - - code_idx+=opcodes(2)+opargs(2); -} - -/* Instruction to get an immediate value into the primary or the alternate - * register - */ -SC_FUNC void ldconst(cell val,regid reg) -{ - assert(reg==sPRI || reg==sALT); - switch (reg) { - case sPRI: - if (val==0) { - stgwrite("\tzero.pri\n"); - code_idx+=opcodes(1); - } else { - stgwrite("\tconst.pri "); - outval(val, TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ - break; - case sALT: - if (val==0) { - stgwrite("\tzero.alt\n"); - code_idx+=opcodes(1); - } else { - stgwrite("\tconst.alt "); - outval(val, TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ - break; - } /* switch */ -} - -/* Copy value in alternate register to the primary register */ -SC_FUNC void moveto1(void) -{ - stgwrite("\tmove.pri\n"); - code_idx+=opcodes(1)+opargs(0); -} - -/* Push primary or the alternate register onto the stack - */ -SC_FUNC void pushreg(regid reg) -{ - assert(reg==sPRI || reg==sALT); - switch (reg) { - case sPRI: - stgwrite("\tpush.pri\n"); - break; - case sALT: - stgwrite("\tpush.alt\n"); - break; - } /* switch */ - code_idx+=opcodes(1); -} - -/* - * Push a constant value onto the stack - */ -SC_FUNC void pushval(cell val) -{ - stgwrite("\tpush.c "); - outval(val, TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* Pop stack into the primary or the alternate register - */ -SC_FUNC void popreg(regid reg) -{ - assert(reg==sPRI || reg==sALT); - switch (reg) { - case sPRI: - stgwrite("\tpop.pri\n"); - break; - case sALT: - stgwrite("\tpop.alt\n"); - break; - } /* switch */ - code_idx+=opcodes(1); -} - -/* - * swap the top-of-stack with the value in primary register - */ -SC_FUNC void swap1(void) -{ - stgwrite("\tswap.pri\n"); - code_idx+=opcodes(1); -} - -/* Switch statements - * The "switch" statement generates a "case" table using the "CASE" opcode. - * The case table contains a list of records, each record holds a comparison - * value and a label to branch to on a match. The very first record is an - * exception: it holds the size of the table (excluding the first record) and - * the label to branch to when none of the values in the case table match. - * The case table is sorted on the comparison value. This allows more advanced - * abstract machines to sift the case table with a binary search. - */ -SC_FUNC void ffswitch(int label) -{ - stgwrite("\tswitch "); - outval(label,TRUE); /* the label is the address of the case table */ - code_idx+=opcodes(1)+opargs(1); -} - -SC_FUNC void ffcase(cell value,char *labelname,int newtable) -{ - if (newtable) { - stgwrite("\tcasetbl\n"); - code_idx+=opcodes(1); - } /* if */ - stgwrite("\tcase "); - outval(value,FALSE); - stgwrite(" "); - stgwrite(labelname); - stgwrite("\n"); - code_idx+=opcodes(0)+opargs(2); -} - -/* - * Call specified function - */ -SC_FUNC void ffcall(symbol *sym,const char *label,int numargs) -{ - char symname[2*sNAMEMAX+16]; - - assert(sym!=NULL); - assert(sym->ident==iFUNCTN); - if (sc_asmfile) - funcdisplayname(symname,sym->name); - if ((sym->usage & uNATIVE)!=0) { - /* reserve a SYSREQ id if called for the first time */ - assert(label==NULL); - if (sc_status==statWRITE && (sym->usage & uREAD)==0 && sym->addr>=0) - sym->addr=ntv_funcid++; - stgwrite("\tsysreq.c "); - outval(sym->addr,FALSE); - if (sc_asmfile) { - stgwrite("\t; "); - stgwrite(symname); - } /* if */ - stgwrite("\n"); /* write on a separate line, to mark a sequence point for the peephole optimizer */ - stgwrite("\tstack "); - outval((numargs+1)*sizeof(cell), TRUE); - code_idx+=opcodes(2)+opargs(2); - } else { - /* normal function */ - stgwrite("\tcall "); - if (label!=NULL) { - stgwrite("l."); - stgwrite(label); - } else { - stgwrite(sym->name); - } /* if */ - if (sc_asmfile - && (label!=NULL || !isalpha(sym->name[0]) && sym->name[0]!='_' && sym->name[0]!=sc_ctrlchar)) - { - stgwrite("\t; "); - stgwrite(symname); - } /* if */ - stgwrite("\n"); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* Return from function - * - * Global references: funcstatus (referred to only) - */ -SC_FUNC void ffret(int remparams) -{ - if (remparams) - stgwrite("\tretn\n"); - else - stgwrite("\tret\n"); - code_idx+=opcodes(1); -} - -SC_FUNC void ffabort(int reason) -{ - stgwrite("\thalt "); - outval(reason,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -SC_FUNC void ffbounds(cell size) -{ - if ((sc_debug & sCHKBOUNDS)!=0) { - stgwrite("\tbounds "); - outval(size,TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* - * Jump to local label number (the number is converted to a name) - */ -SC_FUNC void jumplabel(int number) -{ - stgwrite("\tjump "); - outval(number,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* - * Define storage (global and static variables) - */ -SC_FUNC void defstorage(void) -{ - stgwrite("dump "); -} - -/* - * Inclrement/decrement stack pointer. Note that this routine does - * nothing if the delta is zero. - */ -SC_FUNC void modstk(int delta) -{ - if (delta) { - stgwrite("\tstack "); - outval(delta, TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* set the stack to a hard offset from the frame */ -SC_FUNC void setstk(cell value) -{ - stgwrite("\tlctrl 5\n"); /* get FRM in PRI */ - assert(value<=0); /* STK should always become <= FRM */ - if (value<0) { - stgwrite("\tadd.c "); - outval(value, TRUE); /* add (negative) offset */ - code_idx+=opcodes(1)+opargs(1); - // ??? write zeros in the space between STK and the value in PRI (the new stk) - // get value of STK in ALT - // zero PRI - // need new FILL opcode that takes a variable size - } /* if */ - stgwrite("\tsctrl 4\n"); /* store in STK */ - code_idx+=opcodes(2)+opargs(2); -} - -SC_FUNC void modheap(int delta) -{ - if (delta) { - stgwrite("\theap "); - outval(delta, TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -SC_FUNC void setheap_pri(void) -{ - stgwrite("\theap "); /* ALT = HEA++ */ - outval(sizeof(cell), TRUE); - stgwrite("\tstor.i\n"); /* store PRI (default value) at address ALT */ - stgwrite("\tmove.pri\n"); /* move ALT to PRI: PRI contains the address */ - code_idx+=opcodes(3)+opargs(1); -} - -SC_FUNC void setheap(cell value) -{ - stgwrite("\tconst.pri "); /* load default value in PRI */ - outval(value, TRUE); - code_idx+=opcodes(1)+opargs(1); - setheap_pri(); -} - -/* - * Convert a cell number to a "byte" address; i.e. double or quadruple - * the primary register. - */ -SC_FUNC void cell2addr(void) -{ - #if PAWN_CELL_SIZE==16 - stgwrite("\tshl.c.pri 1\n"); - #elif PAWN_CELL_SIZE==32 - stgwrite("\tshl.c.pri 2\n"); - #elif PAWN_CELL_SIZE==64 - stgwrite("\tshl.c.pri 3\n"); - #else - #error Unsupported cell size - #endif - code_idx+=opcodes(1)+opargs(1); -} - -/* - * Double or quadruple the alternate register. - */ -SC_FUNC void cell2addr_alt(void) -{ - #if PAWN_CELL_SIZE==16 - stgwrite("\tshl.c.alt 1\n"); - #elif PAWN_CELL_SIZE==32 - stgwrite("\tshl.c.alt 2\n"); - #elif PAWN_CELL_SIZE==64 - stgwrite("\tshl.c.alt 3\n"); - #else - #error Unsupported cell size - #endif - code_idx+=opcodes(1)+opargs(1); -} - -/* - * Convert "distance of addresses" to "number of cells" in between. - * Or convert a number of packed characters to the number of cells (with - * truncation). - */ -SC_FUNC void addr2cell(void) -{ - #if PAWN_CELL_SIZE==16 - stgwrite("\tshr.c.pri 1\n"); - #elif PAWN_CELL_SIZE==32 - stgwrite("\tshr.c.pri 2\n"); - #elif PAWN_CELL_SIZE==64 - stgwrite("\tshr.c.pri 3\n"); - #else - #error Unsupported cell size - #endif - code_idx+=opcodes(1)+opargs(1); -} - -/* Convert from character index to byte address. This routine does - * nothing if a character has the size of a byte. - */ -SC_FUNC void char2addr(void) -{ - #if sCHARBITS==16 - stgwrite("\tshl.c.pri 1\n"); - code_idx+=opcodes(1)+opargs(1); - #endif -} - -/* Align PRI (which should hold a character index) to an address. - * The first character in a "pack" occupies the highest bits of - * the cell. This is at the lower memory address on Big Endian - * computers and on the higher address on Little Endian computers. - * The ALIGN.pri/alt instructions must solve this machine dependence; - * that is, on Big Endian computers, ALIGN.pri/alt shuold do nothing - * and on Little Endian computers they should toggle the address. - */ -SC_FUNC void charalign(void) -{ - stgwrite("\talign.pri "); - outval(sCHARBITS/8,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* - * Add a constant to the primary register. - */ -SC_FUNC void addconst(cell value) -{ - if (value!=0) { - stgwrite("\tadd.c "); - outval(value,TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* - * signed multiply of primary and secundairy registers (result in primary) - */ -SC_FUNC void os_mult(void) -{ - stgwrite("\tsmul\n"); - code_idx+=opcodes(1); -} - -/* - * signed divide of alternate register by primary register (quotient in - * primary; remainder in alternate) - */ -SC_FUNC void os_div(void) -{ - stgwrite("\tsdiv.alt\n"); - code_idx+=opcodes(1); -} - -/* - * modulus of (alternate % primary), result in primary (signed) - */ -SC_FUNC void os_mod(void) -{ - stgwrite("\tsdiv.alt\n"); - stgwrite("\tmove.pri\n"); /* move ALT to PRI */ - code_idx+=opcodes(2); -} - -/* - * Add primary and alternate registers (result in primary). - */ -SC_FUNC void ob_add(void) -{ - stgwrite("\tadd\n"); - code_idx+=opcodes(1); -} - -/* - * subtract primary register from alternate register (result in primary) - */ -SC_FUNC void ob_sub(void) -{ - stgwrite("\tsub.alt\n"); - code_idx+=opcodes(1); -} - -/* - * arithmic shift left alternate register the number of bits - * given in the primary register (result in primary). - * There is no need for a "logical shift left" routine, since - * logical shift left is identical to arithmic shift left. - */ -SC_FUNC void ob_sal(void) -{ - stgwrite("\txchg\n"); - stgwrite("\tshl\n"); - code_idx+=opcodes(2); -} - -/* - * arithmic shift right alternate register the number of bits - * given in the primary register (result in primary). - */ -SC_FUNC void os_sar(void) -{ - stgwrite("\txchg\n"); - stgwrite("\tsshr\n"); - code_idx+=opcodes(2); -} - -/* - * logical (unsigned) shift right of the alternate register by the - * number of bits given in the primary register (result in primary). - */ -SC_FUNC void ou_sar(void) -{ - stgwrite("\txchg\n"); - stgwrite("\tshr\n"); - code_idx+=opcodes(2); -} - -/* - * inclusive "or" of primary and alternate registers (result in primary) - */ -SC_FUNC void ob_or(void) -{ - stgwrite("\tor\n"); - code_idx+=opcodes(1); -} - -/* - * "exclusive or" of primary and alternate registers (result in primary) - */ -SC_FUNC void ob_xor(void) -{ - stgwrite("\txor\n"); - code_idx+=opcodes(1); -} - -/* - * "and" of primary and secundairy registers (result in primary) - */ -SC_FUNC void ob_and(void) -{ - stgwrite("\tand\n"); - code_idx+=opcodes(1); -} - -/* - * test ALT==PRI; result in primary register (1 or 0). - */ -SC_FUNC void ob_eq(void) -{ - stgwrite("\teq\n"); - code_idx+=opcodes(1); -} - -/* - * test ALT!=PRI - */ -SC_FUNC void ob_ne(void) -{ - stgwrite("\tneq\n"); - code_idx+=opcodes(1); -} - -/* The abstract machine defines the relational instructions so that PRI is - * on the left side and ALT on the right side of the operator. For example, - * SLESS sets PRI to either 1 or 0 depending on whether the expression - * "PRI < ALT" is true. - * - * The compiler generates comparisons with ALT on the left side of the - * relational operator and PRI on the right side. The XCHG instruction - * prefixing the relational operators resets this. We leave it to the - * peephole optimizer to choose more compact instructions where possible. - */ - -/* Relational operator prefix for chained relational expressions. The - * "suffix" code restores the stack. - * For chained relational operators, the goal is to keep the comparison - * result "so far" in PRI and the value of the most recent operand in - * ALT, ready for a next comparison. - * The "prefix" instruction pushed the comparison result (PRI) onto the - * stack and moves the value of ALT into PRI. If there is a next comparison, - * PRI can now serve as the "left" operand of the relational operator. - */ -SC_FUNC void relop_prefix(void) -{ - stgwrite("\tpush.pri\n"); - stgwrite("\tmove.pri\n"); - code_idx+=opcodes(2); -} - -SC_FUNC void relop_suffix(void) -{ - stgwrite("\tswap.alt\n"); - stgwrite("\tand\n"); - stgwrite("\tpop.alt\n"); - code_idx+=opcodes(3); -} - -/* - * test ALTPRI (signed) - */ -SC_FUNC void os_gt(void) -{ - stgwrite("\txchg\n"); - stgwrite("\tsgrtr\n"); - code_idx+=opcodes(2); -} - -/* - * test ALT>=PRI (signed) - */ -SC_FUNC void os_ge(void) -{ - stgwrite("\txchg\n"); - stgwrite("\tsgeq\n"); - code_idx+=opcodes(2); -} - -/* - * logical negation of primary register - */ -SC_FUNC void lneg(void) -{ - stgwrite("\tnot\n"); - code_idx+=opcodes(1); -} - -/* - * two's complement primary register - */ -SC_FUNC void neg(void) -{ - stgwrite("\tneg\n"); - code_idx+=opcodes(1); -} - -/* - * one's complement of primary register - */ -SC_FUNC void invert(void) -{ - stgwrite("\tinvert\n"); - code_idx+=opcodes(1); -} - -/* - * nop - */ -SC_FUNC void nooperation(void) -{ - stgwrite("\tnop\n"); - code_idx+=opcodes(1); -} - - -/* increment symbol - */ -SC_FUNC void inc(value *lval) -{ - symbol *sym; - - sym=lval->sym; - if (lval->ident==iARRAYCELL) { - /* indirect increment, address already in PRI */ - stgwrite("\tinc.i\n"); - code_idx+=opcodes(1); - } else if (lval->ident==iARRAYCHAR) { - /* indirect increment of single character, address already in PRI */ - stgwrite("\tpush.pri\n"); - stgwrite("\tpush.alt\n"); - stgwrite("\tmove.alt\n"); /* copy address */ - stgwrite("\tlodb.i "); /* read from PRI into PRI */ - outval(sCHARBITS/8,TRUE); /* read one or two bytes */ - stgwrite("\tinc.pri\n"); - stgwrite("\tstrb.i "); /* write PRI to ALT */ - outval(sCHARBITS/8,TRUE); /* write one or two bytes */ - stgwrite("\tpop.alt\n"); - stgwrite("\tpop.pri\n"); - code_idx+=opcodes(8)+opargs(2); - } else if (lval->ident==iREFERENCE) { - assert(sym!=NULL); - stgwrite("\tpush.pri\n"); - /* load dereferenced value */ - assert(sym->vclass==sLOCAL); /* global references don't exist in Pawn */ - if (sym->vclass==sLOCAL) - stgwrite("\tlref.s.pri "); - else - stgwrite("\tlref.pri "); - outval(sym->addr,TRUE); - /* increment */ - stgwrite("\tinc.pri\n"); - /* store dereferenced value */ - if (sym->vclass==sLOCAL) - stgwrite("\tsref.s.pri "); - else - stgwrite("\tsref.pri "); - outval(sym->addr,TRUE); - stgwrite("\tpop.pri\n"); - code_idx+=opcodes(5)+opargs(2); - } else { - /* local or global variable */ - assert(sym!=NULL); - if (sym->vclass==sLOCAL) - stgwrite("\tinc.s "); - else - stgwrite("\tinc "); - outval(sym->addr,TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* decrement symbol - * - * in case of an integer pointer, the symbol must be incremented by 2. - */ -SC_FUNC void dec(value *lval) -{ - symbol *sym; - - sym=lval->sym; - if (lval->ident==iARRAYCELL) { - /* indirect decrement, address already in PRI */ - stgwrite("\tdec.i\n"); - code_idx+=opcodes(1); - } else if (lval->ident==iARRAYCHAR) { - /* indirect decrement of single character, address already in PRI */ - stgwrite("\tpush.pri\n"); - stgwrite("\tpush.alt\n"); - stgwrite("\tmove.alt\n"); /* copy address */ - stgwrite("\tlodb.i "); /* read from PRI into PRI */ - outval(sCHARBITS/8,TRUE); /* read one or two bytes */ - stgwrite("\tdec.pri\n"); - stgwrite("\tstrb.i "); /* write PRI to ALT */ - outval(sCHARBITS/8,TRUE); /* write one or two bytes */ - stgwrite("\tpop.alt\n"); - stgwrite("\tpop.pri\n"); - code_idx+=opcodes(8)+opargs(2); - } else if (lval->ident==iREFERENCE) { - assert(sym!=NULL); - stgwrite("\tpush.pri\n"); - /* load dereferenced value */ - assert(sym->vclass==sLOCAL); /* global references don't exist in Pawn */ - if (sym->vclass==sLOCAL) - stgwrite("\tlref.s.pri "); - else - stgwrite("\tlref.pri "); - outval(sym->addr,TRUE); - /* decrement */ - stgwrite("\tdec.pri\n"); - /* store dereferenced value */ - if (sym->vclass==sLOCAL) - stgwrite("\tsref.s.pri "); - else - stgwrite("\tsref.pri "); - outval(sym->addr,TRUE); - stgwrite("\tpop.pri\n"); - code_idx+=opcodes(5)+opargs(2); - } else { - /* local or global variable */ - assert(sym!=NULL); - if (sym->vclass==sLOCAL) - stgwrite("\tdec.s "); - else - stgwrite("\tdec "); - outval(sym->addr,TRUE); - code_idx+=opcodes(1)+opargs(1); - } /* if */ -} - -/* - * Jumps to "label" if PRI != 0 - */ -SC_FUNC void jmp_ne0(int number) -{ - stgwrite("\tjnz "); - outval(number,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* - * Jumps to "label" if PRI == 0 - */ -SC_FUNC void jmp_eq0(int number) -{ - stgwrite("\tjzer "); - outval(number,TRUE); - code_idx+=opcodes(1)+opargs(1); -} - -/* write a value in hexadecimal; optionally adds a newline */ -SC_FUNC void outval(cell val,int newline) -{ - stgwrite(itoh(val)); - if (newline) - stgwrite("\n"); -} diff --git a/compiler-init/compiler-init/sc5.c b/compiler-init/compiler-init/sc5.c deleted file mode 100644 index d9d0298a..00000000 --- a/compiler-init/compiler-init/sc5.c +++ /dev/null @@ -1,228 +0,0 @@ -/* Pawn compiler - Error message system - * In fact a very simple system, using only 'panic mode'. - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc5.c 3579 2006-06-06 13:35:29Z thiadmer $ - */ -#include -#if defined __WIN32__ || defined _WIN32 || defined __MSDOS__ - #include -#endif -#if defined LINUX || defined __GNUC__ - #include -#endif -#include -#include -#include /* ANSI standardized variable argument list functions */ -#include -#if defined FORTIFY - #include -#endif -#include "sc.h" - -#if defined _MSC_VER - #pragma warning(push) - #pragma warning(disable:4125) /* decimal digit terminates octal escape sequence */ -#endif - -#include "sc5.scp" - -#if defined _MSC_VER - #pragma warning(pop) -#endif - -#define NUM_WARNINGS (sizeof warnmsg / sizeof warnmsg[0]) -static unsigned char warndisable[(NUM_WARNINGS + 7) / 8]; /* 8 flags in a char */ - -static int errflag; -static int errstart; /* line number at which the instruction started */ -static int errline; /* forced line number for the error message */ - -/* error - * - * Outputs an error message (note: msg is passed optionally). - * If an error is found, the variable "errflag" is set and subsequent - * errors are ignored until lex() finds a semicolumn or a keyword - * (lex() resets "errflag" in that case). - * - * Global references: inpfname (reffered to only) - * fline (reffered to only) - * fcurrent (reffered to only) - * errflag (altered) - */ -SC_FUNC int error(int number,...) -{ -static char *prefix[3]={ "error", "fatal error", "warning" }; -static int lastline,errorcount; -static short lastfile; - char *msg,*pre; - va_list argptr; - char string[128]; - - /* errflag is reset on each semicolon. - * In a two-pass compiler, an error should not be reported twice. Therefore - * the error reporting is enabled only in the second pass (and only when - * actually producing output). Fatal errors may never be ignored. - */ - if ((errflag || sc_status!=statWRITE) && (number<100 || number>=200)) - return 0; - - /* also check for disabled warnings */ - if (number>=200) { - int index=(number-200)/8; - int mask=1 << ((number-200)%8); - if ((warndisable[index] & mask)!=0) - return 0; - } /* if */ - - if (number<100){ - msg=errmsg[number-1]; - pre=prefix[0]; - errflag=TRUE; /* set errflag (skip rest of erroneous expression) */ - errnum++; - } else if (number<200){ - msg=fatalmsg[number-100]; - pre=prefix[1]; - errnum++; /* a fatal error also counts as an error */ - } else { - msg=warnmsg[number-200]; - pre=prefix[2]; - warnnum++; - } /* if */ - - strexpand(string,(unsigned char *)msg,sizeof string,SCPACK_TABLE); - - assert(errstart<=fline); - if (errline>0) - errstart=errline; - else - errline=fline; - assert(errstart<=errline); - va_start(argptr,number); - if (strlen(errfname)==0) { - int start= (errstart==errline) ? -1 : errstart; - if (pc_error(number,string,inpfname,start,errline,argptr)) { - if (outf!=NULL) { - pc_closeasm(outf,TRUE); - outf=NULL; - } /* if */ - longjmp(errbuf,3); /* user abort */ - } /* if */ - } else { - FILE *fp=fopen(errfname,"a"); - if (fp!=NULL) { - if (errstart>=0 && errstart!=errline) - fprintf(fp,"%s(%d -- %d) : %s %03d: ",inpfname,errstart,errline,pre,number); - else - fprintf(fp,"%s(%d) : %s %03d: ",inpfname,errline,pre,number); - vfprintf(fp,string,argptr); - fclose(fp); - } /* if */ - } /* if */ - va_end(argptr); - - if (number>=100 && number<200 || errnum>25){ - if (strlen(errfname)==0) { - va_start(argptr,number); - pc_error(0,"\nCompilation aborted.",NULL,0,0,argptr); - va_end(argptr); - } /* if */ - if (outf!=NULL) { - pc_closeasm(outf,TRUE); - outf=NULL; - } /* if */ - longjmp(errbuf,2); /* fatal error, quit */ - } /* if */ - - errline=-1; - /* check whether we are seeing many errors on the same line */ - if ((errstart<0 && lastline!=fline) || lastlinefline || fcurrent!=lastfile) - errorcount=0; - lastline=fline; - lastfile=fcurrent; - if (number<200) - errorcount++; - if (errorcount>=3) - error(107); /* too many error/warning messages on one line */ - - return 0; -} - -SC_FUNC void errorset(int code,int line) -{ - switch (code) { - case sRESET: - errflag=FALSE; /* start reporting errors */ - break; - case sFORCESET: - errflag=TRUE; /* stop reporting errors */ - break; - case sEXPRMARK: - errstart=fline; /* save start line number */ - break; - case sEXPRRELEASE: - errstart=-1; /* forget start line number */ - errline=-1; - break; - case sSETPOS: - errline=line; - break; - } /* switch */ -} - -/* sc_enablewarning() - * Enables or disables a warning (errors cannot be disabled). - * Initially all warnings are enabled. The compiler does this by setting bits - * for the *disabled* warnings and relying on the array to be zero-initialized. - * - * Parameter enable can be: - * o 0 for disable - * o 1 for enable - * o 2 for toggle - */ -int pc_enablewarning(int number,int enable) -{ - int index; - unsigned char mask; - - if (number<200) - return FALSE; /* errors and fatal errors cannot be disabled */ - number -= 200; - if (number>=NUM_WARNINGS) - return FALSE; - - index=number/8; - mask=(unsigned char)(1 << (number%8)); - switch (enable) { - case 0: - warndisable[index] |= mask; - break; - case 1: - warndisable[index] &= (unsigned char)~mask; - break; - case 2: - warndisable[index] ^= mask; - break; - } /* switch */ - - return TRUE; -} - -#undef SCPACK_TABLE diff --git a/compiler-init/compiler-init/sc5.scp b/compiler-init/compiler-init/sc5.scp deleted file mode 100644 index a08315c2..00000000 --- a/compiler-init/compiler-init/sc5.scp +++ /dev/null @@ -1,342 +0,0 @@ -/* Pawn compiler - Error message strings (plain and compressed formats) - * - * Copyright (c) ITB CompuPhase, 2000-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc5.sch 3590 2006-06-24 14:16:39Z thiadmer $ - */ - -SC_FUNC int strexpand(char *dest, unsigned char *source, int maxlen, unsigned char pairtable[128][2]); - -#define SCPACK_TABLE errstr_table -/*-*SCPACK start of pair table, do not change or remove this line */ -unsigned char errstr_table[][2] = { - {101,32}, {111,110}, {116,32}, {105,110}, {97,114}, {116,105}, {100,32}, {115,32}, {101,114}, {97,108}, {101,110}, {37,115}, {133,129}, {34,139}, {141,34}, {117,110}, - {110,111}, {114,101}, {115,105}, {121,32}, {97,116}, {111,114}, {97,110}, {32,142}, {109,98}, {115,116}, {41,10}, {100,101}, {109,138}, {101,134}, {98,108}, {140,32}, - {111,108}, {114,97}, {144,130}, {118,137}, {143,99}, {102,164}, {115,121}, {166,152}, {167,160}, {117,115}, {97,32}, {115,146}, {97,158}, {149,32}, {132,161}, {105,134}, - {103,32}, {163,175}, {103,117}, {178,156}, {136,32}, {132,179}, {131,177}, {111,102}, {116,104}, {101,120}, {105,135}, {165,159}, {101,100}, {99,104}, {118,132}, {168,151}, - {105,172}, {190,192}, {155,102}, {174,147}, {183,32}, {109,97}, {116,111}, {99,129}, {101,135}, {181,130}, {98,128}, {115,10}, {112,145}, {153,148}, {44,32}, {40,191}, - {169,130}, {151,10}, {101,10}, {207,154}, {109,208}, {116,97}, {105,99}, {194,131}, {193,128}, {34,32}, {129,32}, {132,97}, {100,105}, {146,122}, {110,32}, {137,32}, - {104,97}, {101,108}, {117,108}, {99,111}, {108,111}, {109,148}, {199,153}, {58,209}, {111,112}, {97,115}, {108,128}, {232,136}, {230,150}, {150,32}, {204,171}, {131,176}, - {212,202}, {102,105}, {119,105}, {185,238}, {109,112}, {116,136}, {165,140}, {197,147}, {102,149}, {111,32}, {131,32}, {213,176}, {110,117}, {115,117}, {118,128} -}; -/*-*SCPACK end of pair table, do not change or remove this line */ - -static char *errmsg[] = { -#ifdef SCPACK -/*001*/ "expected token: \"%s\", but found \"%s\"\n", -/*002*/ "only a single statement (or expression) can follow each \"case\"\n", -/*003*/ "declaration of a local variable must appear in a compound block\n", -/*004*/ "function \"%s\" is not implemented\n", -/*005*/ "function may not have arguments\n", -/*006*/ "must be assigned to an array\n", -/*007*/ "operator cannot be redefined\n", -/*008*/ "must be a constant expression; assumed zero\n", -/*009*/ "invalid array size (negative, zero or out of bounds)\n", -/*010*/ "invalid function or declaration\n", -/*011*/ "invalid outside functions\n", -/*012*/ "invalid function call, not a valid address\n", -/*013*/ "no entry point (no public functions)\n", -/*014*/ "invalid statement; not in switch\n", -/*015*/ "\"default\" case must be the last case in switch statement\n", -/*016*/ "multiple defaults in \"switch\"\n", -/*017*/ "undefined symbol \"%s\"\n", -/*018*/ "initialization data exceeds declared size\n", -/*019*/ "not a label: \"%s\"\n", -/*020*/ "invalid symbol name \"%s\"\n", -/*021*/ "symbol already defined: \"%s\"\n", -/*022*/ "must be lvalue (non-constant)\n", -/*023*/ "array assignment must be simple assignment\n", -/*024*/ "\"break\" or \"continue\" is out of context\n", -/*025*/ "function heading differs from prototype\n", -/*026*/ "no matching \"#if...\"\n", -/*027*/ "invalid character constant\n", -/*028*/ "invalid subscript (not an array or too many subscripts): \"%s\"\n", -/*029*/ "invalid expression, assumed zero\n", -/*030*/ "compound statement not closed at the end of file (started at line %d)\n", -/*031*/ "unknown directive\n", -/*032*/ "array index out of bounds (variable \"%s\")\n", -/*033*/ "array must be indexed (variable \"%s\")\n", -/*034*/ "argument does not have a default value (argument %d)\n", -/*035*/ "argument type mismatch (argument %d)\n", -/*036*/ "empty statement\n", -/*037*/ "invalid string (possibly non-terminated string)\n", -/*038*/ "extra characters on line\n", -/*039*/ "constant symbol has no size\n", -/*040*/ "duplicate \"case\" label (value %d)\n", -/*041*/ "invalid ellipsis, array size is not known\n", -/*042*/ "invalid combination of class specifiers\n", -/*043*/ "character constant exceeds range for packed string\n", -/*044*/ "positional parameters must precede all named parameters\n", -/*045*/ "too many function arguments\n", -/*046*/ "unknown array size (variable \"%s\")\n", -/*047*/ "array sizes do not match, or destination array is too small\n", -/*048*/ "array dimensions do not match\n", -/*049*/ "invalid line continuation\n", -/*050*/ "invalid range\n", -/*051*/ "invalid subscript, use \"[ ]\" operators on major dimensions\n", -/*052*/ "multi-dimensional arrays must be fully initialized\n", -/*053*/ "exceeding maximum number of dimensions\n", -/*054*/ "unmatched closing brace (\"}\")\n", -/*055*/ "start of function body without function header\n", -/*056*/ "arrays, local variables and function arguments cannot be public (variable \"%s\")\n", -/*057*/ "unfinished expression before compiler directive\n", -/*058*/ "duplicate argument; same argument is passed twice\n", -/*059*/ "function argument may not have a default value (variable \"%s\")\n", -/*060*/ "multiple \"#else\" directives between \"#if ... #endif\"\n", -/*061*/ "\"#elseif\" directive follows an \"#else\" directive\n", -/*062*/ "number of operands does not fit the operator\n", -/*063*/ "function result tag of operator \"%s\" must be \"%s\"\n", -/*064*/ "cannot change predefined operators\n", -/*065*/ "function argument may only have a single tag (argument %d)\n", -/*066*/ "function argument may not be a reference argument or an array (argument \"%s\")\n", -/*067*/ "variable cannot be both a reference and an array (variable \"%s\")\n", -/*068*/ "invalid rational number precision in #pragma\n", -/*069*/ "rational number format already defined\n", -/*070*/ "rational number support was not enabled\n", -/*071*/ "user-defined operator must be declared before use (function \"%s\")\n", -/*072*/ "\"sizeof\" operator is invalid on \"function\" symbols\n", -/*073*/ "function argument must be an array (argument \"%s\")\n", -/*074*/ "#define pattern must start with an alphabetic character\n", -/*075*/ "input line too long (after substitutions)\n", -/*076*/ "syntax error in the expression, or invalid function call\n", -/*077*/ "malformed UTF-8 encoding, or corrupted file: %s\n", -/*078*/ "function uses both \"return\" and \"return \"\n", -/*079*/ "inconsistent return types (array & non-array)\n", -/*080*/ "unknown symbol, or not a constant symbol (symbol \"%s\")\n", -/*081*/ "cannot take a tag as a default value for an indexed array parameter (symbol \"%s\")\n", -/*082*/ "user-defined operators and native functions may not have states\n", -/*083*/ "a function or variable may only belong to a single automaton (symbol \"%s\")\n", -/*084*/ "state conflict: one of the states is already assigned to another implementation (symbol \"%s\")\n", -/*085*/ "no states are defined for symbol \"%s\"\n", -/*086*/ "unknown automaton \"%s\"\n", -/*087*/ "unknown state \"%s\" for automaton \"%s\"\n", -/*088*/ "public variables and local variables may not have states (symbol \"%s\")\n", -/*089*/ "state variables may not be initialized (symbol \"%s\")\n", -/*090*/ "public functions may not return arrays (symbol \"%s\")\n", -/*091*/ "ambiguous constant; tag override is required (symbol \"%s\")\n" -#else - "\271pect\235\306k\212:\227\316bu\202fo\217\206\216\012", - "\201l\223\252s\203g\352\315e\234\202(\255\363\201) c\355f\240\344w ea\275 \042c\351e\042\012", - "\233cl\333\237\304\252\344c\337\330\324appe\204 \372\252\343\364o\217\206\236ock\012", - "\366\227 \272\242i\364le\234t\274\012", - "\273\367\242\340\376\265t\313", - "\360a\253gn\235\306 \355\256y\012", - "\353\224\255c\226\242\312\221\327\274\012", - "\360\252\354\202\363\201; \351\375m\235z\210o\012", - "\266\303\335\200(nega\205ve\316z\210\371\255ou\202\304bo\217ds\232", - "\266\273\255\233cl\333\214\012", - "\266out\222d\200\366\313", - "\266\273c\211l\316\242\252\261add\221s\313", - "\220 \212tr\223po\203\202(\220 pu\236\326 \366s\232", - "\266\315e\234t; \242\372s\362t\275\012", - "\042\302a\342t\331c\351\200\360\270\200l\351\202c\351\200\372s\362t\275 \315e\234t\012", - "m\342\205p\352\302a\342t\207\372\042s\362t\275\042\012", - "\217\327\235\277\012", - "\203i\205\211iza\237d\224\252\271ce\274\207\233cl\204\235\335\322", - "\242\252lab\341\347", - "\266\250 nam\200\216\012", - "\250 \211\221ad\223\327\274\347", - "\360l\243u\200(n\201-\354t\232", - "\303a\253gn\234\202\360\222\364\352a\253gn\234t\012", - "\042b\221ak\331\255\042\307t\203ue\331\272ou\202\304\307t\271t\012", - "\273head\357\334ff\210\207from pro\306typ\322", - "\220 \345\275\357\042#if...\042\012", - "\266\275\333ct\264\354t\012", - "\266\375bscrip\202(\242\355\303\255\306\371m\226\223\375bscripts)\347", - "\266\363\201\316\351\375m\235z\210o\012", - "\343\364o\217\206\315e\234\202\242c\344s\235a\202\270\200\212\206\304\361\352(\231\204t\235a\202l\203\200%d\232", - "\217k\220w\336\334\221c\205v\322", - "\303\203\233x ou\202\304bo\217d\207(\330\216\232", - "\303\360\203\233x\235(\330\216\232", - "\311do\310\242\340\376\252\302a\342\202\243u\200(\311%d\232", - "\311typ\200mis\345\275 (\311%d\232", - "e\364t\223\315e\234t\012", - "\266\231r\357(po\253\236\223n\201-\365m\203\224\235\231r\203g\232", - "\271t\241 \275\333c\365\207\332l\203\322", - "\354\202\250 \340\207\220 \335\322", - "dupl\326\224\200\042c\351e\331lab\341 (\243u\200%d\232", - "\266\341lip\222s\316\303\335\200\272\242k\220wn\012", - "\266\343\230\203a\237\304cl\351\207speci\361\210\313", - "\275\333ct\264\354\202\271ce\274\207r\226g\200f\255pack\235\231r\203g\012", - "po\222\214\337p\333me\365\207\324\314c\274\200\211l nam\235p\333me\365\313", - "\306\371m\226\223\273\265t\313", - "\217k\220w\336\303\335\200(\330\216\232", - "\303\335\310d\371\242\345\275\316\255\233\231\203a\237\303\272\306\371sm\211l\012", - "\303\334\234\222\201\207d\371\242\345\275\012", - "\266l\203\200\307t\203ua\214\012", - "\266r\226g\322", - "\266\375bscript\316\251\200\042[ ]\331\353\224\225\207\332\305j\255\334\234\222\201\313", - "m\342\205-\334\234\222\201\337\256y\207\360f\342l\223\203i\205\211iz\274\012", - "\271ce\274\357\305ximum \374\230\264\304\334\234\222\201\313", - "\217\345\275\235c\344s\357b\241c\200(\042}\042\232", - "\231\204\202\304\273bod\223\362\270ou\202\273head\210\012", - "\256ys\316\344c\337\301\310\226\206\273\265t\207c\226\242\312pu\236\326 (\330\216\232", - "\217f\203ish\235\363\332be\370\200\343\364il\264\334\221c\205v\322", - "dupl\326\224\200\265t; sam\200\311\272p\351s\235tw\326\322", - "\273\311\367\242\340\376\252\302a\342\202\243u\200(\330\216\232", - "m\342\205p\352\042#\341se\331\334\221c\205v\310betwe\212 \042#if ... #\212\334f\042\012", - "\042#\341seif\331\334\221c\205\376f\240\344w\207\355\042#\341se\331\334\221c\205v\322", - "\374\230\264\304\353\226d\207do\310\242\361\202\270\200\353\224\225\012", - "\273\221s\342\202\373\304\353\224\225\227 \360\216\012", - "c\226\242\275\226g\200\314\327\235\353\224\225\313", - "\273\311\367\201l\223\340\376\252s\203g\352\373(\311%d\232", - "\273\311\367\242\312\252\221f\210\212c\200\311\255\355\303(\311\216\232", - "\330c\226\242\312bo\270 \252\221f\210\212c\200\226\206\355\303(\330\216\232", - "\266\241\214\337\374\230\264\314ci\222\332\372#p\241g\305\012", - "\241\214\337\374\230\264\370\305\202\211\221ad\223\327\274\012", - "\241\214\337\374\230\264\375pp\225\202wa\207\242\212\254\274\012", - "\251\210-\327\235\353\224\255\360\233cl\204\235be\370\200\251\200(\366\227\232", - "\042\335e\267\331\353\224\255\272\266\332\042\366\331\250\313", - "\273\311\360\355\303(\311\216\232", - "#\327\200p\224\365\336\324\231\204\202\362\270 \355\211p\340be\205c \275\333c\365\012", - "\203pu\202l\203\200\306\371l\201\260(aft\264\375bs\205tu\214s\232", - "\246n\325x \210r\255\372\270\200\363\201\316\255\266\273c\211l\012", - "m\211\370m\235UTF-8 \212\343d\203g\316\255c\225rupt\235\361le: \213\012", - "\273\251\310bo\270 \042\221turn\331\226\206\042\221tur\336<\243ue>\042\012", - "\203\307\222\231\212\202\221tur\336typ\310(\303& n\201-\256y\232", - "\217k\220w\336\250\316\255\242\252\354\202\250 \323", - "c\226\242\325k\200\252\373a\207\252\302a\342\202\243u\200f\255\355\203\233x\235\303p\333met\264\323", - "\251\210-\327\235\353\224\225\207\226\206na\205\376\366\207\367\242\340\376\315e\313", - "\252\273\255\330\367\201l\223b\341\201\260\306 \252s\203g\352au\306\345\332\323", - "\315\200\307fl\326t: \201\200\304\270\200\315\310\272\211\221ad\223a\253gn\235\306 a\220\270\264i\364le\234\325\237\323", - "\220 \315\310\204\200\327\235f\255\277\012", - "\217k\220w\336au\306\345\201\321", - "\217k\220w\336\315\200\216 f\255au\306\345\201\321", - "pu\236\326 \301\310\226\206\344c\337\301\310\367\242\340\376\315\310\323", - "\315\200\301\310\367\242\312\203i\205\211iz\235\323", - "pu\236\326 \366\207\367\242\221tur\336\256y\207\323", - "a\230i\262ou\207\354t; \373ov\210rid\200\272\221qui\221\206\323" -#endif - }; - -static char *fatalmsg[] = { -#ifdef SCPACK -/*100*/ "cannot read from file: \"%s\"\n", -/*101*/ "cannot write to file: \"%s\"\n", -/*102*/ "table overflow: \"%s\"\n", - /* table can be: loop table - * literal table - * staging buffer - * option table (response file) - * peephole optimizer table - */ -/*103*/ "insufficient memory\n", -/*104*/ "invalid assembler instruction \"%s\"\n", -/*105*/ "numeric overflow, exceeding capacity\n", -/*106*/ "compiled script exceeds the maximum memory size (%ld bytes)\n", -/*107*/ "too many error messages on one line\n", -/*108*/ "codepage mapping file not found\n", -/*109*/ "invalid path: \"%s\"\n", -/*110*/ "assertion failed: %s\n", -/*111*/ "user error: %s\n", -#else - "c\226\242\221a\206from \361le\347", - "c\226\242writ\200\306 \361le\347", - "t\254\200ov\210f\344w\347", - "\203\375ff\326i\212\202mem\225y\012", - "\266\351se\230l\264\203\231ruc\214\321", - "\374m\210\326 ov\210f\344w\316\271ce\274\357capacity\012", - "\343\364il\235scrip\202\271ce\274\207\270\200\305ximum mem\225\223\335\200(%l\206bytes\232", - "\306\371m\226\223\210r\255messag\310\332\201\200l\203\322", - "\343\233pag\200\305pp\357\361\352\242fo\217d\012", - "\266p\224h\347", - "\351s\210\237fail\274: \213\012", - "\251\264\210r\225: \213\012" -#endif - }; - -static char *warnmsg[] = { -#ifdef SCPACK -/*200*/ "symbol \"%s\" is truncated to %d characters\n", -/*201*/ "redefinition of constant/macro (symbol \"%s\")\n", -/*202*/ "number of arguments does not match definition\n", -/*203*/ "symbol is never used: \"%s\"\n", -/*204*/ "symbol is assigned a value that is never used: \"%s\"\n", -/*205*/ "redundant code: constant expression is zero\n", -/*206*/ "redundant test: constant expression is non-zero\n", -/*207*/ "unknown #pragma\n", -/*208*/ "function with tag result used before definition, forcing reparse\n", -/*209*/ "function \"%s\" should return a value\n", -/*210*/ "possible use of symbol before initialization: \"%s\"\n", -/*211*/ "possibly unintended assignment\n", -/*212*/ "possibly unintended bitwise operation\n", -/*213*/ "tag mismatch\n", -/*214*/ "possibly a \"const\" array argument was intended: \"%s\"\n", -/*215*/ "expression has no effect\n", -/*216*/ "nested comment\n", -/*217*/ "loose indentation\n", -/*218*/ "old style prototypes used with optional semicolumns\n", -/*219*/ "local variable \"%s\" shadows a variable at a preceding level\n", -/*220*/ "expression with tag override must appear between parentheses\n", -/*221*/ "label name \"%s\" shadows tag name\n", -/*222*/ "number of digits exceeds rational number precision\n", -/*223*/ "redundant \"sizeof\": argument size is always 1 (symbol \"%s\")\n", -/*224*/ "indeterminate array size in \"sizeof\" expression (symbol \"%s\")\n", -/*225*/ "unreachable code\n", -/*226*/ "a variable is assigned to itself (symbol \"%s\")\n", -/*227*/ "more initiallers than enum fields\n", -/*228*/ "length of initialler exceeds size of the enum field\n", -/*229*/ "index tag mismatch (symbol \"%s\")\n", -/*230*/ "no implementation for state \"%s\" in function \"%s\", no fall-back\n", -/*231*/ "state specification on forward declaration is ignored\n", -/*232*/ "output file is written, but with compact encoding disabled\n", -/*233*/ "state variable \"%s\" shadows a global variable\n", -/*234*/ "function is depricated (symbol \"%s\") %s\n", -/*235*/ "public function lacks forward declaration (symbol \"%s\")\n", -/*236*/ "unknown parameter in substitution (incorrect #define pattern)\n" -#else - "\277 \272tr\244\224\235\306 %\206\275\333c\365\313", - "\221\327i\237\304\354t/\305cr\371\323", - "\374\230\264\304\265t\207do\310\242\345\275 \327i\214\012", - "\250 \272nev\264\251\274\347", - "\250 \272a\253gn\235\252\243u\200\270a\202\272nev\264\251\274\347", - "\221d\217d\226\202\343\233: \354\202\363\332\272z\210o\012", - "\221d\217d\226\202te\231: \354\202\363\332\272n\201-z\210o\012", - "\217k\220w\336#p\241g\305\012", - "\273\362\270 \373\221s\342\202\251\235be\370\200\327i\214\316\370c\357\221p\204s\322", - "\366\227 sho\342\206\221tur\336\252\243u\322", - "po\253\236\200\251\200\304\250 be\370\200\203i\205\211iza\214\347", - "po\253\236\223\217\203t\212\233\206a\253gn\234t\012", - "po\253\236\223\217\203t\212\233\206bit\362s\200\353a\214\012", - "\373mis\345\275\012", - "po\253\236\223\252\042\346\331\303\311wa\207\203t\212\233d\347", - "\363\332\340\207\220 effect\012", - "ne\231\235\343m\234t\012", - "\344os\200\203d\212\325\214\012", - "\240\206\231y\352pro\306typ\310\251\235\362\270 \350\214\337sem\326\240umn\313", - "\344c\337\330\216 s\340dow\207\252\330a\202\252\314c\274\357lev\341\012", - "\363\332\362\270 \373ov\210rid\200\324appe\204 betwe\212 p\204\212\270ese\313", - "lab\341 nam\200\216 s\340dow\207\373nam\322", - "\374\230\264\304\334git\207\271ce\274\207\241\214\337\374\230\264\314ci\222\201\012", - "\221d\217d\226\202\042\335e\267\042: \311\335\200\272\211way\2071 \323", - "\203\233\365m\203\224\200\303\335\200\372\042\335e\267\331\363\332\323", - "\217\221a\275\254\200\343\233\012", - "\252\330\272a\253gn\235\306 its\341f \323", - "m\225\200\203i\205\211l\210\207\270\355\212um \361\341d\313", - "l\212g\270 \304\203i\205\211l\264\271ce\274\207\335\200\304\270\200\212um \361\341d\012", - "\203\233x \373mis\345\275 \323", - "\220 i\364le\234\325\237f\255\315\200\216 \372\366\227\316\220 f\211l-back\012", - "\315\200specif\326a\237\332\370w\204\206\233cl\333\237\272ig\220\221d\012", - "outpu\202\361\352\272writt\212\316bu\202\362\270 \343\364ac\202\212\343d\357\334s\254\274\012", - "\315\200\330\216 s\340dow\207\252g\344b\337\301\322", - "\273\272\233pr\326\224\235\317) \213\012", - "pu\236\326 \273lack\207\370w\204\206\233cl\333\237\323", - "\217k\220w\336p\333met\264\372\375bs\205tu\237(\203c\225\221c\202#\327\200p\224\365n\232" -#endif - }; diff --git a/compiler-init/compiler-init/sc6.c b/compiler-init/compiler-init/sc6.c deleted file mode 100644 index 9dd5a169..00000000 --- a/compiler-init/compiler-init/sc6.c +++ /dev/null @@ -1,1298 +0,0 @@ -/* Pawn compiler - Binary code generation (the "assembler") - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc6.c 3579 2006-06-06 13:35:29Z thiadmer $ - */ -#include -#include -#include /* for macro max() */ -#include /* for macro offsetof() */ -#include -#include -#if defined FORTIFY - #include -#endif -#include "lstring.h" -#include "sc.h" -#include "amxdbg.h" -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ - #include -#endif - - -static void append_dbginfo(FILE *fout); - - -typedef cell (*OPCODE_PROC)(FILE *fbin,char *params,cell opcode); - -typedef struct { - cell opcode; - char *name; - int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */ - OPCODE_PROC func; -} OPCODE; - -static cell codeindex; /* similar to "code_idx" */ -static cell *lbltab; /* label table */ -static int writeerror; -static int bytes_in, bytes_out; -static jmp_buf compact_err; - -/* apparently, strtol() does not work correctly on very large (unsigned) - * hexadecimal values */ -static ucell hex2long(const char *s,char **n) -{ - ucell result=0L; - int negate=FALSE; - int digit; - - /* ignore leading whitespace */ - while (*s==' ' || *s=='\t') - s++; - - /* allow a negation sign to create the two's complement of numbers */ - if (*s=='-') { - negate=TRUE; - s++; - } /* if */ - - assert((*s>='0' && *s<='9') || (*s>='a' && *s<='f') || (*s>='a' && *s<='f')); - for ( ;; ) { - if (*s>='0' && *s<='9') - digit=*s-'0'; - else if (*s>='a' && *s<='f') - digit=*s-'a' + 10; - else if (*s>='A' && *s<='F') - digit=*s-'A' + 10; - else - break; /* probably whitespace */ - result=(result<<4) | digit; - s++; - } /* for */ - if (n!=NULL) - *n=(char*)s; - if (negate) - result=(~result)+1; /* take two's complement of the result */ - return (ucell)result; -} - -static ucell getparam(const char *s,char **n) -{ - ucell result=0; - for ( ;; ) { - result+=hex2long(s,(char**)&s); - if (*s!='+') - break; - s++; - } /* for */ - if (n!=NULL) - *n=(char*)s; - return result; -} - -#if BYTE_ORDER==BIG_ENDIAN -static uint16_t *align16(uint16_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - /* swap two bytes */ - t=s[0]; - s[0]=s[1]; - s[1]=t; - return v; -} - -static uint32_t *align32(uint32_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - /* swap outer two bytes */ - t=s[0]; - s[0]=s[3]; - s[3]=t; - /* swap inner two bytes */ - t=s[1]; - s[1]=s[2]; - s[2]=t; - return v; -} - -#if PAWN_CELL_SIZE>=64 -static uint64_t *align64(uint64_t *v) -{ - unsigned char *s = (unsigned char *)v; - unsigned char t; - - t=s[0]; - s[0]=s[7]; - s[7]=t; - - t=s[1]; - s[1]=s[6]; - s[6]=t; - - t=s[2]; - s[2]=s[5]; - s[5]=t; - - t=s[3]; - s[3]=s[4]; - s[4]=t; - - return v; -} -#endif - - #if PAWN_CELL_SIZE==16 - #define aligncell(v) align16(v) - #elif PAWN_CELL_SIZE==32 - #define aligncell(v) align32(v) - #elif PAWN_CELL_SIZE==64 - #define aligncell(v) align64(v) - #endif -#else - #define align16(v) (v) - #define align32(v) (v) - #define aligncell(v) (v) -#endif - -static char *skipwhitespace(char *str) -{ - while (isspace(*str)) - str++; - return str; -} - -static char *stripcomment(char *str) -{ - char *ptr=strchr(str,';'); - if (ptr!=NULL) { - *ptr++='\n'; /* terminate the line, but leave the '\n' */ - *ptr='\0'; - } /* if */ - return str; -} - -static void write_encoded(FILE *fbin,ucell *c,int num) -{ - #if PAWN_CELL_SIZE == 16 - #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ - #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ - #elif PAWN_CELL_SIZE == 32 - #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ - #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ - #elif PAWN_CELL_SIZE == 64 - #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ - #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ - #endif - - assert(fbin!=NULL); - while (num-->0) { - if (sc_compress) { - ucell p=(ucell)*c; - unsigned char t[ENC_MAX]; - unsigned char code; - int index; - for (index=0; index>=7; - } /* for */ - /* skip leading zeros */ - while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) - index--; - /* skip leading -1s */ - if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) - index--; - while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) - index--; - /* write high byte first, write continuation bits */ - assert(index>0); - while (index-->0) { - code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); - writeerror |= !pc_writebin(fbin,&code,1); - bytes_out++; - } /* while */ - bytes_in+=sizeof *c; - assert(AMX_COMPACTMARGIN>2); - if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) - longjmp(compact_err,1); - } else { - assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); - writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); - } /* if */ - c++; - } /* while */ -} - -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell noop(FILE *fbin,char *params,cell opcode) -{ - return 0; -} - -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell set_currentfile(FILE *fbin,char *params,cell opcode) -{ - fcurrent=(short)getparam(params,NULL); - return 0; -} - -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell parm0(FILE *fbin,char *params,cell opcode) -{ - if (fbin!=NULL) - write_encoded(fbin,(ucell*)&opcode,1); - return opcodes(1); -} - -static cell parm1(FILE *fbin,char *params,cell opcode) -{ - ucell p=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p,1); - } /* if */ - return opcodes(1)+opargs(1); -} - -static cell parm2(FILE *fbin,char *params,cell opcode) -{ - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - } /* if */ - return opcodes(1)+opargs(2); -} - -static cell parm3(FILE *fbin,char *params,cell opcode) -{ - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - } /* if */ - return opcodes(1)+opargs(3); -} - -static cell parm4(FILE *fbin,char *params,cell opcode) -{ - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,¶ms); - ucell p4=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - write_encoded(fbin,&p4,1); - } /* if */ - return opcodes(1)+opargs(4); -} - -static cell parm5(FILE *fbin,char *params,cell opcode) -{ - ucell p1=getparam(params,¶ms); - ucell p2=getparam(params,¶ms); - ucell p3=getparam(params,¶ms); - ucell p4=getparam(params,¶ms); - ucell p5=getparam(params,NULL); - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p1,1); - write_encoded(fbin,&p2,1); - write_encoded(fbin,&p3,1); - write_encoded(fbin,&p4,1); - write_encoded(fbin,&p5,1); - } /* if */ - return opcodes(1)+opargs(5); -} - -#if defined __BORLANDC__ || defined __WATCOMC__ - #pragma argsused -#endif -static cell do_dump(FILE *fbin,char *params,cell opcode) -{ - ucell p; - int num = 0; - - while (*params!='\0') { - p=getparam(params,¶ms); - if (fbin!=NULL) - write_encoded(fbin,&p,1); - num++; - while (isspace(*params)) - params++; - } /* while */ - return num*sizeof(cell); -} - -static cell do_call(FILE *fbin,char *params,cell opcode) -{ - char name[sNAMEMAX+1]; - int i; - symbol *sym; - ucell p; - - for (i=0; !isspace(*params); i++,params++) { - assert(*params!='\0'); - assert(i=0 && iident==iFUNCTN || sym->ident==iREFFUNC); - assert(sym->vclass==sGLOBAL); - p=sym->addr; - } /* if */ - - if (fbin!=NULL) { - write_encoded(fbin,(ucell*)&opcode,1); - write_encoded(fbin,&p,1); - } /* if */ - return opcodes(1)+opargs(1); -} - -static cell do_jump(FILE *fbin,char *params,cell opcode) -{ - int i; - ucell p; - - i=(int)hex2long(params,NULL); - assert(i>=0 && i=0 && i=0 && i=MAX_INSTR_LEN) - return 0; - strlcpy(str,instr,maxlen+1); - /* look up the instruction with a binary search - * the assembler is case insensitive to instructions (but case sensitive - * to symbols) - */ - low=1; /* entry 0 is reserved (for "not found") */ - high=(sizeof opcodelist / sizeof opcodelist[0])-1; - while (low0) - low=mid+1; - else - high=mid; - } /* while */ - - assert(low==high); - if (stricmp(str,opcodelist[low].name)==0) - return low; /* found */ - return 0; /* not found, return special index */ -} - -SC_FUNC int assemble(FILE *fout,FILE *fin) -{ - AMX_HEADER hdr; - AMX_FUNCSTUBNT func; - int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; - long nametablesize,nameofs; - #if PAWN_CELL_SIZE > 32 - char line[512]; - #else - char line[256]; - #endif - char *instr,*params; - int i,pass,size; - int16_t count; - symbol *sym, **nativelist; - constvalue *constptr; - cell mainaddr; - char nullchar = 0; - - /* if compression failed, restart the assembly with compaction switched off */ - if (setjmp(compact_err)!=0) { - assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ - sc_compress=FALSE; - pc_resetbin(fout,0); - error(232); /* disabled compact encoding */ - } /* if */ - - #if !defined NDEBUG - /* verify that the opcode list is sorted (skip entry 1; it is reserved - * for a non-existant opcode) - */ - assert(opcodelist[1].name!=NULL); - for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { - assert(opcodelist[i].name!=NULL); - assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); - } /* for */ - #endif - - writeerror=FALSE; - nametablesize=sizeof(int16_t); - numpublics=0; - numnatives=0; - numpubvars=0; - mainaddr=-1; - /* count number of public and native functions and public variables */ - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - int match=0; - if (sym->ident==iFUNCTN) { - if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) - match=++numnatives; - if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) - match=++numpublics; - if (strcmp(sym->name,uMAINFUNC)==0) { - assert(sym->vclass==sGLOBAL); - mainaddr=sym->addr; - } /* if */ - } else if (sym->ident==iVARIABLE) { - if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) - match=++numpubvars; - } /* if */ - if (match) { - char alias[sNAMEMAX+1]; - assert(sym!=NULL); - if ((sym->usage & uNATIVE)==0 || !lookup_alias(alias,sym->name)) { - assert(strlen(sym->name)<=sNAMEMAX); - strcpy(alias,sym->name); - } /* if */ - nametablesize+=strlen(alias)+1; - } /* if */ - } /* for */ - assert(numnatives==ntv_funcid); - - /* count number of libraries */ - numlibraries=0; - if (pc_addlibtable) { - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { - if (constptr->value>0) { - assert(strlen(constptr->name)>0); - numlibraries++; - nametablesize+=strlen(constptr->name)+1; - } /* if */ - } /* for */ - } /* if */ - - /* count number of public tags */ - numtags=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - if ((constptr->value & PUBLICTAG)!=0) { - assert(strlen(constptr->name)>0); - numtags++; - nametablesize+=strlen(constptr->name)+1; - } /* if */ - } /* for */ - - /* pad the header to sc_dataalign - * => thereby the code segment is aligned - * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned - * => and thereby the stack top is aligned too - */ - assert(sc_dataalign!=0); - padding= (int)(sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign); - if (padding==sc_dataalign) - padding=0; - - /* write the abstract machine header */ - memset(&hdr, 0, sizeof hdr); - hdr.magic=(unsigned short)AMX_MAGIC; - hdr.file_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MAX_FILE_VER_JIT : CUR_FILE_VERSION); - hdr.amx_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MIN_AMX_VER_JIT : MIN_AMX_VERSION); - hdr.flags=(short)(sc_debug & sSYMBOLIC); - if (sc_compress) - hdr.flags|=AMX_FLAG_COMPACT; - if (sc_debug==0) - hdr.flags|=AMX_FLAG_NOCHECKS; - if (pc_memflags & suSLEEP_INSTR) - hdr.flags|=AMX_FLAG_SLEEP; - hdr.defsize=sizeof(AMX_FUNCSTUBNT); - hdr.publics=sizeof hdr; /* public table starts right after the header */ - hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); - hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); - hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); - hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); - hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); - hdr.cod=hdr.nametable + nametablesize + padding; - hdr.dat=hdr.cod + code_idx; - hdr.hea=hdr.dat + glb_declared*sizeof(cell); - hdr.stp=hdr.hea + pc_stksize*sizeof(cell); - hdr.cip=mainaddr; - hdr.size=hdr.hea; /* preset, this is incorrect in case of compressed output */ - pc_writebin(fout,&hdr,sizeof hdr); - - /* dump zeros up to the rest of the header, so that we can easily "seek" */ - for (nameofs=sizeof hdr; nameofsnext) { - if (sym->ident==iFUNCTN - && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) - { - assert(sym->vclass==sGLOBAL); - func.address=sym->addr; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,sym->name,strlen(sym->name)+1); - nameofs+=strlen(sym->name)+1; - count++; - } /* if */ - } /* for */ - - /* write the natives table */ - /* The native functions must be written in sorted order. (They are - * sorted on their "id", not on their name). A nested loop to find - * each successive function would be an O(n^2) operation. But we - * do not really need to sort, because the native function id's - * are sequential and there are no duplicates. So we first walk - * through the complete symbol list and store a pointer to every - * native function of interest in a temporary table, where its id - * serves as the index in the table. Now we can walk the table and - * have all native functions in sorted order. - */ - if (numnatives>0) { - nativelist=(symbol **)malloc(numnatives*sizeof(symbol *)); - if (nativelist==NULL) - error(103); /* insufficient memory */ - #if !defined NDEBUG - memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ - #endif - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if (sym->ident==iFUNCTN && (sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { - assert(sym->addr < numnatives); - nativelist[(int)sym->addr]=sym; - } /* if */ - } /* for */ - count=0; - for (i=0; iname)) { - assert(strlen(sym->name)<=sNAMEMAX); - strcpy(alias,sym->name); - } /* if */ - assert(sym->vclass==sGLOBAL); - func.address=0; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,alias,strlen(alias)+1); - nameofs+=strlen(alias)+1; - count++; - } /* for */ - free(nativelist); - } /* if */ - - /* write the libraries table */ - if (pc_addlibtable) { - count=0; - for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { - if (constptr->value>0) { - assert(strlen(constptr->name)>0); - func.address=0; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - nameofs+=strlen(constptr->name)+1; - count++; - } /* if */ - } /* for */ - } /* if */ - - /* write the public variables table */ - count=0; - for (sym=glbtab.next; sym!=NULL; sym=sym->next) { - if (sym->ident==iVARIABLE && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { - assert((sym->usage & uDEFINE)!=0); - assert(sym->vclass==sGLOBAL); - func.address=sym->addr; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,sym->name,strlen(sym->name)+1); - nameofs+=strlen(sym->name)+1; - count++; - } /* if */ - } /* for */ - - /* write the public tagnames table */ - count=0; - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - if ((constptr->value & PUBLICTAG)!=0) { - assert(strlen(constptr->name)>0); - func.address=constptr->value & TAGMASK; - func.nameofs=nameofs; - #if BYTE_ORDER==BIG_ENDIAN - align32(&func.address); - align32(&func.nameofs); - #endif - pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); - pc_writebin(fout,&func,sizeof func); - pc_resetbin(fout,nameofs); - pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - nameofs+=strlen(constptr->name)+1; - count++; - } /* if */ - } /* for */ - - /* write the "maximum name length" field in the name table */ - assert(nameofs==hdr.nametable+nametablesize); - pc_resetbin(fout,hdr.nametable); - count=sNAMEMAX; - #if BYTE_ORDER==BIG_ENDIAN - align16(&count); - #endif - pc_writebin(fout,&count,sizeof count); - pc_resetbin(fout,hdr.cod); - - /* First pass: relocate all labels */ - /* This pass is necessary because the code addresses of labels is only known - * after the peephole optimization flag. Labels can occur inside expressions - * (e.g. the conditional operator), which are optimized. - */ - lbltab=NULL; - if (sc_labnum>0) { - /* only very short programs have zero labels; no first pass is needed - * if there are no labels */ - lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); - if (lbltab==NULL) - error(103); /* insufficient memory */ - codeindex=0; - pc_resetasm(fin); - while (pc_readasm(fin,line,sizeof line)!=NULL) { - stripcomment(line); - instr=skipwhitespace(line); - /* ignore empty lines */ - if (*instr=='\0') - continue; - if (tolower(*instr)=='l' && *(instr+1)=='.') { - int lindex=(int)hex2long(instr+2,NULL); - assert(lindex>=0 && lindexinstr); - i=findopcode(instr,(int)(params-instr)); - if (opcodelist[i].name==NULL) { - *params='\0'; - error(104,instr); /* invalid assembler instruction */ - } /* if */ - if (opcodelist[i].segment==sIN_CSEG) - codeindex+=opcodelist[i].func(NULL,skipwhitespace(params),opcodelist[i].opcode); - } /* if */ - } /* while */ - } /* if */ - - /* Second pass (actually 2 more passes, one for all code and one for all data) */ - bytes_in=0; - bytes_out=0; - for (pass=sIN_CSEG; pass<=sIN_DSEG; pass++) { - pc_resetasm(fin); - while (pc_readasm(fin,line,sizeof line)!=NULL) { - stripcomment(line); - instr=skipwhitespace(line); - /* ignore empty lines and labels (labels have a special syntax, so these - * must be parsed separately) */ - if (*instr=='\0' || tolower(*instr)=='l' && *(instr+1)=='.') - continue; - /* get to the end of the instruction (make use of the '\n' that fgets() - * added at the end of the line; this way we will *always* drop on a - * whitespace character) */ - for (params=instr; *params!='\0' && !isspace(*params); params++) - /* nothing */; - assert(params>instr); - i=findopcode(instr,(int)(params-instr)); - assert(opcodelist[i].name!=NULL); - if (opcodelist[i].segment==pass) - opcodelist[i].func(fout,skipwhitespace(params),opcodelist[i].opcode); - } /* while */ - } /* for */ - if (bytes_out-bytes_in>0) - error(106); /* compression buffer overflow */ - - if (lbltab!=NULL) { - free(lbltab); - #if !defined NDEBUG - lbltab=NULL; - #endif - } /* if */ - - if (sc_compress) - hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ - if (!writeerror && (sc_debug & sSYMBOLIC)!=0) - append_dbginfo(fout); /* optionally append debug file */ - - if (writeerror) - error(101,"disk full"); - - /* adjust the header */ - size=(int)hdr.cod; /* save, the value in the header may be swapped */ - #if BYTE_ORDER==BIG_ENDIAN - align32(&hdr.size); - align16(&hdr.magic); - align16(&hdr.flags); - align16(&hdr.defsize); - align32(&hdr.publics); - align32(&hdr.natives); - align32(&hdr.libraries); - align32(&hdr.pubvars); - align32(&hdr.tags); - align32(&hdr.nametable); - align32(&hdr.cod); - align32(&hdr.dat); - align32(&hdr.hea); - align32(&hdr.stp); - align32(&hdr.cip); - #endif - pc_resetbin(fout,0); - pc_writebin(fout,&hdr,sizeof hdr); - - /* return the size of the header (including name tables, but excluding code - * or data sections) - */ - return size; -} - -static void append_dbginfo(FILE *fout) -{ - AMX_DBG_HDR dbghdr; - AMX_DBG_LINE dbgline; - AMX_DBG_SYMBOL dbgsym; - AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; - int index,dim,dbgsymdim; - char *str,*prevstr,*name,*prevname; - ucell codeidx,previdx; - constvalue *constptr; - char symname[2*sNAMEMAX+16]; - int16_t id1,id2; - ucell address; - - /* header with general information */ - memset(&dbghdr, 0, sizeof dbghdr); - dbghdr.size=sizeof dbghdr; - dbghdr.magic=AMX_DBG_MAGIC; - dbghdr.file_version=CUR_FILE_VERSION; - dbghdr.amx_version=MIN_AMX_VERSION; - - /* first pass: collect the number of items in various tables */ - - /* file table */ - previdx=0; - prevstr=NULL; - prevname=NULL; - for (index=0; (str=get_dbgstring(index))!=NULL; index++) { - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='F') { - codeidx=hex2long(str+2,&name); - if (codeidx!=previdx) { - if (prevstr!=NULL) { - assert(prevname!=NULL); - dbghdr.files++; - dbghdr.size+=sizeof(cell)+strlen(prevname)+1; - } /* if */ - previdx=codeidx; - } /* if */ - prevstr=str; - prevname=skipwhitespace(name); - } /* if */ - } /* for */ - if (prevstr!=NULL) { - assert(prevname!=NULL); - dbghdr.files++; - dbghdr.size+=sizeof(cell)+strlen(prevname)+1; - } /* if */ - - /* line number table */ - for (index=0; (str=get_dbgstring(index))!=NULL; index++) { - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='L') { - dbghdr.lines++; - dbghdr.size+=sizeof(AMX_DBG_LINE); - } /* if */ - } /* for */ - - /* symbol table */ - for (index=0; (str=get_dbgstring(index))!=NULL; index++) { - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='S') { - dbghdr.symbols++; - name=strchr(str+2,':'); - assert(name!=NULL); - dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); - if ((prevstr=strchr(name,'['))!=NULL) - while ((prevstr=strchr(prevstr+1,':'))!=NULL) - dbghdr.size+=sizeof(AMX_DBG_SYMDIM); - } /* if */ - } /* for */ - - /* tag table */ - for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - dbghdr.tags++; - dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); - } /* for */ - - /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); - dbghdr.automatons++; - dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); - } /* for */ - - /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - dbghdr.states++; - dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); - } /* for */ - - - /* pass 2: generate the tables */ - #if BYTE_ORDER==BIG_ENDIAN - align32((uint32_t*)&dbghdr.size); - align16(&dbghdr.magic); - align16(&dbghdr.flags); - align16(&dbghdr.files); - align16(&dbghdr.lines); - align16(&dbghdr.symbols); - align16(&dbghdr.tags); - align16(&dbghdr.automatons); - align16(&dbghdr.states); - #endif - writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); - - /* file table */ - previdx=0; - prevstr=NULL; - prevname=NULL; - for (index=0; (str=get_dbgstring(index))!=NULL; index++) { - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='F') { - codeidx=hex2long(str+2,&name); - if (codeidx!=previdx) { - if (prevstr!=NULL) { - assert(prevname!=NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&previdx); - #endif - writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); - writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); - } /* if */ - previdx=codeidx; - } /* if */ - prevstr=str; - prevname=skipwhitespace(name); - } /* if */ - } /* for */ - if (prevstr!=NULL) { - assert(prevname!=NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&previdx); - #endif - writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); - writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); - } /* if */ - - /* line number table */ - for (index=0; (str=get_dbgstring(index))!=NULL; index++) { - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='L') { - dbgline.address=hex2long(str+2,&str); - dbgline.line=(int32_t)hex2long(str,NULL); - #if BYTE_ORDER==BIG_ENDIAN - aligncell(&dbgline.address); - align32(&dbgline.line); - #endif - writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); - } /* if */ - } /* for */ - - /* symbol table */ - for (index=0; (str=get_dbgstring(index))!=NULL; index++) { - assert(str!=NULL); - assert(str[0]!='\0' && str[1]==':'); - if (str[0]=='S') { - dbgsym.address=hex2long(str+2,&str); - dbgsym.tag=(int16_t)hex2long(str,&str); - str=skipwhitespace(str); - assert(*str==':'); - name=skipwhitespace(str+1); - str=strchr(name,' '); - assert(str!=NULL); - assert((int)(str-name)next) { - assert(strlen(constptr->name)>0); - id1=(int16_t)(constptr->value & TAGMASK); - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - #endif - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - /* automaton table */ - for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); - id1=(int16_t)constptr->index; - address=(ucell)constptr->value; - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - aligncell(&address); - #endif - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,&address,sizeof address); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - /* state table */ - for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { - assert(strlen(constptr->name)>0); - id1=(int16_t)constptr->value; - id2=(int16_t)constptr->index; - address=(ucell)constptr->value; - #if BYTE_ORDER==BIG_ENDIAN - align16(&id1); - align16(&id2); - #endif - writeerror |= !pc_writebin(fout,&id1,sizeof id1); - writeerror |= !pc_writebin(fout,&id2,sizeof id2); - writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); - } /* for */ - - delete_dbgstringtable(); -} diff --git a/compiler-init/compiler-init/sc7.c b/compiler-init/compiler-init/sc7.c deleted file mode 100644 index f5a52d5a..00000000 --- a/compiler-init/compiler-init/sc7.c +++ /dev/null @@ -1,703 +0,0 @@ -/* Pawn compiler - Staging buffer and optimizer - * - * The staging buffer - * ------------------ - * The staging buffer allows buffered output of generated code, deletion - * of redundant code, optimization by a tinkering process and reversing - * the ouput of evaluated expressions (which is used for the reversed - * evaluation of arguments in functions). - * Initially, stgwrite() writes to the file directly, but after a call to - * stgset(TRUE), output is redirected to the buffer. After a call to - * stgset(FALSE), stgwrite()'s output is directed to the file again. Thus - * only one routine is used for writing to the output, which can be - * buffered output or direct output. - * - * staging buffer variables: stgbuf - the buffer - * stgidx - current index in the staging buffer - * staging - if true, write to the staging buffer; - * if false, write to file directly. - * - * The peephole optimizer uses a dual "pipeline". The staging buffer (described - * above) gets optimized for each expression or sub-expression in a function - * call. The peephole optimizer is recursive, but it does not span multiple - * sub-expressions. However, the data gets written to a second buffer that - * behaves much like the staging buffer. This second buffer gathers all - * optimized strings from the staging buffer for a complete expression. The - * peephole optmizer then runs over this second buffer to find optimzations - * across function parameter boundaries. - * - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sc7.c 3579 2006-06-06 13:35:29Z thiadmer $ - */ -#include -#include -#include /* for atoi() */ -#include -#include -#if defined FORTIFY - #include -#endif -#include "sc.h" - -#if defined _MSC_VER - #pragma warning(push) - #pragma warning(disable:4125) /* decimal digit terminates octal escape sequence */ -#endif - -#include "sc7.scp" - -#if defined _MSC_VER - #pragma warning(pop) -#endif - -static int stgstring(char *start,char *end); -static void stgopt(char *start,char *end,int (*outputfunc)(char *str)); - - -#define sSTG_GROW 512 -#define sSTG_MAX 20480 - -static char *stgbuf=NULL; -static int stgmax=0; /* current size of the staging buffer */ - -static char *stgpipe=NULL; -static int pipemax=0; /* current size of the stage pipe, a second staging buffer */ -static int pipeidx=0; - -#define CHECK_STGBUFFER(index) if ((int)(index)>=stgmax) grow_stgbuffer(&stgbuf, stgmax, (index)+1) -#define CHECK_STGPIPE(index) if ((int)(index)>=pipemax) grow_stgbuffer(&stgpipe, pipemax, (index)+1) - -static void grow_stgbuffer(char **buffer, int curmax, int requiredsize) -{ - char *p; - int clear= (*buffer==NULL); /* if previously none, empty buffer explicitly */ - - assert(curmaxsSTG_MAX) - error(102,"staging buffer"); /* staging buffer overflow (fatal error) */ - curmax=requiredsize+sSTG_GROW; - if (*buffer!=NULL) - p=(char *)realloc(*buffer,curmax*sizeof(char)); - else - p=(char *)malloc(curmax*sizeof(char)); - if (p==NULL) - error(102,"staging buffer"); /* staging buffer overflow (fatal error) */ - *buffer=p; - if (clear) - **buffer='\0'; -} - -SC_FUNC void stgbuffer_cleanup(void) -{ - if (stgbuf!=NULL) { - free(stgbuf); - stgbuf=NULL; - stgmax=0; - } /* if */ - if (stgpipe!=NULL) { - free(stgpipe); - stgpipe=NULL; - pipemax=0; - pipeidx=0; - } /* if */ -} - -/* the variables "stgidx" and "staging" are declared in "scvars.c" */ - -/* stgmark - * - * Copies a mark into the staging buffer. At this moment there are three - * possible marks: - * sSTARTREORDER identifies the beginning of a series of expression - * strings that must be written to the output file in - * reordered order - * sENDREORDER identifies the end of 'reverse evaluation' - * sEXPRSTART + idx only valid within a block that is evaluated in - * reordered order, it identifies the start of an - * expression; the "idx" value is the argument position - * - * Global references: stgidx (altered) - * stgbuf (altered) - * staging (referred to only) - */ -SC_FUNC void stgmark(char mark) -{ - if (staging) { - CHECK_STGBUFFER(stgidx); - stgbuf[stgidx++]=mark; - } /* if */ -} - -static int rebuffer(char *str) -{ - if (sc_status==statWRITE) { - if (pipeidx>=2 && stgpipe[pipeidx-1]=='\0' && stgpipe[pipeidx-2]!='\n') - pipeidx-=1; /* overwrite last '\0' */ - while (*str!='\0') { /* copy to staging buffer */ - CHECK_STGPIPE(pipeidx); - stgpipe[pipeidx++]=*str++; - } /* while */ - CHECK_STGPIPE(pipeidx); - stgpipe[pipeidx++]='\0'; - } /* if */ - return TRUE; -} - -static int filewrite(char *str) -{ - if (sc_status==statWRITE) - return pc_writeasm(outf,str); - return TRUE; -} - -/* stgwrite - * - * Writes the string "st" to the staging buffer or to the output file. In the - * case of writing to the staging buffer, the terminating byte of zero is - * copied too, but... the optimizer can only work on complete lines (not on - * fractions of it. Therefore if the string is staged, if the last character - * written to the buffer is a '\0' and the previous-to-last is not a '\n', - * the string is concatenated to the last string in the buffer (the '\0' is - * overwritten). This also means an '\n' used in the middle of a string isn't - * recognized and could give wrong results with the optimizer. - * Even when writing to the output file directly, all strings are buffered - * until a whole line is complete. - * - * Global references: stgidx (altered) - * stgbuf (altered) - * staging (referred to only) - */ -SC_FUNC void stgwrite(const char *st) -{ - int len; - - if (staging) { - assert(stgidx==0 || stgbuf!=NULL); /* staging buffer must be valid if there is (apparently) something in it */ - if (stgidx>=2 && stgbuf[stgidx-1]=='\0' && stgbuf[stgidx-2]!='\n') - stgidx-=1; /* overwrite last '\0' */ - while (*st!='\0') { /* copy to staging buffer */ - CHECK_STGBUFFER(stgidx); - stgbuf[stgidx++]=*st++; - } /* while */ - CHECK_STGBUFFER(stgidx); - stgbuf[stgidx++]='\0'; - } else { - len=(stgbuf!=NULL) ? strlen(stgbuf) : 0; - CHECK_STGBUFFER(len+strlen(st)+1); - strcat(stgbuf,st); - len=strlen(stgbuf); - if (len>0 && stgbuf[len-1]=='\n') { - filewrite(stgbuf); - stgbuf[0]='\0'; - } /* if */ - } /* if */ -} - -/* stgout - * - * Writes the staging buffer to the output file via stgstring() (for - * reversing expressions in the buffer) and stgopt() (for optimizing). It - * resets "stgidx". - * - * Global references: stgidx (altered) - * stgbuf (referred to only) - * staging (referred to only) - */ -SC_FUNC void stgout(int index) -{ - int reordered=0; - int idx; - - if (!staging) - return; - assert(pipeidx==0); - - /* first pass: sub-expressions */ - if (sc_status==statWRITE) - reordered=stgstring(&stgbuf[index],&stgbuf[stgidx]); - stgidx=index; - - /* second pass: optimize the buffer created in the first pass */ - if (sc_status==statWRITE) { - if (reordered) { - stgopt(stgpipe,stgpipe+pipeidx,filewrite); - } else { - /* there is no sense in re-optimizing if the order of the sub-expressions - * did not change; so output directly - */ - for (idx=0; idx=0) - stack[arg].end=start-1; /* finish previous argument */ - arg=(unsigned char)*start - sEXPRSTART; - stack[arg].start=start+1; - if (arg>=argc) - argc=arg+1; - } /* if */ - start++; - } else { - start+=strlen(start)+1; - } /* if */ - } /* switch */ - } while (nest); /* enddo */ - if (arg>=0) - stack[arg].end=start-1; /* finish previous argument */ - while (argc>0) { - argc--; - stgstring(stack[argc].start,stack[argc].end); - } /* while */ - free(stack); - } else { - ptr=start; - while (ptr0) - filewrite(stgbuf); - } /* if */ - stgbuf[0]='\0'; -} - -/* phopt_init - * Initialize all sequence strings of the peehole optimizer. The strings - * are embedded in the .EXE file in compressed format, here we expand - * them (and allocate memory for the sequences). - */ -static SEQUENCE *sequences; - -SC_FUNC int phopt_init(void) -{ - int number, i, len; - char str[160]; - - /* count number of sequences */ - for (number=0; sequences_cmp[number].find!=NULL; number++) - /* nothing */; - number++; /* include an item for the NULL terminator */ - - if ((sequences=(SEQUENCE*)malloc(number * sizeof(SEQUENCE)))==NULL) - return FALSE; - - /* pre-initialize all to NULL (in case of failure) */ - for (i=0; i (PAWN_CELL_SIZE/4) * MAX_OPT_CAT - #define MAX_ALIAS sNAMEMAX -#else - #define MAX_ALIAS (PAWN_CELL_SIZE/4) * MAX_OPT_CAT -#endif - -static int matchsequence(char *start,char *end,char *pattern, - char symbols[MAX_OPT_VARS][MAX_ALIAS+1], - int *match_length) -{ - int var,i; - char str[MAX_ALIAS+1]; - char *start_org=start; - cell value; - char *ptr; - - *match_length=0; - for (var=0; var=end) - return FALSE; - switch (*pattern) { - case '%': /* new "symbol" */ - pattern++; - assert(isdigit(*pattern)); - var=atoi(pattern) - 1; - assert(var>=0 && var=0 && var=0 && var0) { /* delete a section */ - memmove(dest,dest+offset,dest_length-offset); - memset(dest+dest_length-offset,0xcc,offset); /* not needed, but for cleanlyness */ - } else if (offset<0) { /* insert a section */ - memmove(dest-offset, dest, dest_length); - } /* if */ - memcpy(dest, replace, repl_length); -} - -/* stgopt - * - * Optimizes the staging buffer by checking for series of instructions that - * can be coded more compact. The routine expects the lines in the staging - * buffer to be separated with '\n' and '\0' characters. - * - * The longest sequences should probably be checked first. - */ - -static void stgopt(char *start,char *end,int (*outputfunc)(char *str)) -{ - char symbols[MAX_OPT_VARS][MAX_ALIAS+1]; - int seq,match_length,repl_length; - int matches; - char *debut=start; /* save original start of the buffer */ - - assert(sequences!=NULL); - /* do not match anything if debug-level is maximum */ - if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) { - do { - matches=0; - start=debut; - while (start=0); - if (*sequences[seq].find=='\0') { - if (pc_optimize==sOPTIMIZE_NOMACRO) { - break; /* don't look further */ - } else { - seq++; /* continue with next string */ - continue; - } /* if */ - } /* if */ - if (matchsequence(start,end,sequences[seq].find,symbols,&match_length)) { - char *replace=replacesequence(sequences[seq].replace,symbols,&repl_length); - /* If the replacement is bigger than the original section, we may need - * to "grow" the staging buffer. This is quite complex, due to the - * re-ordering of expressions that can also happen in the staging - * buffer. In addition, it should not happen: the peephole optimizer - * must replace sequences with *shorter* sequences, not longer ones. - * So, I simply forbid sequences that are longer than the ones they - * are meant to replace. - */ - assert(match_length>=repl_length); - if (match_length>=repl_length) { - strreplace(start,replace,match_length,repl_length,(int)(end-start)); - end-=match_length-repl_length; - free(replace); - code_idx-=sequences[seq].savesize; - seq=0; /* restart search for matches */ - matches++; - } else { - /* actually, we should never get here (match_length0); - } /* if (pc_optimize>sOPTIMIZE_NONE && sc_status==statWRITE) */ - - for (start=debut; start ;$lcl - * stack -4 push.c - * const.pri ;$exp - * stor.s.pri - - * ;$exp - - */ - { - #ifdef SCPACK - ";$lcl %1 %2!stack -4!const.pri %3!stor.s.pri %2!;$exp!", - ";$lcl %1 %2!push.c %3!;$exp!", - #else - "\224lcl\332\227ack -4!\233\206\256\227or\222\230\252", - "\224lcl\332\331\256\252", - #endif - seqsize(3,3) - seqsize(1,1) - }, - { - #ifdef SCPACK - ";$lcl %1 %2!stack -4!zero.pri!stor.s.pri %2!;$exp!", - ";$lcl %1 %2!push.c 0!;$exp!", - #else - "\224lcl\332\227ack -4!\373\227or\222\230\252", - "\224lcl\332\331 0!\252", - #endif - seqsize(3,2) - seqsize(1,1) - }, - /* During a calculation, the intermediate result must sometimes - * be moved from PRI to ALT, like in: - * push.pri move.alt - * load.s.pri n1 load.s.pri n1 - * pop.alt - - * - * The above also accurs for "load.pri" and for "const.pri", - * so add another two cases. - */ - { - #ifdef SCPACK - "push.pri!load.s.pri %1!pop.alt!", - "move.alt!load.s.pri %1!", - #else - "\244\333\241", - "\306\333", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "push.pri!load.pri %1!pop.alt!", - "move.alt!load.pri %1!", - #else - "\244\311\241", - "\306\311", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "push.pri!const.pri %1!pop.alt!", - "move.alt!const.pri %1!", - #else - "\244\330\241", - "\306\330", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "push.pri!zero.pri!pop.alt!", - "move.alt!zero.pri!", - #else - "\244\373\241", - "\306\373", - #endif - seqsize(3,0) - seqsize(2,0) - }, - /* saving PRI and then loading from its address - * occurs when indexing a multi-dimensional array - */ - { - #ifdef SCPACK - "push.pri!load.i!pop.alt!", - "move.alt!load.i!", - #else - "\244\342i\266", - "\306\342\355", - #endif - seqsize(3,0) - seqsize(2,0) - }, - /* An even simpler PUSH/POP optimization (occurs in - * switch statements): - * push.pri move.alt - * pop.alt - - */ - { - #ifdef SCPACK - "push.pri!pop.alt!", - "move.alt!", - #else - "\244\241", - "\306", - #endif - seqsize(2,0) - seqsize(1,0) - }, - /* Some simple arithmetic sequences - */ - { - #ifdef SCPACK - "move.alt!load.s.pri %1!add!", - "load.s.alt %1!add!", - #else - "\306\333\271", - "\375\271", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "move.alt!load.pri %1!add!", - "load.alt %1!add!", - #else - "\306\311\271", - "\374\271", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "move.alt!const.pri %1!add!", - "const.alt %1!add!", - #else - "\306\330\271", - "\356\271", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "move.alt!load.s.pri %1!sub.alt!", - "load.s.alt %1!sub!", - #else - "\306\333sub\223", - "\375sub!", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "move.alt!load.pri %1!sub.alt!", - "load.alt %1!sub!", - #else - "\306\311sub\223", - "\374sub!", - #endif - seqsize(3,1) - seqsize(2,1) - }, - { - #ifdef SCPACK - "move.alt!const.pri %1!sub.alt!", - "const.alt %1!sub!", - #else - "\306\330sub\223", - "\356sub!", - #endif - seqsize(3,1) - seqsize(2,1) - }, - /* User-defined operators first load the operands into registers and - * then have them pushed onto the stack. This can give rise to sequences - * like: - * const.pri n1 push.c n1 - * const.alt n2 push.c n2 - * push.pri - - * push.alt - - * A similar sequence occurs with the two PUSH.pri/alt instructions inverted. - * The first, second, or both CONST.pri/alt instructions can also be - * LOAD.pri/alt. - * This gives 2 x 4 cases. - */ - { - #ifdef SCPACK - "const.pri %1!const.alt %2!push.pri!push.alt!", - "push.c %1!push.c %2!", - #else - "\330\233\304\244\354", - "\331\202\331\221", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "const.pri %1!const.alt %2!push.alt!push.pri!", - "push.c %2!push.c %1!", - #else - "\330\233\304\354\244", - "\331\221\331\202", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "const.pri %1!load.alt %2!push.pri!push.alt!", - "push.c %1!push %2!", - #else - "\330\362\244\354", - "\331\202\216\221", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "const.pri %1!load.alt %2!push.alt!push.pri!", - "push %2!push.c %1!", - #else - "\330\362\354\244", - "\216\221\331\202", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "load.pri %1!const.alt %2!push.pri!push.alt!", - "push %1!push.c %2!", - #else - "\311\233\304\244\354", - "\216\202\331\221", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "load.pri %1!const.alt %2!push.alt!push.pri!", - "push.c %2!push %1!", - #else - "\311\233\304\354\244", - "\331\221\216\202", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "load.pri %1!load.alt %2!push.pri!push.alt!", - "push %1!push %2!", - #else - "\311\362\244\354", - "\216\202\216\221", - #endif - seqsize(4,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "load.pri %1!load.alt %2!push.alt!push.pri!", - "push %2!push %1!", - #else - "\311\362\354\244", - "\216\221\216\202", - #endif - seqsize(4,2) - seqsize(2,2) - }, - /* Function calls (parameters are passed on the stack) - * load.s.pri n1 push.s n1 - * push.pri - - * -------------------------------------- - * load.pri n1 push n1 - * push.pri - - * -------------------------------------- - * const.pri n1 push.c n1 - * push.pri - - * -------------------------------------- - * zero.pri push.c 0 - * push.pri - - * -------------------------------------- - * addr.pri n1 push.adr n1 - * push.pri - - * - * However, PRI must not be needed after this instruction - * if this shortcut is used. Check for the ;$par comment. - */ - { - #ifdef SCPACK - "load.s.pri %1!push.pri!;$par!", - "push.s %1!;$par!", - #else - "\226\264\255", - "\216\222\202\255", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "load.pri %1!push.pri!;$par!", - "push %1!;$par!", - #else - "\217\264\255", - "\216\202\255", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "const.pri %1!push.pri!;$par!", - "push.c %1!;$par!", - #else - "\233\264\255", - "\331\202\255", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "zero.pri!push.pri!;$par!", - "push.c 0!;$par!", - #else - "\373\244\255", - "\331 0!\255", - #endif - seqsize(2,0) - seqsize(1,1) - }, - { - #ifdef SCPACK - "addr.pri %1!push.pri!;$par!", - "push.adr %1!;$par!", - #else - "\265\264\255", - "\216\326\202\255", - #endif - seqsize(2,1) - seqsize(1,1) - }, - /* References with a default value generate new cells on the heap - * dynamically. That code often ends with: - * move.pri push.alt - * push.pri - - */ - { - #ifdef SCPACK - "move.pri!push.pri!", - "push.alt!", - #else - "\300\236\244", - "\354", - #endif - seqsize(2,0) - seqsize(1,0) - }, - /* Simple arithmetic operations on constants. Noteworthy is the - * subtraction of a constant, since it is converted to the addition - * of the inverse value. - * const.alt n1 add.c n1 - * add - - * -------------------------------------- - * const.alt n1 add.c -n1 - * sub - - * -------------------------------------- - * const.alt n1 smul.c n1 - * smul - - * -------------------------------------- - * const.alt n1 eq.c.pri n1 - * eq - - */ - { - #ifdef SCPACK - "const.alt %1!add!", - "add.c %1!", - #else - "\356\271", - "\235\242\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "const.alt %1!sub!", - "add.c -%1!", - #else - "\356sub!", - "\235\242 -\201", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "const.alt %1!smul!", - "smul.c %1!", - #else - "\356smul!", - "smu\307\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "const.alt %1!eq!", - "eq.c.pri %1!", - #else - "\356\325", - "\262\242\225", - #endif - seqsize(2,1) - seqsize(1,1) - }, - /* Some operations use the alternative subtraction operation --these - * can also be optimized. - * const.pri n1 load.s.pri n2 - * load.s.alt n2 add.c -n1 - * sub.alt - - * -------------------------------------- - * const.pri n1 load.pri n2 - * load.alt n2 add.c -n1 - * sub.alt - - */ - { - #ifdef SCPACK - "const.pri %1!load.s.alt %2!sub.alt!", - "load.s.pri %2!add.c -%1!", - #else - "\330\226\304sub\223", - "\246\235\242 -\201", - #endif - seqsize(3,2) - seqsize(2,2) - }, - { - #ifdef SCPACK - "const.pri %1!load.alt %2!sub.alt!", - "load.pri %2!add.c -%1!", - #else - "\330\362sub\223", - "\334\235\242 -\201", - #endif - seqsize(3,2) - seqsize(2,2) - }, - /* With arrays indexed with constants that come from enumerations, it happens - * multiple add.c opcodes follow in sequence. - * add.c n1 add.c n1+n2 - * add.c n2 - - */ - { - #ifdef SCPACK - "add.c %1!add.c %2!", - "add.c %1+%2!", - #else - "\235\242\202\235\242\221", - "\235\242\267+%2!", - #endif - seqsize(2,2) - seqsize(1,1) - }, - /* Compare and jump - * eq jneq n1 - * jzer n1 - - * -------------------------------------- - * eq jeq n1 - * jnz n1 - - * -------------------------------------- - * neq jeq n1 - * jzer n1 - - * -------------------------------------- - * neq jneq n1 - * jnz n1 - - * Compares followed by jzer occur much more - * often than compares followed with jnz. So we - * take the easy route here. - * less jgeq n1 - * jzer n1 - - * -------------------------------------- - * leq jgrtr n1 - * jzer n1 - - * -------------------------------------- - * grtr jleq n1 - * jzer n1 - - * -------------------------------------- - * geq jless n1 - * jzer n1 - - * -------------------------------------- - * sless jsgeq n1 - * jzer n1 - - * -------------------------------------- - * sleq jsgrtr n1 - * jzer n1 - - * -------------------------------------- - * sgrtr jsleq n1 - * jzer n1 - - * -------------------------------------- - * sgeq jsless n1 - * jzer n1 - - */ - { - #ifdef SCPACK - "eq!jzer %1!", - "jneq %1!", - #else - "\325\323", - "jn\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "eq!jnz %1!", - "jeq %1!", - #else - "\325jnz\202", - "j\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "neq!jzer %1!", - "jeq %1!", - #else - "n\325\323", - "j\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "neq!jnz %1!", - "jneq %1!", - #else - "n\325jnz\202", - "jn\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "less!jzer %1!", - "jgeq %1!", - #else - "l\352!\323", - "jg\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "leq!jzer %1!", - "jgrtr %1!", - #else - "l\325\323", - "jg\353r\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "grtr!jzer %1!", - "jleq %1!", - #else - "g\353\237\323", - "jl\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "geq!jzer %1!", - "jless %1!", - #else - "g\325\323", - "jl\352\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "sless!jzer %1!", - "jsgeq %1!", - #else - "\372!\323", - "j\320\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "sleq!jzer %1!", - "jsgrtr %1!", - #else - "\321\325\323", - "j\371r\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "sgrtr!jzer %1!", - "jsleq %1!", - #else - "\371\237\323", - "j\321\357", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "sgeq!jzer %1!", - "jsless %1!", - #else - "\320\325\323", - "j\372\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - /* Test for zero (common case, especially for strings) - * E.g. the test expression of: "for (i=0; str{i}!=0; ++i)" - * - * zero.alt jzer n1 - * jeq n1 - - * -------------------------------------- - * zero.alt jnz n1 - * jneq n1 - - */ - { - #ifdef SCPACK - "zero.alt!jeq %1!", - "jzer %1!", - #else - "\340\223j\357", - "\323", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "zero.alt!jneq %1!", - "jnz %1!", - #else - "\340\223jn\357", - "jnz\202", - #endif - seqsize(2,1) - seqsize(1,1) - }, - /* Incrementing and decrementing leaves a value in - * in PRI which may not be used (for example, as the - * third expression in a "for" loop). - * inc n1 inc n1 ; ++n - * load.pri n1 ;$exp - * ;$exp - - * -------------------------------------- - * load.pri n1 inc n1 ; n++, e.g. "for (n=0; n<10; n++)" - * inc n1 ;$exp - * ;$exp - - * Plus the varieties for stack relative increments - * and decrements. - */ - { - #ifdef SCPACK - "inc %1!load.pri %1!;$exp!", - "inc %1!;$exp!", - #else - "inc\202\311\252", - "inc\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "load.pri %1!inc %1!;$exp!", - "inc %1!;$exp!", - #else - "\311inc\301", - "inc\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "inc.s %1!load.s.pri %1!;$exp!", - "inc.s %1!;$exp!", - #else - "inc\222\202\333\252", - "inc\222\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "load.s.pri %1!inc.s %1!;$exp!", - "inc.s %1!;$exp!", - #else - "\333inc\222\301", - "inc\222\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "dec %1!load.pri %1!;$exp!", - "dec %1!;$exp!", - #else - "dec\202\311\252", - "dec\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "load.pri %1!dec %1!;$exp!", - "dec %1!;$exp!", - #else - "\311dec\301", - "dec\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "dec.s %1!load.s.pri %1!;$exp!", - "dec.s %1!;$exp!", - #else - "dec\222\202\333\252", - "dec\222\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "load.s.pri %1!dec.s %1!;$exp!", - "dec.s %1!;$exp!", - #else - "\333dec\222\301", - "dec\222\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - /* ??? the same (increments and decrements) for references */ - /* Loading the constant zero has a special opcode. - * When storing zero in memory, the value of PRI must not be later on. - * const.pri 0 zero n1 - * stor.pri n1 ;$exp - * ;$exp - - * -------------------------------------- - * const.pri 0 zero.s n1 - * stor.s.pri n1 ;$exp - * ;$exp - - * -------------------------------------- - * zero.pri zero n1 - * stor.pri n1 ;$exp - * ;$exp - - * -------------------------------------- - * zero.pri zero.s n1 - * stor.s.pri n1 ;$exp - * ;$exp - - * -------------------------------------- - * const.pri 0 zero.pri - * -------------------------------------- - * const.alt 0 zero.alt - * The last two alternatives save more memory than they save - * time, but anyway... - */ - { - #ifdef SCPACK - "const.pri 0!stor.pri %1!;$exp!", - "zero %1!;$exp!", - #else - "\233\206 0!\227or\225\252", - "\340\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "const.pri 0!stor.s.pri %1!;$exp!", - "zero.s %1!;$exp!", - #else - "\233\206 0!\227or\222\225\252", - "\340\222\301", - #endif - seqsize(2,2) - seqsize(1,1) - }, - { - #ifdef SCPACK - "zero.pri!stor.pri %1!;$exp!", - "zero %1!;$exp!", - #else - "\373\227or\225\252", - "\340\301", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "zero.pri!stor.s.pri %1!;$exp!", - "zero.s %1!;$exp!", - #else - "\373\227or\222\225\252", - "\340\222\301", - #endif - seqsize(2,1) - seqsize(1,1) - }, - { - #ifdef SCPACK - "const.pri 0!", - "zero.pri!", - #else - "\233\206 0!", - "\373", - #endif - seqsize(1,1) - seqsize(1,0) - }, - { - #ifdef SCPACK - "const.alt 0!", - "zero.alt!", - #else - "\233\212 0!", - "\340\223", - #endif - seqsize(1,1) - seqsize(1,0) - }, - - /* ------------------ */ - /* Macro instructions */ - /* ------------------ */ - - { "", "", 0 }, /* separator, so optimizer can stop before generating macro opcodes */ - - /* optimizing the calling of native functions (which always have a parameter - * count pushed before, and the stack pointer restored afterwards - */ - { - #ifdef SCPACK - "push.c %1!sysreq.c %2!stack %3!", //note: %3 == %1 + 4 - "sysreq.n %2 %1!", - #else - "\331\202sysr\262\242\221\227ack\256", - "sysr\262.n\220\202", - #endif - seqsize(3,3) - seqsize(1,2) - }, - /* ----- */ - /* Functions with many parameters with the same "type" have sequences like: - * push.c n1 push3.c n1 n2 n3 - * ;$par ;$par - * push.c n2 - - * ;$par - - * push.c n3 - - * ;$par - - * etc. etc. - * - * Similar sequences occur with PUSH, PUSH.s and PUSHADDR - */ - { - #ifdef SCPACK - "push.c %1!;$par!push.c %2!;$par!push.c %3!;$par!push.c %4!;$par!push.c %5!", - "push5.c %1 %2 %3 %4 %5!", - #else - "\331\336\242\345\242\256\257\242\335\257\242\2035!", - "\2165\242\343\245\302\2035!", - #endif - seqsize(5,5) - seqsize(1,5) - }, - { - #ifdef SCPACK - "push.c %1!;$par!push.c %2!;$par!push.c %3!;$par!push.c %4!", - "push4.c %1 %2 %3 %4!", - #else - "\331\336\242\345\242\256\257\242\335", - "\2164\242\343\245\335", - #endif - seqsize(4,4) - seqsize(1,4) - }, - { - #ifdef SCPACK - "push.c %1!;$par!push.c %2!;$par!push.c %3!", - "push3.c %1 %2 %3!", - #else - "\331\336\242\345\242\256", - "\2163\242\343\256", - #endif - seqsize(3,3) - seqsize(1,3) - }, - { - #ifdef SCPACK - "push.c %1!;$par!push.c %2!", - "push2.c %1 %2!", - #else - "\331\336\242\221", - "\2162\242\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - /* ----- */ - { - #ifdef SCPACK - "push %1!;$par!push %2!;$par!push %3!;$par!push %4!;$par!push %5!", - "push5 %1 %2 %3 %4 %5!", - #else - "\216\336\345\256\257\335\257\2035!", - "\2165\343\245\302\2035!", - #endif - seqsize(5,5) - seqsize(1,5) - }, - { - #ifdef SCPACK - "push %1!;$par!push %2!;$par!push %3!;$par!push %4!", - "push4 %1 %2 %3 %4!", - #else - "\216\336\345\256\257\335", - "\2164\343\245\335", - #endif - seqsize(4,4) - seqsize(1,4) - }, - { - #ifdef SCPACK - "push %1!;$par!push %2!;$par!push %3!", - "push3 %1 %2 %3!", - #else - "\216\336\345\256", - "\2163\343\256", - #endif - seqsize(3,3) - seqsize(1,3) - }, - { - #ifdef SCPACK - "push %1!;$par!push %2!", - "push2 %1 %2!", - #else - "\216\336\221", - "\2162\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - /* ----- */ - { - #ifdef SCPACK - "push.s %1!;$par!push.s %2!;$par!push.s %3!;$par!push.s %4!;$par!push.s %5!", - "push5.s %1 %2 %3 %4 %5!", - #else - "\216\222\336\222\345\222\256\257\222\335\257\222\2035!", - "\2165\222\343\245\302\2035!", - #endif - seqsize(5,5) - seqsize(1,5) - }, - { - #ifdef SCPACK - "push.s %1!;$par!push.s %2!;$par!push.s %3!;$par!push.s %4!", - "push4.s %1 %2 %3 %4!", - #else - "\216\222\336\222\345\222\256\257\222\335", - "\2164\222\343\245\335", - #endif - seqsize(4,4) - seqsize(1,4) - }, - { - #ifdef SCPACK - "push.s %1!;$par!push.s %2!;$par!push.s %3!", - "push3.s %1 %2 %3!", - #else - "\216\222\336\222\345\222\256", - "\2163\222\343\256", - #endif - seqsize(3,3) - seqsize(1,3) - }, - { - #ifdef SCPACK - "push.s %1!;$par!push.s %2!", - "push2.s %1 %2!", - #else - "\216\222\336\222\221", - "\2162\222\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - /* ----- */ - { - #ifdef SCPACK - "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!;$par!push.adr %4!;$par!push.adr %5!", - "push5.adr %1 %2 %3 %4 %5!", - #else - "\216\326\336\326\345\326\256\257\326\335\257\326\2035!", - "\2165\326\343\245\302\2035!", - #endif - seqsize(5,5) - seqsize(1,5) - }, - { - #ifdef SCPACK - "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!;$par!push.adr %4!", - "push4.adr %1 %2 %3 %4!", - #else - "\216\326\336\326\345\326\256\257\326\335", - "\2164\326\343\245\335", - #endif - seqsize(4,4) - seqsize(1,4) - }, - { - #ifdef SCPACK - "push.adr %1!;$par!push.adr %2!;$par!push.adr %3!", - "push3.adr %1 %2 %3!", - #else - "\216\326\336\326\345\326\256", - "\2163\326\343\256", - #endif - seqsize(3,3) - seqsize(1,3) - }, - { - #ifdef SCPACK - "push.adr %1!;$par!push.adr %2!", - "push2.adr %1 %2!", - #else - "\216\326\336\326\221", - "\2162\326\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - /* Loading two registers at a time - * load.pri n1 load.both n1 n2 - * load.alt n2 - - * -------------------------------------- - * load.alt n2 load.both n1 n2 - * load.pri n1 - - * -------------------------------------- - * load.s.pri n1 load.s.both n1 n2 - * load.s.alt n2 - - * -------------------------------------- - * load.s.alt n2 load.s.both n1 n2 - * load.s.pri n1 - - */ - { - #ifdef SCPACK - "load.pri %1!load.alt %2!", - "load.both %1 %2!", - #else - "\311\362", - "\342\275th\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - { - #ifdef SCPACK - "load.alt %2!load.pri %1!", - "load.both %1 %2!", - #else - "\362\311", - "\342\275th\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - { - #ifdef SCPACK - "load.s.pri %1!load.s.alt %2!", - "load.s.both %1 %2!", - #else - "\333\226\304", - "\226.\275th\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - { - #ifdef SCPACK - "load.s.alt %2!load.s.pri %1!", - "load.s.both %1 %2!", - #else - "\226\304\333", - "\226.\275th\332", - #endif - seqsize(2,2) - seqsize(1,2) - }, - /* Loading two registers and then pushing them occurs with user operators - * load.both n1 n2 push2 n1 n2 - * push.pri - - * push.alt - - * -------------------------------------- - * load.s.both n1 n2 push2.s n1 n2 - * push.pri - - * push.alt - - */ - { - #ifdef SCPACK - "load.both %1 %2!push.pri!push.alt!", - "push2 %1 %2!", - #else - "\342\275th\332\244\354", - "\2162\332", - #endif - seqsize(3,2) - seqsize(1,2) - }, - { - #ifdef SCPACK - "load.s.both %1 %2!push.pri!push.alt!", - "push2.s %1 %2!", - #else - "\226.\275th\332\244\354", - "\2162\222\332", - #endif - seqsize(3,2) - seqsize(1,2) - }, - /* Load a constant in a variable - * const.pri n1 const n2 n1 - * stor.pri n2 - - * -------------------------------------- - * const.pri n1 const.s n2 n1 - * stor.s.pri n2 - - */ - { - #ifdef SCPACK - "const.pri %1!stor.pri %2!", - "const %2 %1!", - #else - "\330\227or\230", - "\233\220\202", - #endif - seqsize(2,2) - seqsize(1,2) - }, - { - #ifdef SCPACK - "const.pri %1!stor.s.pri %2!", - "const.s %2 %1!", - #else - "\330\227or\222\230", - "\233\222\220\202", - #endif - seqsize(2,2) - seqsize(1,2) - }, - /* ----- */ - { NULL, NULL, 0 } -}; diff --git a/compiler-init/compiler-init/scexpand.c b/compiler-init/compiler-init/scexpand.c deleted file mode 100644 index 977c0121..00000000 --- a/compiler-init/compiler-init/scexpand.c +++ /dev/null @@ -1,68 +0,0 @@ -/* expand.c -- Byte Pair Encoding decompression */ -/* Copyright 1996 Philip Gage */ - -/* Byte Pair Compression appeared in the September 1997 - * issue of C/C++ Users Journal. The original source code - * may still be found at the web site of the magazine - * (www.cuj.com). - * - * The decompressor has been modified by me (Thiadmer - * Riemersma) to accept a string as input, instead of a - * complete file. - */ -#include -#include -#include "sc.h" - -#define STACKSIZE 16 - -SC_FUNC int strexpand(char *dest, unsigned char *source, int maxlen, unsigned char pairtable[128][2]) -{ - unsigned char stack[STACKSIZE]; - short c, top = 0; - int len; - - assert(maxlen > 0); - len = 1; /* already 1 byte for '\0' */ - for (;;) { - - /* Pop byte from stack or read byte from the input string */ - if (top) - c = stack[--top]; - else if ((c = *(unsigned char *)source++) == '\0') - break; - - /* Push pair on stack or output byte to the output string */ - if (c > 127) { - assert(top+2 <= STACKSIZE); - stack[top++] = pairtable[c-128][1]; - stack[top++] = pairtable[c-128][0]; - } - else { - len++; - if (maxlen > 1) { /* reserve one byte for the '\0' */ - *dest++ = (char)c; - maxlen--; - } - } - } - *dest = '\0'; - return len; /* return number of bytes decoded */ -} - -#if 0 /*for testing*/ -#include "sc5.scp" - -int main (int argc, char **argv) -{ - int i; - char str[128]; - - for (i=0; i<58; i++) { - strexpand(str, errmsg[i], sizeof str, SCPACK_TABLE); - printf("%s", str); - } /* for */ - return 0; -} -#endif - diff --git a/compiler-init/compiler-init/sci18n.c b/compiler-init/compiler-init/sci18n.c deleted file mode 100644 index 7b58e1f4..00000000 --- a/compiler-init/compiler-init/sci18n.c +++ /dev/null @@ -1,428 +0,0 @@ -/* Codepage translation to Unicode, and UTF-8 support - * - * The translation is based on codepage mapping files that are distributed - * by the Unicode consortium, see ftp://ftp.unicode.org/Public/MAPPINGS/. - * - * Character sets with a maximum of 256 codes are translated via a lookup - * table (these are Single-Byte Character Sets). Character sets like Shift-JIS - * with single-byte characters and multi-byte characters (introduced by a - * leader byte) are split into two tables: the 256-entry lookup table for - * the single-byte characters and an extended table for the multi-byte - * characters. The extended table is allocated dynamically; the lookup table - * is allocated statically, so loading SBCS tables cannot fail (if the tables - * themselves are valid, of course). - * - * Copyright (c) ITB CompuPhase, 2004-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: CODEPAGE.C,v 1.0 2004-02-18 12:13:04+01 thiadmer Exp thiadmer $ - */ -#include -#include -#include -#include -#include -#include "sc.h" - -#if !defined TRUE - #define FALSE 0 - #define TRUE 1 -#endif -#if !defined _MAX_PATH - #define _MAX_PATH 250 -#endif -#if !defined DIRSEP_CHAR - #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ - #define DIRSEP_CHAR '/' - #elif defined macintosh - #define DIRSEP_CHAR ':' - #else - #define DIRSEP_CHAR '\\' - #endif -#endif - -#if !defined ELEMENTS - #define ELEMENTS(array) (sizeof(array) / sizeof(array[0])) -#endif - -#if !defined NO_CODEPAGE - -#if !defined MAXCODEPAGE - #define MAXCODEPAGE 12 /* typically "cp" + 4 digits + ".txt" */ -#endif -#define INVALID 0xffffu /* 0xffff and 0xfffe are invalid Unicode characters */ -#define LEADBYTE 0xfffeu - -struct wordpair { - unsigned short index; - wchar_t code; -}; -static char cprootpath[_MAX_PATH] = { DIRSEP_CHAR, '\0' }; -static wchar_t bytetable[256]; -static struct wordpair *wordtable = NULL; -static unsigned wordtablesize = 0; -static unsigned wordtabletop = 0; - - -/* read in a line delimited by '\r' or '\n'; do NOT store the '\r' or '\n' into - * the string and ignore empty lines - * returns 1 for success and 0 for failure - */ -static int cp_readline(FILE *fp,char *string,size_t size) -{ - size_t count=0; - int c; - assert(size>1); - while ((c=fgetc(fp))!=EOF && count0) /* '\r' or '\n' ends a string */ - break; - /* if count==0, the line started with a '\r' or '\n', or perhaps line - * ends in the file are '\r\n' and we read and stopped on the '\r' of - * the preceding line - */ - } else { - string[count++]=(char)c; - } /* if */ - } /* while */ - string[count]='\0'; - return count>0; -} - -/* cp_path() sets the directory where all codepage files must be found (if - * the parameter to cp_set() specifies a full path, that is used instead). - * The path is specified into two parts: root and directory; the full path - * for the codepage direcory is just the concatenation of the two, with a - * directory separator in between. The directory is given in two parts, - * because often a program already retrieves its "home" directory and the - * codepages are most conveniently stored in a subdirectory of this home - * directory. - */ -SC_FUNC int cp_path(const char *root, const char *directory) -{ - size_t len1,len2; - int add_slash1,add_slash2; - - len1= (root!=NULL) ? strlen(root) : 0; - add_slash1= (len1==0 || root[len1-1]!=DIRSEP_CHAR); - len2= (directory!=NULL) ? strlen(directory) : 0; - add_slash2= (len2>0 && root[len2-1]!=DIRSEP_CHAR); - if (len1+add_slash1+len2+add_slash2>=(_MAX_PATH-MAXCODEPAGE)) - return FALSE; /* full filename may not fit */ - if (root!=NULL) - strcpy(cprootpath,root); - if (add_slash1) { - assert(len1==0 || cprootpath[len1]=='\0'); - cprootpath[len1]=DIRSEP_CHAR; - cprootpath[len1+1]='\0'; - } /* if */ - if (directory!=NULL) - strcat(cprootpath,directory); - if (add_slash2) { - assert(cprootpath[len1+add_slash1+len2]=='\0'); - cprootpath[len1+add_slash1+len2]=DIRSEP_CHAR; - cprootpath[len1+add_slash1+len2+1]='\0'; - } /* if */ - cp_set(NULL); /* start with a "linear" table (no translation) */ - return TRUE; -} - -/* cp_set() loads a codepage from a file. The name parameter may be a - * filename (including a full path) or it may be a partial codepage name. - * If the name parameter is NULL, the codepage is cleared to be a "linear" - * table (no translation). - * The following files are attempted to open (where specifies the - * value of the parameter): - * - * / - * /.txt - * /cp - * /cp.txt - */ -SC_FUNC int cp_set(const char *name) -{ - char filename[_MAX_PATH]; - FILE *fp=NULL; - unsigned index; - - /* for name==NULL, set up an identity table */ - if (name==NULL || *name=='\0') { - if (wordtable!=NULL) { - free(wordtable); - wordtable=NULL; - wordtablesize=0; - wordtabletop=0; - } /* if */ - for (index=0; indexMAXCODEPAGE) - return 0; - assert(strlen(name)+strlen(cprootpath)<_MAX_PATH); - strcpy(filename,cprootpath); - strcat(filename,name); - fp=fopen(filename,"rt"); - } /* if */ - if (fp==NULL) { - /* try opening the file in the "root path" for codepages, with a ".txt" extension */ - if (strlen(name)+4>=MAXCODEPAGE) - return 0; - assert(strlen(filename)+4<_MAX_PATH); - strcat(filename,".txt"); - fp=fopen(filename,"rt"); - } /* if */ - if (fp==NULL) { - /* try opening the file in the "root path" for codepages, with "cp" prefixed before the name */ - if (strlen(name)+2>MAXCODEPAGE) - return 0; - assert(2+strlen(name)+strlen(cprootpath)<_MAX_PATH); - strcpy(filename,cprootpath); - strcat(filename,"cp"); - strcat(filename,name); - fp=fopen(filename,"rt"); - } /* if */ - if (fp==NULL) { - /* try opening the file in the "root path" for codepages, with "cp" prefixed an ".txt" appended */ - if (strlen(name)+2+4>MAXCODEPAGE) - return 0; - assert(strlen(filename)+4<_MAX_PATH); - strcat(filename,".txt"); - fp=fopen(filename,"rt"); - } /* if */ - if (fp==NULL) - return FALSE; /* all failed */ - - /* clear the tables */ - for (index=0; index0 && wordtable!=NULL); - if (wordtable!=NULL) { - free(wordtable); - wordtable=NULL; - wordtablesize=0; - wordtabletop=0; - } /* if */ - - /* read in the table */ - while (cp_readline(fp,filename,sizeof filename)) { - char *ptr; - if ((ptr=strchr(filename,'#'))!=NULL) - *ptr='\0'; /* strip of comment */ - for (ptr=filename; *ptr>0 && *ptr<' '; ptr++) - /* nothing */; /* skip leading whitespace */ - if (*ptr!='\0') { - /* content on line */ - unsigned code=LEADBYTE; - int num=sscanf(ptr,"%i %i",&index,&code); - /* if sscanf() returns 1 and the index is in range 0..255, then the - * code is a DBCS lead byte; if sscanf() returns 2 and index>=256, this - * is a double byte pair (lead byte + follower) - */ - if (num>=1 && index<256) { - bytetable[index]=(wchar_t)code; - } else if (num==2 && index>=256 && index=wordtablesize) { - /* grow the list */ - int newsize; - struct wordpair *newblock; - newsize= (wordtablesize==0) ? 128 : 2*wordtablesize; - newblock=(struct wordpair *)malloc(newsize*sizeof(*wordtable)); - if (newblock!=NULL) { - memcpy(newblock,wordtable,wordtabletop*sizeof(*wordtable)); - free(wordtable); - wordtable=newblock; - wordtablesize=newsize; - } /* if */ - } /* if */ - if (wordtabletop0 && (unsigned)wordtable[pos-1].index>index) { - wordtable[pos]=wordtable[pos-1]; - pos--; - } /* while */ - wordtable[pos].index=(unsigned short)index; - wordtable[pos].code=(wchar_t)code; - } /* if */ - } /* if */ - } /* if */ - } /* while */ - - fclose(fp); - return TRUE; -} - -SC_FUNC cell cp_translate(const unsigned char *string,const unsigned char **endptr) -{ - wchar_t result; - - result=bytetable[*string++]; - /* check whether this is a leader code */ - if ((unsigned)result==LEADBYTE && wordtable!=NULL) { - /* look up the code via binary search */ - int low,high,mid; - unsigned short index=(unsigned short)(((*(string-1)) << 8) | *string); - string++; - assert(wordtabletop>0); - low=0; - high=wordtabletop-1; - while (lowwordtable[mid].index) - low=mid+1; - else - high=mid; - } /* while */ - assert(low==high); - if (wordtable[low].index==index) - result=wordtable[low].code; - } /* if */ - - if (endptr!=NULL) - *endptr=string; - return (cell)result; -} - -#endif /* NO_CODEPAGE */ - -#if !defined NO_UTF8 -SC_FUNC cell get_utf8_char(const unsigned char *string,const unsigned char **endptr) -{ - int follow=0; - long lowmark=0; - unsigned char ch; - cell result=0; - - if (endptr!=NULL) - *endptr=string; - - for ( ;; ) { - ch=*string++; - - if (follow>0 && (ch & 0xc0)==0x80) { - /* leader code is active, combine with earlier code */ - result=(result << 6) | (ch & 0x3f); - if (--follow==0) { - /* encoding a character in more bytes than is strictly needed, - * is not really valid UTF-8; we are strict here to increase - * the chance of heuristic dectection of non-UTF-8 text - * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) - */ - if (result=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) - return -1; - } /* if */ - break; - } else if (follow==0 && (ch & 0x80)==0x80) { - /* UTF-8 leader code */ - if ((ch & 0xe0)==0xc0) { - /* 110xxxxx 10xxxxxx */ - follow=1; - lowmark=0x80L; - result=ch & 0x1f; - } else if ((ch & 0xf0)==0xe0) { - /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ - follow=2; - lowmark=0x800L; - result=ch & 0x0f; - } else if ((ch & 0xf8)==0xf0) { - /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - follow=3; - lowmark=0x10000L; - result=ch & 0x07; - } else if ((ch & 0xfc)==0xf8) { - /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - follow=4; - lowmark=0x200000L; - result=ch & 0x03; - } else if ((ch & 0xfe)==0xfc) { - /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (32 bits) */ - follow=5; - lowmark=0x4000000L; - result=ch & 0x01; - } else { - /* this is invalid UTF-8 */ - return -1; - } /* if */ - } else if (follow==0 && (ch & 0x80)==0x00) { - /* 0xxxxxxx (US-ASCII) */ - result=ch; - break; - } else { - /* this is invalid UTF-8 */ - return -1; - } /* if */ - - } /* for */ - - if (endptr!=NULL) - *endptr=string; - return result; -} -#endif - -SC_FUNC int scan_utf8(FILE *fp,const char *filename) -{ - #if defined NO_UTF8 - return 0; - #else - void *resetpos=pc_getpossrc(fp); - int utf8=TRUE; - int firstchar=TRUE,bom_found=FALSE; - const unsigned char *ptr; - - while (utf8 && pc_readsrc(fp,pline,sLINEMAX)!=NULL) { - ptr=pline; - if (firstchar) { - /* check whether the very first character on the very first line - * starts with a BYTE order mark - */ - cell c=get_utf8_char(ptr,&ptr); - bom_found= (c==0xfeff); - utf8= (c>=0); - firstchar=FALSE; - } /* if */ - while (utf8 && *ptr!='\0') - utf8= (get_utf8_char(ptr,&ptr)>=0); - } /* while */ - pc_resetsrc(fp,resetpos); - if (bom_found) { - unsigned char bom[3]; - if (!utf8) - error(77,filename); /* malformed UTF-8 encoding */ - pc_readsrc(fp,bom,3); - assert(bom[0]==0xef && bom[1]==0xbb && bom[2]==0xbf); - } /* if */ - return utf8; - #endif /* NO_UTF8 */ -} diff --git a/compiler-init/compiler-init/sclist.c b/compiler-init/compiler-init/sclist.c deleted file mode 100644 index 18c961b0..00000000 --- a/compiler-init/compiler-init/sclist.c +++ /dev/null @@ -1,486 +0,0 @@ -/* Pawn compiler - maintenance of various lists - * - * o Name list (aliases) - * o Include path list - * o Macro defintions (text substitutions) - * - * Copyright (c) ITB CompuPhase, 2001-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: sclist.c 3579 2006-06-06 13:35:29Z thiadmer $ - */ -#include -#include -#include -#include -#include "sc.h" - -#if defined FORTIFY - #include -#endif - -/* a "private" implementation of strdup(), so that porting - * to other memory allocators becomes easier. - * By Søren Hannibal. - */ -SC_FUNC char* duplicatestring(const char* sourcestring) -{ - char* result=(char*)malloc(strlen(sourcestring)+1); - strcpy(result,sourcestring); - return result; -} - - -static stringpair *insert_stringpair(stringpair *root,char *first,char *second,int matchlength) -{ - stringpair *cur,*pred; - - assert(root!=NULL); - assert(first!=NULL); - assert(second!=NULL); - /* create a new node, and check whether all is okay */ - if ((cur=(stringpair*)malloc(sizeof(stringpair)))==NULL) - return NULL; - cur->first=duplicatestring(first); - cur->second=duplicatestring(second); - cur->matchlength=matchlength; - if (cur->first==NULL || cur->second==NULL) { - if (cur->first!=NULL) - free(cur->first); - if (cur->second!=NULL) - free(cur->second); - free(cur); - return NULL; - } /* if */ - /* link the node to the tree, find the position */ - for (pred=root; pred->next!=NULL && strcmp(pred->next->first,first)<0; pred=pred->next) - /* nothing */; - cur->next=pred->next; - pred->next=cur; - return cur; -} - -static void delete_stringpairtable(stringpair *root) -{ - stringpair *cur, *next; - - assert(root!=NULL); - cur=root->next; - while (cur!=NULL) { - next=cur->next; - assert(cur->first!=NULL); - assert(cur->second!=NULL); - free(cur->first); - free(cur->second); - free(cur); - cur=next; - } /* while */ - memset(root,0,sizeof(stringpair)); -} - -static stringpair *find_stringpair(stringpair *cur,char *first,int matchlength) -{ - int result=0; - - assert(matchlength>0); /* the function cannot handle zero-length comparison */ - assert(first!=NULL); - while (cur!=NULL && result<=0) { - result=(int)*cur->first - (int)*first; - if (result==0 && matchlength==cur->matchlength) { - result=strncmp(cur->first,first,matchlength); - if (result==0) - return cur; - } /* if */ - cur=cur->next; - } /* while */ - return NULL; -} - -static int delete_stringpair(stringpair *root,stringpair *item) -{ - stringpair *cur; - - assert(root!=NULL); - cur=root; - while (cur->next!=NULL) { - if (cur->next==item) { - cur->next=item->next; /* unlink from list */ - assert(item->first!=NULL); - assert(item->second!=NULL); - free(item->first); - free(item->second); - free(item); - return TRUE; - } /* if */ - cur=cur->next; - } /* while */ - return FALSE; -} - -/* ----- string list functions ----------------------------------- */ -static stringlist *insert_string(stringlist *root,char *string) -{ - stringlist *cur; - - assert(string!=NULL); - if ((cur=(stringlist*)malloc(sizeof(stringlist)))==NULL) - error(103); /* insufficient memory (fatal error) */ - if ((cur->line=duplicatestring(string))==NULL) - error(103); /* insufficient memory (fatal error) */ - /* insert as "last" */ - assert(root!=NULL); - while (root->next!=NULL) - root=root->next; - cur->next=root->next; - root->next=cur; - return cur; -} - -static char *get_string(stringlist *root,int index) -{ - stringlist *cur; - - assert(root!=NULL); - cur=root->next; - while (cur!=NULL && index-->0) - cur=cur->next; - if (cur!=NULL) { - assert(cur->line!=NULL); - return cur->line; - } /* if */ - return NULL; -} - -static int delete_string(stringlist *root,int index) -{ - stringlist *cur,*item; - - assert(root!=NULL); - for (cur=root; cur->next!=NULL && index>0; cur=cur->next,index--) - /* nothing */; - if (cur->next!=NULL) { - item=cur->next; - cur->next=item->next; /* unlink from list */ - assert(item->line!=NULL); - free(item->line); - free(item); - return TRUE; - } /* if */ - return FALSE; -} - -SC_FUNC void delete_stringtable(stringlist *root) -{ - stringlist *cur,*next; - - assert(root!=NULL); - cur=root->next; - while (cur!=NULL) { - next=cur->next; - assert(cur->line!=NULL); - free(cur->line); - free(cur); - cur=next; - } /* while */ - memset(root,0,sizeof(stringlist)); -} - - -/* ----- alias table --------------------------------------------- */ -static stringpair alias_tab = {NULL, NULL, NULL}; /* alias table */ - -SC_FUNC stringpair *insert_alias(char *name,char *alias) -{ - stringpair *cur; - - assert(name!=NULL); - assert(strlen(name)<=sNAMEMAX); - assert(alias!=NULL); - assert(strlen(alias)<=sNAMEMAX); - if ((cur=insert_stringpair(&alias_tab,name,alias,strlen(name)))==NULL) - error(103); /* insufficient memory (fatal error) */ - return cur; -} - -SC_FUNC int lookup_alias(char *target,char *name) -{ - stringpair *cur=find_stringpair(alias_tab.next,name,strlen(name)); - if (cur!=NULL) { - assert(strlen(cur->second)<=sNAMEMAX); - strcpy(target,cur->second); - } /* if */ - return cur!=NULL; -} - -SC_FUNC void delete_aliastable(void) -{ - delete_stringpairtable(&alias_tab); -} - -/* ----- include paths list -------------------------------------- */ -static stringlist includepaths = {NULL, NULL}; /* directory list for include files */ - -SC_FUNC stringlist *insert_path(char *path) -{ - return insert_string(&includepaths,path); -} - -SC_FUNC char *get_path(int index) -{ - return get_string(&includepaths,index); -} - -SC_FUNC void delete_pathtable(void) -{ - delete_stringtable(&includepaths); - assert(includepaths.next==NULL); -} - - -/* ----- text substitution patterns ------------------------------ */ -#if !defined NO_DEFINE - -static stringpair substpair = { NULL, NULL, NULL}; /* list of substitution pairs */ - -static stringpair *substindex['z'-PUBLIC_CHAR+1]; /* quick index to first character */ -static void adjustindex(char c) -{ - stringpair *cur; - assert(c>='A' && c<='Z' || c>='a' && c<='z' || c=='_' || c==PUBLIC_CHAR); - assert(PUBLIC_CHAR<'A' && 'A'<'_' && '_'<'z'); - - for (cur=substpair.next; cur!=NULL && cur->first[0]!=c; cur=cur->next) - /* nothing */; - substindex[(int)c-PUBLIC_CHAR]=cur; -} - -SC_FUNC stringpair *insert_subst(char *pattern,char *substitution,int prefixlen) -{ - stringpair *cur; - - assert(pattern!=NULL); - assert(substitution!=NULL); - if ((cur=insert_stringpair(&substpair,pattern,substitution,prefixlen))==NULL) - error(103); /* insufficient memory (fatal error) */ - adjustindex(*pattern); - return cur; -} - -SC_FUNC stringpair *find_subst(char *name,int length) -{ - stringpair *item; - assert(name!=NULL); - assert(length>0); - assert(*name>='A' && *name<='Z' || *name>='a' && *name<='z' || *name=='_' || *name==PUBLIC_CHAR); - item=substindex[(int)*name-PUBLIC_CHAR]; - if (item!=NULL) - item=find_stringpair(item,name,length); - return item; -} - -SC_FUNC int delete_subst(char *name,int length) -{ - stringpair *item; - assert(name!=NULL); - assert(length>0); - assert(*name>='A' && *name<='Z' || *name>='a' && *name<='z' || *name=='_' || *name==PUBLIC_CHAR); - item=substindex[(int)*name-PUBLIC_CHAR]; - if (item!=NULL) - item=find_stringpair(item,name,length); - if (item==NULL) - return FALSE; - delete_stringpair(&substpair,item); - adjustindex(*name); - return TRUE; -} - -SC_FUNC void delete_substtable(void) -{ - int i; - delete_stringpairtable(&substpair); - for (i=0; i=199901L - #define __STDC_FORMAT_MACROS - #define __STDC_CONSTANT_MACROS - #include /* automatically includes stdint.h */ -#elif (defined _MSC_VER || defined __BORLANDC__) && (defined _I64_MAX || defined HAVE_I64) - #define PRId64 "I64d" - #define PRIx64 "I64x" -#else - #define PRId64 "lld" - #define PRIx64 "llx" -#endif -#if PAWN_CELL_SIZE==64 - #define PRIdC PRId64 - #define PRIxC PRIx64 -#elif PAWN_CELL_SIZE==32 - #define PRIdC "ld" - #define PRIxC "lx" -#else - #define PRIdC "d" - #define PRIxC "x" -#endif - -static stringlist dbgstrings = {NULL, NULL}; - -SC_FUNC stringlist *insert_dbgfile(const char *filename) -{ - - if (sc_status==statWRITE && (sc_debug & sSYMBOLIC)!=0) { - char string[_MAX_PATH+40]; - assert(filename!=NULL); - assert(strlen(filename)+400) - linenr--; /* line numbers are zero-based in the debug information */ - sprintf(string,"L:%" PRIxC " %x",code_idx,linenr); - return insert_string(&dbgstrings,string); - } /* if */ - return NULL; -} - -SC_FUNC stringlist *insert_dbgsymbol(symbol *sym) -{ - if (sc_status==statWRITE && (sc_debug & sSYMBOLIC)!=0) { - char string[2*sNAMEMAX+128]; - char symname[2*sNAMEMAX+16]; - - funcdisplayname(symname,sym->name); - /* address tag:name codestart codeend ident vclass [tag:dim ...] */ - if (sym->ident==iFUNCTN) { - sprintf(string,"S:%" PRIxC " %x:%s %" PRIxC " %" PRIxC " %x %x", - sym->addr,sym->tag,symname,sym->addr,sym->codeaddr,sym->ident,sym->vclass); - } else { - sprintf(string,"S:%" PRIxC " %x:%s %" PRIxC " %" PRIxC " %x %x", - sym->addr,sym->tag,symname,sym->codeaddr,code_idx,sym->ident,sym->vclass); - } /* if */ - if (sym->ident==iARRAY || sym->ident==iREFARRAY) { - #if !defined NDEBUG - int count=sym->dim.array.level; - #endif - symbol *sub; - strcat(string," [ "); - for (sub=sym; sub!=NULL; sub=finddepend(sub)) { - assert(sub->dim.array.level==count--); - sprintf(string+strlen(string),"%x:%x ",sub->x.tags.index,sub->dim.array.length); - } /* for */ - strcat(string,"]"); - } /* if */ - - return insert_string(&dbgstrings,string); - } /* if */ - return NULL; -} - -SC_FUNC char *get_dbgstring(int index) -{ - return get_string(&dbgstrings,index); -} - -SC_FUNC void delete_dbgstringtable(void) -{ - delete_stringtable(&dbgstrings); - assert(dbgstrings.next==NULL); -} diff --git a/compiler-init/compiler-init/scmemfil.c b/compiler-init/compiler-init/scmemfil.c deleted file mode 100644 index 73a76c63..00000000 --- a/compiler-init/compiler-init/scmemfil.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Pawn compiler - * - * Routines to maintain a "text file" in memory. - * - * Copyright (c) ITB CompuPhase, 2003-2006 - * - * This software is provided 'as-is', without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from the - * use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: scmemfil.c 3579 2006-06-06 13:35:29Z thiadmer $ - */ - -#include -#include -#include -#include -#include "memfile.h" - -#if defined FORTIFY - #include -#endif - - -#define BUFFERSIZE 512u - -/* For every block, except the first: - * buffer points to a block that is BUFFERSIZE long that holds the data - * bufpos is the "used size" of the block - * For the first block: - * buffer points to the "file name" - * bufpos is the current "file pointer" - */ -typedef memfile_t MEMFILE; -#define tMEMFILE 1 - -#include "sc.h" - - -MEMFILE *mfcreate(char *filename) -{ - return memfile_creat(filename, 4096); -} - -void mfclose(MEMFILE *mf) -{ - memfile_destroy(mf); -} - -int mfdump(MEMFILE *mf) -{ - FILE *fp; - int okay; - - assert(mf!=NULL); - /* create the file */ - fp=fopen(mf->name, "wb"); - if (fp==NULL) - return 0; - - okay=1; - okay = okay & (fwrite(mf->base, mf->usedoffs, 1, fp)==(size_t)mf->usedoffs); - - fclose(fp); - return okay; -} - -long mflength(MEMFILE *mf) -{ - return mf->usedoffs; -} - -long mfseek(MEMFILE *mf,long offset,int whence) -{ - long length; - - assert(mf!=NULL); - if (mf->usedoffs == 0) - return 0L; /* early exit: not a single byte in the file */ - - /* find the size of the memory file */ - length=mflength(mf); - - /* convert the offset to an absolute position */ - switch (whence) { - case SEEK_SET: - break; - case SEEK_CUR: - offset+=mf->offs; - break; - case SEEK_END: - assert(offset<=0); - offset+=length; - break; - } /* switch */ - - /* clamp to the file length limit */ - if (offset<0) - offset=0; - else if (offset>length) - offset=length; - - /* set new position and return it */ - memfile_seek(mf, offset); - - return offset; -} - -unsigned int mfwrite(MEMFILE *mf,unsigned char *buffer,unsigned int size) -{ - return (memfile_write(mf, buffer, size) ? size : 0); -} - -unsigned int mfread(MEMFILE *mf,unsigned char *buffer,unsigned int size) -{ - return memfile_read(mf, buffer, size); -} - -char *mfgets(MEMFILE *mf,char *string,unsigned int size) -{ - char *ptr; - unsigned int read; - long seek; - - assert(mf!=NULL); - - read=mfread(mf,(unsigned char *)string,size); - if (read==0) - return NULL; - seek=0L; - - /* make sure that the string is zero-terminated */ - assert(read<=size); - if (read -#include -#include -#include -#include -#include "sc.h" -#if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ - #include -#endif - -#if defined FORTIFY - #include -#endif - -typedef struct s_statelist { - struct s_statelist *next; - int *states; /* list of states in this combination */ - int numstates; /* number of items in the above list */ - int fsa; /* automaton id */ - int listid; /* unique id for this combination list */ -} statelist; - -static statelist statelist_tab = { NULL, NULL, 0, 0, 0}; /* state combinations table */ - - -static constvalue *find_automaton(const char *name,int *last) -{ - constvalue *ptr; - - assert(last!=NULL); - *last=0; - ptr=sc_automaton_tab.next; - while (ptr!=NULL) { - if (strcmp(name,ptr->name)==0) - return ptr; - if (ptr->index>*last) - *last=ptr->index; - ptr=ptr->next; - } /* while */ - return NULL; -} - -SC_FUNC constvalue *automaton_add(const char *name) -{ - constvalue *ptr; - int last; - - assert(strlen(name)name)); - ptr=find_automaton(name,&last); - if (ptr==NULL) { - assert(last+1 <= SHRT_MAX); - ptr=append_constval(&sc_automaton_tab,name,(cell)0,(short)(last+1)); - } /* if */ - return ptr; -} - -SC_FUNC constvalue *automaton_find(const char *name) -{ - int last; - return find_automaton(name,&last); -} - -SC_FUNC constvalue *automaton_findid(int id) -{ - constvalue *ptr; - for (ptr=sc_automaton_tab.next; ptr!=NULL && ptr->index!=id; ptr=ptr->next) - /* nothing */; - return ptr; -} - - -static constvalue *find_state(const char *name,int fsa,int *last) -{ - constvalue *ptr; - - assert(last!=NULL); - *last=0; - ptr=sc_state_tab.next; - while (ptr!=NULL) { - if (ptr->index==fsa) { - if (strcmp(name,ptr->name)==0) - return ptr; - if ((int)ptr->value>*last) - *last=(int)ptr->value; - } /* if */ - ptr=ptr->next; - } /* while */ - return NULL; -} - -SC_FUNC constvalue *state_add(const char *name,int fsa) -{ - constvalue *ptr; - int last; - - assert(strlen(name)name)); - ptr=find_state(name,fsa,&last); - if (ptr==NULL) { - assert(fsa <= SHRT_MAX); - ptr=append_constval(&sc_state_tab,name,(cell)(last+1),(short)fsa); - } /* if */ - return ptr; -} - -SC_FUNC constvalue *state_find(const char *name,int fsa_id) -{ - int last; /* dummy */ - return find_state(name,fsa_id,&last); -} - -SC_FUNC constvalue *state_findid(int id) -{ - constvalue *ptr; - for (ptr=sc_state_tab.next; ptr!=NULL && ptr->value!=id; ptr=ptr->next) - /* nothing */; - return ptr; -} - -SC_FUNC void state_buildlist(int **list,int *listsize,int *count,int stateid) -{ - int idx; - - assert(list!=NULL); - assert(listsize!=NULL); - assert(*listsize>=0); - assert(count!=NULL); - assert(*count>=0); - assert(*count<=*listsize); - - if (*count==*listsize) { - /* To avoid constantly calling malloc(), the list is grown by 4 states at - * a time. - */ - *listsize+=4; - *list=(int*)realloc(*list,*listsize*sizeof(int)); - if (*list==NULL) - error(103); /* insufficient memory */ - } /* if */ - - /* find the insertion point (the list has to stay sorted) */ - for (idx=0; idx<*count && *list[idx]0); - assert(last!=NULL); - *last=0; - ptr=statelist_tab.next; - while (ptr!=NULL) { - if (ptr->listid>*last) - *last=ptr->listid; - if (ptr->fsa==fsa && ptr->numstates==count) { - /* compare all states */ - for (i=0; istates[i]==list[i]; i++) - /* nothing */; - if (i==count) - return ptr; - } /* if */ - ptr=ptr->next; - } /* while */ - return NULL; -} - -static statelist *state_getlist_ptr(int listid) -{ - statelist *ptr; - - assert(listid>0); - for (ptr=statelist_tab.next; ptr!=NULL && ptr->listid!=listid; ptr=ptr->next) - /* nothing */; - return ptr; -} - -SC_FUNC int state_addlist(int *list,int count,int fsa) -{ - statelist *ptr; - int last; - - assert(list!=NULL); - assert(count>0); - ptr=state_findlist(list,count,fsa,&last); - if (ptr==NULL) { - if ((ptr=(statelist*)malloc(sizeof(statelist)))==NULL) - error(103); /* insufficient memory */ - if ((ptr->states=(int*)malloc(count*sizeof(int)))==NULL) { - free(ptr); - error(103); /* insufficient memory */ - } /* if */ - memcpy(ptr->states,list,count*sizeof(int)); - ptr->numstates=count; - ptr->fsa=fsa; - ptr->listid=last+1; - ptr->next=statelist_tab.next; - statelist_tab.next=ptr; - } /* if */ - assert(ptr!=NULL); - return ptr->listid; -} - -SC_FUNC void state_deletetable(void) -{ - statelist *ptr; - - while (statelist_tab.next!=NULL) { - ptr=statelist_tab.next; - /* unlink first */ - statelist_tab.next=ptr->next; - /* then delete */ - assert(ptr->states!=NULL); - free(ptr->states); - free(ptr); - } /* while */ -} - -SC_FUNC int state_getfsa(int listid) -{ - statelist *ptr; - - assert(listid>=0); - if (listid==0) - return -1; - - ptr=state_getlist_ptr(listid); - return (ptr!=NULL) ? ptr->fsa : -1; /* fsa 0 exists */ -} - -SC_FUNC int state_count(int listid) -{ - statelist *ptr=state_getlist_ptr(listid); - if (ptr==NULL) - return 0; /* unknown list, no states in it */ - return ptr->numstates; -} - -SC_FUNC int state_inlist(int listid,int state) -{ - statelist *ptr; - int i; - - ptr=state_getlist_ptr(listid); - if (ptr==NULL) - return FALSE; /* unknown list, state not in it */ - for (i=0; inumstates; i++) - if (ptr->states[i]==state) - return TRUE; - return FALSE; -} - -SC_FUNC int state_listitem(int listid,int index) -{ - statelist *ptr; - - ptr=state_getlist_ptr(listid); - assert(ptr!=NULL); - assert(index>=0 && indexnumstates); - return ptr->states[index]; -} - -static int checkconflict(statelist *psrc,statelist *ptgt) -{ - int s,t; - - assert(psrc!=NULL); - assert(ptgt!=NULL); - for (s=0; snumstates; s++) - for (t=0; tnumstates; t++) - if (psrc->states[s]==ptgt->states[t]) - return 1; /* state conflict */ - return 0; -} - -/* This function searches whether one of the states in the list of statelist id's - * of a symbol exists in any other statelist id's of the same function; it also - * verifies that all definitions of the symbol are in the same automaton. - */ -SC_FUNC void state_conflict(symbol *root) -{ - statelist *psrc,*ptgt; - constvalue *srcptr,*tgtptr; - symbol *sym; - - assert(root!=NULL); - for (sym=root->next; sym!=NULL; sym=sym->next) { - if (sym->parent!=NULL || sym->ident!=iFUNCTN) - continue; /* hierarchical data type or no function */ - if (sym->states==NULL) - continue; /* this function has no states */ - for (srcptr=sym->states->next; srcptr!=NULL; srcptr=srcptr->next) { - if (srcptr->index==-1) - continue; /* state list id -1 is a special case */ - psrc=state_getlist_ptr(srcptr->index); - assert(psrc!=NULL); - for (tgtptr=srcptr->next; tgtptr!=NULL; tgtptr=tgtptr->next) { - if (tgtptr->index==-1) - continue; /* state list id -1 is a special case */ - ptgt=state_getlist_ptr(tgtptr->index); - assert(ptgt!=NULL); - if (psrc->fsa!=ptgt->fsa && strcmp(sym->name,uENTRYFUNC)!=0) - error(83,sym->name); /* this function is part of another machine */ - if (checkconflict(psrc,ptgt)) - error(84,sym->name); /* state conflict */ - } /* for (tgtptr) */ - } /* for (srcptr) */ - } /* for (sym) */ -} - -/* check whether the two state lists (whose ids are passed in) share any - * states - */ -SC_FUNC int state_conflict_id(int listid1,int listid2) -{ - statelist *psrc,*ptgt; - - psrc=state_getlist_ptr(listid1); - assert(psrc!=NULL); - ptgt=state_getlist_ptr(listid2); - assert(ptgt!=NULL); - return checkconflict(psrc,ptgt); -} diff --git a/compiler-init/compiler-init/scvars.c b/compiler-init/compiler-init/scvars.c deleted file mode 100644 index 6a1365f8..00000000 --- a/compiler-init/compiler-init/scvars.c +++ /dev/null @@ -1,113 +0,0 @@ -/* Pawn compiler - * - * Global (cross-module) variables. - * - * Copyright (c) ITB CompuPhase, 1997-2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: scvars.c 3577 2006-06-02 16:22:52Z thiadmer $ - */ -#include -#include /* for _MAX_PATH */ -#include "sc.h" - -/* global variables - * - * All global variables that are shared amongst the compiler files are - * declared here. - */ -SC_VDEFINE symbol loctab; /* local symbol table */ -SC_VDEFINE symbol glbtab; /* global symbol table */ -SC_VDEFINE cell *litq; /* the literal queue */ -SC_VDEFINE unsigned char pline[sLINEMAX+1]; /* the line read from the input file */ -SC_VDEFINE const unsigned char *lptr; /* points to the current position in "pline" */ -SC_VDEFINE constvalue tagname_tab = { NULL, "", 0, 0}; /* tagname table */ -SC_VDEFINE constvalue libname_tab = { NULL, "", 0, 0}; /* library table (#pragma library "..." syntax) */ -SC_VDEFINE constvalue *curlibrary = NULL; /* current library */ -SC_VDEFINE int pc_addlibtable = TRUE; /* is the library table added to the AMX file? */ -SC_VDEFINE symbol *curfunc; /* pointer to current function */ -SC_VDEFINE char *inpfname; /* pointer to name of the file currently read from */ -SC_VDEFINE char outfname[_MAX_PATH]; /* intermediate (assembler) file name */ -SC_VDEFINE char binfname[_MAX_PATH]; /* binary file name */ -SC_VDEFINE char errfname[_MAX_PATH]; /* error file name */ -SC_VDEFINE char sc_ctrlchar = CTRL_CHAR; /* the control character (or escape character)*/ -SC_VDEFINE char sc_ctrlchar_org = CTRL_CHAR;/* the default control character */ -SC_VDEFINE int litidx = 0; /* index to literal table */ -SC_VDEFINE int litmax = sDEF_LITMAX; /* current size of the literal table */ -SC_VDEFINE int stgidx = 0; /* index to the staging buffer */ -SC_VDEFINE int sc_labnum = 0; /* number of (internal) labels */ -SC_VDEFINE int staging = 0; /* true if staging output */ -SC_VDEFINE cell declared = 0; /* number of local cells declared */ -SC_VDEFINE cell glb_declared=0; /* number of global cells declared */ -SC_VDEFINE cell code_idx = 0; /* number of bytes with generated code */ -SC_VDEFINE int ntv_funcid= 0; /* incremental number of native function */ -SC_VDEFINE int errnum = 0; /* number of errors */ -SC_VDEFINE int warnnum = 0; /* number of warnings */ -SC_VDEFINE int sc_debug = sCHKBOUNDS; /* by default: bounds checking+assertions */ -SC_VDEFINE int sc_packstr= FALSE; /* strings are packed by default? */ -SC_VDEFINE int sc_asmfile= FALSE; /* create .ASM file? */ -SC_VDEFINE int sc_listing= FALSE; /* create .LST file? */ -SC_VDEFINE int sc_compress=TRUE; /* compress bytecode? */ -SC_VDEFINE int sc_needsemicolon=TRUE;/* semicolon required to terminate expressions? */ -SC_VDEFINE int sc_dataalign=sizeof(cell);/* data alignment value */ -SC_VDEFINE int sc_alignnext=FALSE; /* must frame of the next function be aligned? */ -SC_VDEFINE int pc_docexpr=FALSE; /* must expression be attached to documentation comment? */ -SC_VDEFINE int curseg = 0; /* 1 if currently parsing CODE, 2 if parsing DATA */ -SC_VDEFINE cell pc_stksize=sDEF_AMXSTACK;/* default stack size */ -SC_VDEFINE cell pc_amxlimit=0; /* default abstract machine size limit = none */ -SC_VDEFINE cell pc_amxram=0; /* default abstract machine data size limit = none */ -SC_VDEFINE int freading = FALSE; /* Is there an input file ready for reading? */ -SC_VDEFINE int fline = 0; /* the line number in the current file */ -SC_VDEFINE short fnumber = 0; /* the file number in the file table (debugging) */ -SC_VDEFINE short fcurrent= 0; /* current file being processed (debugging) */ -SC_VDEFINE short sc_intest=FALSE; /* true if inside a test */ -SC_VDEFINE int sideeffect= 0; /* true if an expression causes a side-effect */ -SC_VDEFINE int stmtindent= 0; /* current indent of the statement */ -SC_VDEFINE int indent_nowarn=FALSE;/* skip warning "217 loose indentation" */ -SC_VDEFINE int sc_tabsize=8; /* number of spaces that a TAB represents */ -SC_VDEFINE short sc_allowtags=TRUE; /* allow/detect tagnames in lex() */ -SC_VDEFINE int sc_status; /* read/write status */ -SC_VDEFINE int sc_rationaltag=0; /* tag for rational numbers */ -SC_VDEFINE int rational_digits=0; /* number of fractional digits */ -SC_VDEFINE int sc_allowproccall=0; /* allow/detect tagnames in lex() */ -SC_VDEFINE short sc_is_utf8=FALSE; /* is this source file in UTF-8 encoding */ -SC_VDEFINE char *pc_depricate=NULL;/* if non-null, mark next declaration as depricated */ -SC_VDEFINE int sc_curstates=0; /* ID of the current state list */ -SC_VDEFINE int pc_optimize=sOPTIMIZE_NOMACRO; /* (peephole) optimization level */ -SC_VDEFINE int pc_memflags=0; /* special flags for the stack/heap usage */ - -SC_VDEFINE constvalue sc_automaton_tab = { NULL, "", 0, 0}; /* automaton table */ -SC_VDEFINE constvalue sc_state_tab = { NULL, "", 0, 0}; /* state table */ - -SC_VDEFINE FILE *inpf = NULL; /* file read from (source or include) */ -SC_VDEFINE FILE *inpf_org= NULL; /* main source file */ -SC_VDEFINE FILE *outf = NULL; /* (intermediate) text file written to */ - -SC_VDEFINE jmp_buf errbuf; - -#if !defined SC_LIGHT - SC_VDEFINE int sc_makereport=FALSE; /* generate a cross-reference report */ -#endif - -#if defined __WATCOMC__ && !defined NDEBUG - /* Watcom's CVPACK dislikes .OBJ files without functions */ - static int dummyfunc(void) - { - return 0; - } -#endif diff --git a/compiler-init/compiler-init/spcomp.sln b/compiler-init/compiler-init/spcomp.sln deleted file mode 100644 index 461a2bf6..00000000 --- a/compiler-init/compiler-init/spcomp.sln +++ /dev/null @@ -1,21 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spcomp", "spcomp.vcproj", "{B4C844FF-008D-4BD5-B82F-DC06E706C64B}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Debug.ActiveCfg = Debug|Win32 - {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Debug.Build.0 = Debug|Win32 - {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Release.ActiveCfg = Release|Win32 - {B4C844FF-008D-4BD5-B82F-DC06E706C64B}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal diff --git a/compiler-init/compiler-init/spcomp.vcproj b/compiler-init/compiler-init/spcomp.vcproj deleted file mode 100644 index 028f5acb..00000000 --- a/compiler-init/compiler-init/spcomp.vcproj +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/compiler-init/compiler-init/svnrev.h b/compiler-init/compiler-init/svnrev.h deleted file mode 100644 index b0d8a703..00000000 --- a/compiler-init/compiler-init/svnrev.h +++ /dev/null @@ -1,9 +0,0 @@ -#define SMC_VERSION 1 -#define SMC_REVISION 0 -#define SMC_BUILD 1 -#define SMC_VERSTRING "1.0.1.3599" - -#define SVN_REV 3599 -#define SVN_REVSTR "3599" -#define SVN_REVDATE "2006-07-05" -#define SVN_REVSTAMP 20060705L From cb1b5aee3503be0f91bd4f33d38262115f303df6 Mon Sep 17 00:00:00 2001 From: Borja Ferrer Date: Fri, 14 Jul 2006 02:39:00 +0000 Subject: [PATCH 07/20] first step for decl --HG-- branch : faluco extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/faluco%409 --- compiler-init/sc.h | 2 +- compiler-init/sc1.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler-init/sc.h b/compiler-init/sc.h index 25dcd931..7a476a2e 100644 --- a/compiler-init/sc.h +++ b/compiler-init/sc.h @@ -332,7 +332,7 @@ typedef struct s_stringpair { #define tDECL 299 #define tOPERATOR 300 #define tPUBLIC 301 -#define tRETURN 303 +#define tRETURN 302 #define tSIZEOF 303 #define tSLEEP 304 #define tSTATE 305 diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 64f44b92..59a2efdb 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -4591,6 +4591,14 @@ static void statement(int *lastindent,int allow_decl) error(3); /* declaration only valid in a block */ } /* if */ break; + case tDECL: + if (allow_decl) { + declloc(FALSE); + lastst=tDECL; + } else { + error(3); /* declaration only valid in a block */ + } /* if */ + break; case tSTATIC: if (allow_decl) { declloc(TRUE); From 79ef836977b0455136df2bb06cf26181ea599a8f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 04:24:42 +0000 Subject: [PATCH 08/20] oh, fixed linemax --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4010 --- compiler-init/sc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler-init/sc.h b/compiler-init/sc.h index d455c4e1..612a9911 100644 --- a/compiler-init/sc.h +++ b/compiler-init/sc.h @@ -51,7 +51,7 @@ #define sCHARBITS 8 /* size of a packed character */ #define sDIMEN_MAX 4 /* maximum number of array dimensions */ -#define sLINEMAX 1024 /* input line length (in characters) */ +#define sLINEMAX 1023 /* input line length (in characters) */ #define sCOMP_STACK 32 /* maximum nesting of #if .. #endif sections */ #define sDEF_LITMAX 500 /* initial size of the literal pool, in "cells" */ #define sDEF_AMXSTACK 4096 /* default stack size for AMX files */ From 4825f2ce40683174afe04cd74c1f00c10618a4fa Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 04:51:58 +0000 Subject: [PATCH 09/20] removed warning 208 added __DATE__ and __TIME__ changed forward declarations to have case insensitive arguments --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4011 --- compiler-init/sc1.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 64f44b92..fd0b7452 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -61,6 +61,8 @@ #include #endif +#include + #include "lstring.h" #include "sc.h" #include "svnrev.h" @@ -130,6 +132,7 @@ static void dostate(void); static void addwhile(int *ptr); static void delwhile(void); static int *readwhile(void); +static void inst_datetime_defines(void); static int norun = 0; /* the compiler never ran */ static int lastst = 0; /* last executed statement type */ @@ -305,6 +308,7 @@ int pc_compile(int argc, char *argv[]) delete_symbols(&glbtab,0,TRUE,FALSE); #if !defined NO_DEFINE delete_substtable(); + inst_datetime_defines(); #endif resetglobals(); sc_ctrlchar=sc_ctrlchar_org; @@ -369,6 +373,7 @@ int pc_compile(int argc, char *argv[]) delete_symbols(&glbtab,0,TRUE,FALSE); #if !defined NO_DEFINE delete_substtable(); + inst_datetime_defines(); #endif resetglobals(); sc_ctrlchar=sc_ctrlchar_org; @@ -522,6 +527,22 @@ int pc_addconstant(char *name,cell value,int tag) return 1; } +static void inst_datetime_defines(void) +{ + char date[64]; + char ltime[64]; + time_t td; + struct tm *curtime; + + time(&td); + curtime = localtime(&td); + strftime(date, 31, "\"%m/%d%Y\"", curtime); + strftime(ltime, 31, "\"%H:%M:%S\"", curtime); + + insert_subst("__DATE__", date, 8); + insert_subst("__TIME__", ltime, 8); +} + #if defined __cplusplus extern "C" #endif @@ -1041,7 +1062,7 @@ static void setconfig(char *root) #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ /* see www.autopackage.org for the BinReloc module */ br_init_lib(NULL); - ptr=br_find_exe("/opt/pawn/bin/pawncc"); + ptr=br_find_exe("spcomp"); strlcpy(path,ptr,sizeof path); free(ptr); #else @@ -3199,7 +3220,9 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc if ((sym->usage & (uPROTOTYPED | uREAD))==uREAD && sym->tag!=0) { int curstatus=sc_status; sc_status=statWRITE; /* temporarily set status to WRITE, so the warning isn't blocked */ +#if 0 /* SourceMod - silly, should be removed in first pass, so removed */ error(208); +#endif sc_status=curstatus; sc_reparse=TRUE; /* must add another pass to "initial scan" phase */ } /* if */ @@ -3331,7 +3354,11 @@ static int argcompare(arginfo *a1,arginfo *a2) { int result,level,i; +#if 0 /* SourceMod uses case insensitive args for forwards */ result= strcmp(a1->name,a2->name)==0; /* name */ +#else + result=1; +#endif if (result) result= a1->ident==a2->ident; /* type/class */ if (result) From cc6e3f9fabf82957508f337f9c6a03f8d74887af Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 05:51:25 +0000 Subject: [PATCH 10/20] removed debug code fixed defined() not working with no spaces afterward. fixed preprocessor not ignoring string literals --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4012 --- compiler-init/sc2.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/compiler-init/sc2.c b/compiler-init/sc2.c index 667e6d44..59d30b2c 100644 --- a/compiler-init/sc2.c +++ b/compiler-init/sc2.c @@ -715,7 +715,7 @@ static int ftoi(cell *val,const unsigned char *curptr) #if PAWN_CELL_SIZE==32 float value=(float)fnum; *val=*((cell *)&value); - #if !defined NDEBUG + #if 0 /* SourceMod - not needed */ /* I assume that the C/C++ compiler stores "float" values in IEEE 754 * format (as mandated in the ANSI standard). Test this assumption * anyway. @@ -733,7 +733,7 @@ static int ftoi(cell *val,const unsigned char *curptr) #endif #elif PAWN_CELL_SIZE==64 *val=*((cell *)&fnum); - #if !defined NDEBUG + #if 0 /* SourceMod - not needed */ /* I assume that the C/C++ compiler stores "double" values in IEEE 754 * format (as mandated in the ANSI standard). */ @@ -1456,7 +1456,7 @@ static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char int prefixlen; const unsigned char *p,*s,*e; unsigned char *args[10]; - int match,arg,len; + int match,arg,len,argsnum=0; memset(args,0,sizeof args); @@ -1496,6 +1496,8 @@ static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char /* store the parameter (overrule any earlier) */ if (args[arg]!=NULL) free(args[arg]); + else + argsnum++; len=(int)(e-s); args[arg]=(unsigned char*)malloc(len+1); if (args[arg]==NULL) @@ -1551,14 +1553,15 @@ static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char if (match) { /* calculate the length of the substituted string */ for (e=(unsigned char*)substitution,len=0; *e!='\0'; e++) { - if (*e=='%' && isdigit(*(e+1))) { + if (*e=='%' && isdigit(*(e+1)) && argsnum) { arg=*(e+1)-'0'; assert(arg>=0 && arg<=9); - if (args[arg]!=NULL) + if (args[arg]!=NULL) { len+=strlen((char*)args[arg]); - else - len+=2; /* copy '%' plus digit */ - e++; /* skip %, digit is skipped later */ + e++; + } else { + len++; + } } else { len++; } /* if */ @@ -1576,12 +1579,23 @@ static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char if (args[arg]!=NULL) { strins((char*)s,(char*)args[arg],strlen((char*)args[arg])); s+=strlen((char*)args[arg]); + e++; } else { error(236); /* parameter does not exist, incorrect #define pattern */ strins((char*)s,(char*)e,2); s+=2; } /* if */ e++; /* skip %, digit is skipped later */ + } else if (*e == '"') { + p=e; + if (is_startstring(e)) { + e=skipstring(e); + strins((char*)s,(char *)p,(e-p+1)); + s+=(e-p+1); + } else { + strins((char*)s,(char*)e,1); + s++; + } } else { strins((char*)s,(char*)e,1); s++; @@ -1620,7 +1634,7 @@ static void substallpatterns(unsigned char *line,int buffersize) if (*start=='\0') break; /* abort loop on error */ /* if matching the operator "defined", skip it plus the symbol behind it */ - if (strncmp((char*)start,"defined",7)==0 && *(start+7)<=' ') { + if (strncmp((char*)start,"defined",7)==0 && !isalpha((char)*(start+7))) { start+=7; /* skip "defined" */ /* skip white space & parantheses */ while (*start<=' ' && *start!='\0' || *start=='(') From 97312faf3d917094a862496af72698329e5ff0bf Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 06:02:53 +0000 Subject: [PATCH 11/20] wrong arguments moved from warning 202 to error 92 --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4013 --- compiler-init/sc2.c | 20 ++++++++++---------- compiler-init/sc3.c | 4 ++-- compiler-init/sc5.scp | 6 ++++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/compiler-init/sc2.c b/compiler-init/sc2.c index 59d30b2c..1a822f5f 100644 --- a/compiler-init/sc2.c +++ b/compiler-init/sc2.c @@ -1586,16 +1586,16 @@ static int substpattern(unsigned char *line,size_t buffersize,char *pattern,char s+=2; } /* if */ e++; /* skip %, digit is skipped later */ - } else if (*e == '"') { - p=e; - if (is_startstring(e)) { - e=skipstring(e); - strins((char*)s,(char *)p,(e-p+1)); - s+=(e-p+1); - } else { - strins((char*)s,(char*)e,1); - s++; - } + } else if (*e == '"') { + p=e; + if (is_startstring(e)) { + e=skipstring(e); + strins((char*)s,(char *)p,(e-p+1)); + s+=(e-p+1); + } else { + strins((char*)s,(char*)e,1); + s++; + } } else { strins((char*)s,(char*)e,1); s++; diff --git a/compiler-init/sc3.c b/compiler-init/sc3.c index 65fec149..f4ede2aa 100644 --- a/compiler-init/sc3.c +++ b/compiler-init/sc3.c @@ -1988,7 +1988,7 @@ static int nesting=0; if (matchtoken('_')) { arglist[argpos]=ARG_IGNORED; /* flag argument as "present, but ignored" */ if (arg[argidx].ident==0 || arg[argidx].ident==iVARARGS) { - error(202); /* argument count mismatch */ + error(92); /* argument count mismatch */ } else if (!arg[argidx].hasdefault) { error(34,nargs+1); /* argument has no default value */ } /* if */ @@ -2007,7 +2007,7 @@ static int nesting=0; assert(sc_status==statFIRST || arg[argidx].tags!=NULL); switch (arg[argidx].ident) { case 0: - error(202); /* argument count mismatch */ + error(92); /* argument count mismatch */ break; case iVARARGS: /* always pass by reference */ diff --git a/compiler-init/sc5.scp b/compiler-init/sc5.scp index a08315c2..5fef861a 100644 --- a/compiler-init/sc5.scp +++ b/compiler-init/sc5.scp @@ -129,7 +129,8 @@ static char *errmsg[] = { /*088*/ "public variables and local variables may not have states (symbol \"%s\")\n", /*089*/ "state variables may not be initialized (symbol \"%s\")\n", /*090*/ "public functions may not return arrays (symbol \"%s\")\n", -/*091*/ "ambiguous constant; tag override is required (symbol \"%s\")\n" +/*091*/ "ambiguous constant; tag override is required (symbol \"%s\")\n", +/*092*/ "number of arguments does not match definition\n" #else "\271pect\235\306k\212:\227\316bu\202fo\217\206\216\012", "\201l\223\252s\203g\352\315e\234\202(\255\363\201) c\355f\240\344w ea\275 \042c\351e\042\012", @@ -221,7 +222,8 @@ static char *errmsg[] = { "pu\236\326 \301\310\226\206\344c\337\301\310\367\242\340\376\315\310\323", "\315\200\301\310\367\242\312\203i\205\211iz\235\323", "pu\236\326 \366\207\367\242\221tur\336\256y\207\323", - "a\230i\262ou\207\354t; \373ov\210rid\200\272\221qui\221\206\323" + "a\230i\262ou\207\354t; \373ov\210rid\200\272\221qui\221\206\323", + "\374\230\264\304\265t\207do\310\242\345\275 \327i\214\012", #endif }; From 3f8c9a83adae3bf16eb25027e7415293b0bad876 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 06:21:07 +0000 Subject: [PATCH 12/20] fixed public functions requiring a forward --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4014 --- compiler-init/sc1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index fd0b7452..95913675 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -3226,11 +3226,13 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sc_status=curstatus; sc_reparse=TRUE; /* must add another pass to "initial scan" phase */ } /* if */ +#if 0 /* Not used for SourceMod */ /* we want public functions to be explicitly prototyped, as they are called * from the outside */ if (fpublic && (sym->usage & uFORWARD)==0) error(235,symbolname); +#endif /* declare all arguments */ argcnt=declargs(sym,TRUE); opererror=!operatoradjust(opertok,sym,symbolname,tag); From 9cd64d946cb126319332bcab24a332ec23c46c24 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 14 Jul 2006 06:47:51 +0000 Subject: [PATCH 13/20] public functions can now be passed as arguments, with the tag 'Function:' --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4015 --- compiler-init/sc3.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/compiler-init/sc3.c b/compiler-init/sc3.c index f4ede2aa..ddb0acc3 100644 --- a/compiler-init/sc3.c +++ b/compiler-init/sc3.c @@ -1670,13 +1670,29 @@ restart: if (sc_allowproccall) { callfunction(sym,lval1,FALSE); } else { - lval1->sym=NULL; - lval1->ident=iEXPRESSION; - lval1->constval=0; - lval1->tag=0; - error(76); /* invalid function call, or syntax error */ - } /* if */ - return FALSE; + int n=-1,iter=0; + for (sym=glbtab.next; sym!=NULL; sym=sym->next) { + if (sym->ident==iFUNCTN + && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) + { + assert(sym->vclass==sGLOBAL); + if (strcmp(sym->name, lval1->sym->name)==0) { + n = iter; + break; + } + iter++; + } + } + if (n!=-1) { + lval1->sym=NULL; + lval1->ident=iCONSTEXPR; + lval1->constval=n; + lval1->tag=pc_addtag("Function"); + } else { + error(76); /* invalid function call, or syntax error */ + } /* if */ + return FALSE; + } } /* if */ return lvalue; } From 2a032b6bf77be054d131ff838282526e8764a4bb Mon Sep 17 00:00:00 2001 From: Borja Ferrer Date: Sat, 15 Jul 2006 01:24:25 +0000 Subject: [PATCH 14/20] finished decl implementation --HG-- branch : faluco extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/faluco%4016 --- compiler-init/sc1.c | 59 +++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 59a2efdb..33f3f7c5 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -132,6 +132,7 @@ static void delwhile(void); static int *readwhile(void); static int norun = 0; /* the compiler never ran */ +static int autozero = 0; /* if 1 will zero out the variable, if 0 omit the zeroing */ static int lastst = 0; /* last executed statement type */ static int nestlevel = 0; /* number of active (open) compound statements */ static int rettype = 0; /* the type that a "return" expression should have */ @@ -1958,19 +1959,24 @@ static int declloc(int fstatic) int ctag = tag; /* set to "tag" by default */ int explicit_init=FALSE;/* is the variable explicitly initialized? */ if (matchtoken('=')) { + if (!autozero) + error(10); doexpr(FALSE,FALSE,FALSE,FALSE,&ctag,NULL,TRUE); explicit_init=TRUE; } else { - ldconst(0,sPRI); /* uninitialized variable, set to zero */ + if (autozero) + ldconst(0,sPRI); /* uninitialized variable, set to zero */ } /* if */ - /* now try to save the value (still in PRI) in the variable */ - lval.sym=sym; - lval.ident=iVARIABLE; - lval.constval=0; - lval.tag=tag; - check_userop(NULL,ctag,lval.tag,2,NULL,&ctag); - store(&lval); - markexpr(sEXPR,NULL,0); /* full expression ends after the store */ + if (autozero) { + /* now try to save the value (still in PRI) in the variable */ + lval.sym=sym; + lval.ident=iVARIABLE; + lval.constval=0; + lval.tag=tag; + check_userop(NULL,ctag,lval.tag,2,NULL,&ctag); + store(&lval); + markexpr(sEXPR,NULL,0); /* full expression ends after the store */ + } assert(staging); /* end staging phase (optimize expression) */ stgout(staging_start); stgset(FALSE); @@ -1985,15 +1991,17 @@ static int declloc(int fstatic) assert(cur_lit>=0 && cur_lit<=litidx && litidx<=litmax); assert(size>0 && size>=sym->dim.array.length); assert(numdim>1 || size==sym->dim.array.length); - /* final literal values that are zero make no sense to put in the literal - * pool, because values get zero-initialized anyway; we check for this, - * because users often explicitly initialize strings to "" - */ - while (litidx>cur_lit && litq[litidx-1]==0) - litidx--; - /* if the array is not completely filled, set all values to zero first */ - if (litidx-cur_litcur_lit && litq[litidx-1]==0) + litidx--; + /* if the array is not completely filled, set all values to zero first */ + if (litidx-cur_litnext : NULL; do { int fieldlit=litidx; @@ -2312,6 +2320,8 @@ static cell initvector(int ident,int tag,cell size,int fillzero, } while (matchtoken(',')); /* do */ needtoken('}'); } else { + if (hadtoken && !autozero) + error(10); init(ident,&ctag,errorfound); if (!matchtag(tag,ctag,TRUE)) error(213); /* tagname mismatch */ @@ -4585,6 +4595,7 @@ static void statement(int *lastindent,int allow_decl) break; case tNEW: if (allow_decl) { + autozero=1; declloc(FALSE); lastst=tNEW; } else { @@ -4593,9 +4604,10 @@ static void statement(int *lastindent,int allow_decl) break; case tDECL: if (allow_decl) { - declloc(FALSE); - lastst=tDECL; - } else { + autozero=0; + declloc(FALSE); + lastst=tDECL; + } else { error(3); /* declaration only valid in a block */ } /* if */ break; @@ -4974,6 +4986,7 @@ static void dofor(void) * 'compound statement' level of it own. */ nestlevel++; + autozero=1; declloc(FALSE); /* declare local variable */ } else { doexpr(TRUE,TRUE,TRUE,TRUE,NULL,NULL,FALSE); /* expression 1 */ From d2cee081acadcb0e4a019d876772c5329ad75648 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 15 Jul 2006 01:36:50 +0000 Subject: [PATCH 15/20] finished 2/6 sections for file output fixed some more minor sm things --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4018 --- compiler-init/amx.h | 171 ++++++++++++++- compiler-init/libpawnc.c | 5 + compiler-init/osdefs.h | 1 + compiler-init/pawncc.c | 411 +++++++++++++++++++++++++++++++++--- compiler-init/sc1.c | 12 +- compiler-init/sc2.c | 2 + compiler-init/sc6.c | 4 +- compiler-init/sp_file.h | 102 +++++++++ compiler-init/spcomp.vcproj | 8 + 9 files changed, 678 insertions(+), 38 deletions(-) create mode 100644 compiler-init/sp_file.h diff --git a/compiler-init/amx.h b/compiler-init/amx.h index 6efdd35e..ca25a0da 100644 --- a/compiler-init/amx.h +++ b/compiler-init/amx.h @@ -34,9 +34,10 @@ #include #endif -#if defined HAVE_STDINT_H +#if defined __GNUC__ #include -#else + #define HAVE_STDINT_H +#elif !defined HAVE_STDINT_H #if defined __LCC__ || defined __DMC__ || defined LINUX || (defined __WATCOMC__ && __WATCOMC__ >= 1200) #if defined HAVE_INTTYPES_H #include @@ -459,4 +460,170 @@ int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); } #endif +typedef enum { + OP_NONE, /* invalid opcode */ + OP_LOAD_PRI, + OP_LOAD_ALT, + OP_LOAD_S_PRI, + OP_LOAD_S_ALT, + OP_LREF_PRI, + OP_LREF_ALT, + OP_LREF_S_PRI, + OP_LREF_S_ALT, + OP_LOAD_I, + OP_LODB_I, + OP_CONST_PRI, + OP_CONST_ALT, + OP_ADDR_PRI, + OP_ADDR_ALT, + OP_STOR_PRI, + OP_STOR_ALT, + OP_STOR_S_PRI, + OP_STOR_S_ALT, + OP_SREF_PRI, + OP_SREF_ALT, + OP_SREF_S_PRI, + OP_SREF_S_ALT, + OP_STOR_I, + OP_STRB_I, + OP_LIDX, + OP_LIDX_B, + OP_IDXADDR, + OP_IDXADDR_B, + OP_ALIGN_PRI, + OP_ALIGN_ALT, + OP_LCTRL, + OP_SCTRL, + OP_MOVE_PRI, + OP_MOVE_ALT, + OP_XCHG, + OP_PUSH_PRI, + OP_PUSH_ALT, + OP_PUSH_R, + OP_PUSH_C, + OP_PUSH, + OP_PUSH_S, + OP_POP_PRI, + OP_POP_ALT, + OP_STACK, + OP_HEAP, + OP_PROC, + OP_RET, + OP_RETN, + OP_CALL, + OP_CALL_PRI, + OP_JUMP, + OP_JREL, + OP_JZER, + OP_JNZ, + OP_JEQ, + OP_JNEQ, + OP_JLESS, + OP_JLEQ, + OP_JGRTR, + OP_JGEQ, + OP_JSLESS, + OP_JSLEQ, + OP_JSGRTR, + OP_JSGEQ, + OP_SHL, + OP_SHR, + OP_SSHR, + OP_SHL_C_PRI, + OP_SHL_C_ALT, + OP_SHR_C_PRI, + OP_SHR_C_ALT, + OP_SMUL, + OP_SDIV, + OP_SDIV_ALT, + OP_UMUL, + OP_UDIV, + OP_UDIV_ALT, + OP_ADD, + OP_SUB, + OP_SUB_ALT, + OP_AND, + OP_OR, + OP_XOR, + OP_NOT, + OP_NEG, + OP_INVERT, + OP_ADD_C, + OP_SMUL_C, + OP_ZERO_PRI, + OP_ZERO_ALT, + OP_ZERO, + OP_ZERO_S, + OP_SIGN_PRI, + OP_SIGN_ALT, + OP_EQ, + OP_NEQ, + OP_LESS, + OP_LEQ, + OP_GRTR, + OP_GEQ, + OP_SLESS, + OP_SLEQ, + OP_SGRTR, + OP_SGEQ, + OP_EQ_C_PRI, + OP_EQ_C_ALT, + OP_INC_PRI, + OP_INC_ALT, + OP_INC, + OP_INC_S, + OP_INC_I, + OP_DEC_PRI, + OP_DEC_ALT, + OP_DEC, + OP_DEC_S, + OP_DEC_I, + OP_MOVS, + OP_CMPS, + OP_FILL, + OP_HALT, + OP_BOUNDS, + OP_SYSREQ_PRI, + OP_SYSREQ_C, + OP_FILE, /* obsolete */ + OP_LINE, /* obsolete */ + OP_SYMBOL, /* obsolete */ + OP_SRANGE, /* obsolete */ + OP_JUMP_PRI, + OP_SWITCH, + OP_CASETBL, + OP_SWAP_PRI, + OP_SWAP_ALT, + OP_PUSH_ADR, + OP_NOP, + OP_SYSREQ_N, + OP_SYMTAG, /* obsolete */ + OP_BREAK, + OP_PUSH2_C, + OP_PUSH2, + OP_PUSH2_S, + OP_PUSH2_ADR, + OP_PUSH3_C, + OP_PUSH3, + OP_PUSH3_S, + OP_PUSH3_ADR, + OP_PUSH4_C, + OP_PUSH4, + OP_PUSH4_S, + OP_PUSH4_ADR, + OP_PUSH5_C, + OP_PUSH5, + OP_PUSH5_S, + OP_PUSH5_ADR, + OP_LOAD_BOTH, + OP_LOAD_S_BOTH, + OP_CONST, + OP_CONST_S, + /* ----- */ + OP_SYSREQ_D, + OP_SYSREQ_ND, + /* ----- */ + OP_NUM_OPCODES +} OPCODE; + #endif /* AMX_H_INCLUDED */ diff --git a/compiler-init/libpawnc.c b/compiler-init/libpawnc.c index ca42d1a9..c2aa9f27 100644 --- a/compiler-init/libpawnc.c +++ b/compiler-init/libpawnc.c @@ -221,6 +221,8 @@ char *pc_readasm(void *handle, char *string, int maxchars) #endif } +extern memfile_t *bin_file; + /* Should return a pointer, which is used as a "magic cookie" to all I/O * functions; return NULL for failure. */ @@ -234,6 +236,9 @@ void pc_closebin(void *handle,int deletefile) if (deletefile) { memfile_destroy((memfile_t *)handle); + bin_file = NULL; + } else { + bin_file = (memfile_t *)handle; } } diff --git a/compiler-init/osdefs.h b/compiler-init/osdefs.h index 299af953..7a6768c2 100644 --- a/compiler-init/osdefs.h +++ b/compiler-init/osdefs.h @@ -42,6 +42,7 @@ # define access _access # define chdir _chdir # define strdup _strdup +# define unlink _unlink # endif #endif diff --git a/compiler-init/pawncc.c b/compiler-init/pawncc.c index b8234b56..9e834d94 100644 --- a/compiler-init/pawncc.c +++ b/compiler-init/pawncc.c @@ -1,30 +1,389 @@ -/* Pawn compiler driver - * - * Function and variable definition and declaration, statement parser. - * - * Copyright (c) ITB CompuPhase, 2006 - * - * This software is provided "as-is", without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in - * a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Version: $Id: Sc1.c 3114 2005-03-17 14:48:29Z thiadmer $ - */ -#include "sc.h" +#include +#include +#include +#include +#include "memfile.h" +#include "sp_file.h" +#include "amx.h" +#include "osdefs.h" + +#define NUM_SECTIONS 6 + +int pc_printf(const char *message,...); +int pc_compile(int argc, char **argv); +void sfwrite(const void *buf, size_t size, size_t count, FILE *fp); + +memfile_t *bin_file = NULL; +jmp_buf brkout; int main(int argc, char *argv[]) { - return pc_compile(argc,argv); + if (pc_compile(argc,argv) == 0) + { + FILE *fp; + AMX_HEADER *hdr; + sp_file_hdr_t shdr; + uint32_t curoffs = 0; + uint32_t lastsection = 0; + int err; + uint8_t i8; + uint32_t i; + const char *tables[NUM_SECTIONS] = {".code", ".data", ".publics", ".pubvars", ".natives", ".names"}; + uint32_t offsets[NUM_SECTIONS] = {0,0,0,0,0,0}; + sp_file_section_t sh; + + if (bin_file == NULL) + { + return 0; + } + + hdr = (AMX_HEADER *)bin_file->base; + shdr.version = SPFILE_VERSION; + shdr.magic = SPFILE_MAGIC; + + if ((fp=fopen(bin_file->name, "wb")) == NULL) + { + pc_printf("Error writing to file: %s", bin_file->name); + return 1; + } + + if ((err=setjmp(brkout))!=0) + { + goto write_error; + } + + shdr.sections = NUM_SECTIONS; + shdr.stringtab = sizeof(shdr) + (sizeof(sp_file_section_t) * shdr.sections); + + /** + * write the header + * unwritten values: + * imagesize + */ + sfwrite(&shdr, sizeof(shdr), 1, fp); + + curoffs = shdr.stringtab; + + /** + * write the sections + * unwritten values: + * dataoffs + * size + */ + for (i8=0; i8amx_version; + cod.flags = 0; + if (hdr->flags & AMX_FLAG_DEBUG) + { + cod.flags |= SP_FILE_DEBUG; + } + cod.code = sizeof(cod); + + /* write the code in our newer format */ + cbase = (unsigned char *)hdr + hdr->cod; + real_codesize = hdr->dat - hdr->cod; + tbase = (uint8_t *)malloc(real_codesize); + tptr = tbase; + for (cip = 0; cip < real_codesize;) + { +#define DBGPARAM(v) ( (v)=*(cell *)(cbase+(int)cip), cip+=sizeof(cell) ) + op=(uint8_t) *(ucell *)(cbase+(int)cip); + cip += sizeof(cell); + *tptr++ = op; + switch (op) + { + case OP_PUSH5_C: /* instructions with 5 parameters */ + case OP_PUSH5: + case OP_PUSH5_S: + case OP_PUSH5_ADR: + { + memcpy(tptr, cbase+(int)cip, sizeof(cell)*5); + cip += sizeof(cell)*5; + tptr += sizeof(cell)*5; + break; + } + case OP_PUSH4_C: /* instructions with 4 parameters */ + case OP_PUSH4: + case OP_PUSH4_S: + case OP_PUSH4_ADR: + { + memcpy(tptr, cbase+(int)cip, sizeof(cell)*4); + cip += sizeof(cell)*4; + tptr += sizeof(cell)*4; + break; + } + case OP_PUSH3_C: /* instructions with 3 parameters */ + case OP_PUSH3: + case OP_PUSH3_S: + case OP_PUSH3_ADR: + { + memcpy(tptr, cbase+(int)cip, sizeof(cell)*3); + cip += sizeof(cell)*3; + tptr += sizeof(cell)*3; + break; + } + case OP_PUSH2_C: /* instructions with 2 parameters */ + case OP_PUSH2: + case OP_PUSH2_S: + case OP_PUSH2_ADR: + case OP_LOAD_BOTH: + case OP_LOAD_S_BOTH: + case OP_CONST: + case OP_CONST_S: + case OP_SYSREQ_N: + { + memcpy(tptr, cbase+(int)cip, sizeof(cell)*2); + cip += sizeof(cell)*2; + tptr += sizeof(cell)*2; + break; + } + case OP_LOAD_PRI: /* instructions with 1 parameter */ + case OP_LOAD_ALT: + case OP_LOAD_S_PRI: + case OP_LOAD_S_ALT: + case OP_LREF_PRI: + case OP_LREF_ALT: + case OP_LREF_S_PRI: + case OP_LREF_S_ALT: + case OP_LODB_I: + case OP_CONST_PRI: + case OP_CONST_ALT: + case OP_ADDR_PRI: + case OP_ADDR_ALT: + case OP_STOR_PRI: + case OP_STOR_ALT: + case OP_STOR_S_PRI: + case OP_STOR_S_ALT: + case OP_SREF_PRI: + case OP_SREF_ALT: + case OP_SREF_S_PRI: + case OP_SREF_S_ALT: + case OP_STRB_I: + case OP_LIDX_B: + case OP_IDXADDR_B: + case OP_ALIGN_PRI: + case OP_ALIGN_ALT: + case OP_LCTRL: + case OP_SCTRL: + case OP_PUSH_R: + case OP_PUSH_C: + case OP_PUSH: + case OP_PUSH_S: + case OP_STACK: + case OP_HEAP: + case OP_JREL: + case OP_SHL_C_PRI: + case OP_SHL_C_ALT: + case OP_SHR_C_PRI: + case OP_SHR_C_ALT: + case OP_ADD_C: + case OP_SMUL_C: + case OP_ZERO: + case OP_ZERO_S: + case OP_EQ_C_PRI: + case OP_EQ_C_ALT: + case OP_INC: + case OP_INC_S: + case OP_DEC: + case OP_DEC_S: + case OP_MOVS: + case OP_CMPS: + case OP_FILL: + case OP_HALT: + case OP_BOUNDS: + case OP_PUSH_ADR: + case OP_CALL: /* opcodes that need relocation */ + case OP_JUMP: + case OP_JZER: + case OP_JNZ: + case OP_JEQ: + case OP_JNEQ: + case OP_JLESS: + case OP_JLEQ: + case OP_JGRTR: + case OP_JGEQ: + case OP_JSLESS: + case OP_JSLEQ: + case OP_JSGRTR: + case OP_JSGEQ: + case OP_SWITCH: + case OP_SYSREQ_C: + { + *(cell *)tptr = *(cell *)(cbase + (int)cip); + cip += sizeof(cell); + tptr += sizeof(cell); + break; + } + case OP_LOAD_I: /* instructions without parameters */ + case OP_STOR_I: + case OP_LIDX: + case OP_IDXADDR: + case OP_MOVE_PRI: + case OP_MOVE_ALT: + case OP_XCHG: + case OP_PUSH_PRI: + case OP_PUSH_ALT: + case OP_POP_PRI: + case OP_POP_ALT: + case OP_PROC: + case OP_RET: + case OP_RETN: + case OP_CALL_PRI: + case OP_SHL: + case OP_SHR: + case OP_SSHR: + case OP_SMUL: + case OP_SDIV: + case OP_SDIV_ALT: + case OP_UMUL: + case OP_UDIV: + case OP_UDIV_ALT: + case OP_ADD: + case OP_SUB: + case OP_SUB_ALT: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_NOT: + case OP_NEG: + case OP_INVERT: + case OP_ZERO_PRI: + case OP_ZERO_ALT: + case OP_SIGN_PRI: + case OP_SIGN_ALT: + case OP_EQ: + case OP_NEQ: + case OP_LESS: + case OP_LEQ: + case OP_GRTR: + case OP_GEQ: + case OP_SLESS: + case OP_SLEQ: + case OP_SGRTR: + case OP_SGEQ: + case OP_INC_PRI: + case OP_INC_ALT: + case OP_INC_I: + case OP_DEC_PRI: + case OP_DEC_ALT: + case OP_DEC_I: + case OP_SYSREQ_PRI: + case OP_JUMP_PRI: + case OP_SWAP_PRI: + case OP_SWAP_ALT: + case OP_NOP: + case OP_BREAK: + break; + case OP_CASETBL: + { + cell num; + int i; + DBGPARAM(*(cell *)tptr); + num = *(cell *)tptr; + tptr += sizeof(cell); + memcpy(tptr, cbase+(int)cip, (2*num+1)*sizeof(cell)); + tptr += (2*num+1) * sizeof(cell); + cip += (2*num+1) * sizeof(cell); + break; + } + default: + { + assert(0); + } +#undef DBGPARAM + } + } + cod.codesize = (uint32_t)(tptr - tbase); + sfwrite(&cod, sizeof(cod), 1, fp); + sfwrite(tbase, cod.codesize, 1, fp); + free(tbase); + + /* backtrack and write this section's header info */ + curoffs = (uint32_t)ftell(fp); + fseek(fp, offsets[0], SEEK_SET); + sfwrite(&lastsection, sizeof(lastsection), 1, fp); + cod.codesize += sizeof(cod); + sfwrite(&cod.codesize, sizeof(cod.codesize), 1, fp); + fseek(fp, curoffs, SEEK_SET); + lastsection = curoffs; + } + + if (strcmp(tables[1], ".data") == 0) + { + sp_file_data_t dat; + unsigned char *dbase; + + dat.datasize = hdr->hea - hdr->dat; + dat.memsize = hdr->stp; + dat.data = sizeof(dat); + dbase = (unsigned char *)hdr + hdr->dat; + + sfwrite(&dat, sizeof(dat), 1, fp); + if (dat.datasize) + { + sfwrite(dbase, dat.datasize, 1, fp); + } + + /* backtrack and write this section's header info */ + curoffs = ftell(fp); + fseek(fp, offsets[1], SEEK_SET); + sfwrite(&lastsection, sizeof(lastsection), 1, fp); + dat.datasize += sizeof(dat); + sfwrite(&dat.datasize, sizeof(dat.datasize),1, fp); + fseek(fp, curoffs, SEEK_SET); + lastsection = curoffs; + } + + fclose(fp); + + return 0; + +write_error: + pc_printf("Error writing to file: %s", bin_file->name); + unlink(bin_file->name); + fclose(fp); + + return 1; + } + + return 1; +} + +void sfwrite(const void *buf, size_t size, size_t count, FILE *fp) +{ + if (fwrite(buf, size, count, fp) != count) + { + longjmp(brkout, 1); + } } diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 95913675..5ba72230 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -629,14 +629,10 @@ static void initglobals(void) warnnum=0; /* number of warnings */ optproccall=FALSE; /* sourcemod: do not support "procedure call" */ verbosity=1; /* verbosity level, no copyright banner */ - sc_debug=sSYMBOLIC; /* sourcemod: full debug stuff */ - pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */ + sc_debug=sCHKBOUNDS|sSYMBOLIC; /* sourcemod: full debug stuff */ + pc_optimize=sOPTIMIZE_DEFAULT; /* sourcemod: full optimization */ sc_packstr=FALSE; /* strings are unpacked by default */ - #if AMX_COMPACTMARGIN > 2 - sc_compress=TRUE; /* compress output bytecodes */ - #else - sc_compress=FALSE; - #endif + sc_compress=FALSE; /* always disable compact encoding! */ sc_needsemicolon=FALSE;/* semicolon required to terminate expressions? */ sc_dataalign=sizeof(cell); pc_stksize=sDEF_AMXSTACK;/* default stack size */ @@ -1132,7 +1128,7 @@ static void about(void) pc_printf("Options:\n"); pc_printf(" -A alignment in bytes of the data segment and the stack\n"); pc_printf(" -a output assembler code\n"); -#if AMX_COMPACTMARGIN > 2 +#if 0 /* not toggleable in SourceMod */ pc_printf(" -C[+/-] compact encoding for output file (default=%c)\n", sc_compress ? '+' : '-'); #endif pc_printf(" -c codepage name or number; e.g. 1252 for Windows Latin-1\n"); diff --git a/compiler-init/sc2.c b/compiler-init/sc2.c index 1a822f5f..5cc1735f 100644 --- a/compiler-init/sc2.c +++ b/compiler-init/sc2.c @@ -1076,10 +1076,12 @@ static int command(void) if (find_constval(&libname_tab,name,0)==NULL) curlibrary=append_constval(&libname_tab,name,0,0); } /* if */ +#if 0 /* more unused */ } else if (strcmp(str,"pack")==0) { cell val; preproc_expr(&val,NULL); /* default = packed/unpacked */ sc_packstr=(int)val; +#endif } else if (strcmp(str,"rational")==0) { char name[sNAMEMAX+1]; cell digits=0; diff --git a/compiler-init/sc6.c b/compiler-init/sc6.c index 9dd5a169..2ea5f2d4 100644 --- a/compiler-init/sc6.c +++ b/compiler-init/sc6.c @@ -47,7 +47,7 @@ typedef struct { char *name; int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */ OPCODE_PROC func; -} OPCODE; +} OPCODEC; static cell codeindex; /* similar to "code_idx" */ static cell *lbltab; /* label table */ @@ -454,7 +454,7 @@ static cell do_case(FILE *fbin,char *params,cell opcode) return opcodes(0)+opargs(2); } -static OPCODE opcodelist[] = { +static OPCODEC opcodelist[] = { /* node for "invalid instruction" */ { 0, NULL, 0, noop }, /* opcodes in sorted order */ diff --git a/compiler-init/sp_file.h b/compiler-init/sp_file.h new file mode 100644 index 00000000..bd777ad0 --- /dev/null +++ b/compiler-init/sp_file.h @@ -0,0 +1,102 @@ +#ifndef _INCLUDE_SPFILE_H +#define _INCLUDE_SPFILE_H + +#include +#if defined __GNUC__ || defined HAVE_STDINT_ +#include +#else + #if !defined HAVE_STDINT_H + typedef unsigned __int64 uint64_t; + typedef __int64 int64_t; + typedef unsigned __int32 uint32_t; + typedef __int32 int32_t; + typedef unsigned __int16 uint16_t; + typedef __int16 int16_t; + typedef unsigned __int8 uint8_t; + typedef __int8 int8_t; + #define HAVE_STDINT_H + #endif +#endif + +#define SPFILE_MAGIC 0xDEADC0D3 +#define SPFILE_VERSION 0x0100 + +//:TODO: better compiler/nix support +#if defined __linux__ + #pragma pack(1) /* structures must be packed (byte-aligned) */ +#else + #pragma pack(push) + #pragma pack(1) /* structures must be packed (byte-aligned) */ +#endif + +typedef struct sp_file_section_s +{ + uint32_t nameoffs; /* rel offset into global string table */ + uint32_t dataoffs; + uint32_t size; +} sp_file_section_t; + +typedef struct sp_file_hdr_s +{ + uint32_t magic; + uint16_t version; + uint32_t imagesize; + uint8_t sections; + uint32_t stringtab; +} sp_file_hdr_t; + +typedef enum +{ + SP_FILE_NONE = 0, + SP_FILE_DEBUG = 1, +} sp_file_flags_t; + +/* section is ".code" */ +typedef struct sp_file_code_s +{ + uint32_t codesize; /* codesize in bytes */ + uint8_t cellsize; /* cellsize in bytes */ + uint8_t codeversion; /* version of opcodes supported */ + uint16_t flags; /* flags */ + uint32_t code; /* rel offset to code */ +} sp_file_code_t; + +/* section is .data */ +typedef struct sp_file_data_s +{ + uint32_t datasize; /* size of data section */ + uint32_t memsize; /* total mem required (includes data) */ + uint32_t data; /* file offset to data (helper) */ +} sp_file_data_t; + +/* section is .publics */ +typedef struct sp_file_publics_s +{ + uint32_t address; /* address rel to code section */ + uint32_t name; /* index into nametable */ +} sp_file_publics_t; + +/* section is .natives */ +typedef struct sp_file_natives_s +{ + uint32_t name; /* name of native at index */ +} sp_file_natives_t; + +/* section is .pubvars */ +typedef struct sp_file_pubvars_s +{ + uint32_t address; /* address rel to dat section */ + uint32_t name; /* index into nametable */ +} sp_file_pubvars_t; + +#if defined __linux__ + #pragma pack() /* reset default packing */ +#else + #pragma pack(pop) /* reset previous packing */ +#endif + + +/* section is .names */ +typedef char * sp_file_nametab_t; + +#endif //_INCLUDE_SPFILE_H diff --git a/compiler-init/spcomp.vcproj b/compiler-init/spcomp.vcproj index 092d3a57..9653b567 100644 --- a/compiler-init/spcomp.vcproj +++ b/compiler-init/spcomp.vcproj @@ -250,6 +250,10 @@ RelativePath=".\scvars.c" > + + + + From 4ee1db9644db40147806d48f7f1b6084f4541c77 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 15 Jul 2006 01:39:33 +0000 Subject: [PATCH 16/20] fixed being able to secretly set -C --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4019 --- compiler-init/sc1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 5ba72230..2f10b39a 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -758,6 +758,7 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam if (verbosity>1) verbosity=1; break; +#if 0 /* not allowed in SourceMod */ case 'C': #if AMX_COMPACTMARGIN > 2 sc_compress=toggle_option(ptr,sc_compress); @@ -765,6 +766,7 @@ static void parseoptions(int argc,char **argv,char *oname,char *ename,char *pnam about(); #endif break; +#endif case 'c': strlcpy(codepage,option_value(ptr),MAXCODEPAGE); /* set name of codepage */ break; From 9d384ab39146776861a48026746e87eec6c1e65b Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 15 Jul 2006 03:05:56 +0000 Subject: [PATCH 17/20] finished compiler output format --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4020 --- compiler-init/pawncc.c | 133 ++++++++++++++++++++++++++++++++++++++-- compiler-init/sp_file.h | 1 + 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/compiler-init/pawncc.c b/compiler-init/pawncc.c index 9e834d94..53ba7067 100644 --- a/compiler-init/pawncc.c +++ b/compiler-init/pawncc.c @@ -89,7 +89,10 @@ int main(int argc, char *argv[]) lastsection = curoffs; - /** write the code table */ + /** + * Begin writing each of our known tables out + */ + if (strcmp(tables[0], ".code") == 0) { sp_file_code_t cod; @@ -109,6 +112,7 @@ int main(int argc, char *argv[]) cod.flags |= SP_FILE_DEBUG; } cod.code = sizeof(cod); + cod.main = hdr->cip; /* write the code in our newer format */ cbase = (unsigned char *)hdr + hdr->cod; @@ -308,7 +312,6 @@ int main(int argc, char *argv[]) case OP_CASETBL: { cell num; - int i; DBGPARAM(*(cell *)tptr); num = *(cell *)tptr; tptr += sizeof(cell); @@ -332,9 +335,9 @@ int main(int argc, char *argv[]) /* backtrack and write this section's header info */ curoffs = (uint32_t)ftell(fp); fseek(fp, offsets[0], SEEK_SET); - sfwrite(&lastsection, sizeof(lastsection), 1, fp); + sfwrite(&lastsection, sizeof(uint32_t), 1, fp); cod.codesize += sizeof(cod); - sfwrite(&cod.codesize, sizeof(cod.codesize), 1, fp); + sfwrite(&cod.codesize, sizeof(uint32_t), 1, fp); fseek(fp, curoffs, SEEK_SET); lastsection = curoffs; } @@ -358,9 +361,127 @@ int main(int argc, char *argv[]) /* backtrack and write this section's header info */ curoffs = ftell(fp); fseek(fp, offsets[1], SEEK_SET); - sfwrite(&lastsection, sizeof(lastsection), 1, fp); + sfwrite(&lastsection, sizeof(uint32_t), 1, fp); dat.datasize += sizeof(dat); - sfwrite(&dat.datasize, sizeof(dat.datasize),1, fp); + sfwrite(&dat.datasize, sizeof(uint32_t),1, fp); + fseek(fp, curoffs, SEEK_SET); + lastsection = curoffs; + } + + if (strcmp(tables[2], ".publics") == 0) + { + sp_file_publics_t *pbtbl; + AMX_FUNCSTUBNT *stub; + uint32_t publics = (hdr->natives - hdr->publics) / hdr->defsize; + + pbtbl = (sp_file_publics_t *)malloc(sizeof(sp_file_publics_t) * publics); + stub = (AMX_FUNCSTUBNT *)((unsigned char *)hdr + hdr->publics); + + for (i=0; iaddress; + pbtbl[i].name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); + + stub += hdr->defsize; + } + if (publics) + { + sfwrite(pbtbl, sizeof(sp_file_publics_t), publics, fp); + } + free(pbtbl); + + /* backtrack and write section's header info */ + curoffs = ftell(fp); + fseek(fp, offsets[2], SEEK_SET); + sfwrite(&lastsection, sizeof(uint32_t), 1, fp); + publics *= sizeof(sp_file_publics_t); + sfwrite(&publics, sizeof(uint32_t), 1, fp); + fseek(fp, curoffs, SEEK_SET); + lastsection = curoffs; + } + + if (strcmp(tables[3], ".pubvars") == 0) + { + sp_file_pubvars_t *pbvars; + AMX_FUNCSTUBNT *stub; + uint32_t pubvars = (hdr->tags - hdr->pubvars) / hdr->defsize; + + pbvars = (sp_file_pubvars_t *)malloc(sizeof(sp_file_pubvars_t) * pubvars); + stub = (AMX_FUNCSTUBNT *)((unsigned char *)hdr + hdr->pubvars); + + for (i=0; iaddress; + pbvars[i].name = stub->nameofs - (hdr->nametable + sizeof(uint16_t)); + + stub += hdr->defsize; + } + if (pubvars) + { + sfwrite(pbvars, sizeof(sp_file_pubvars_t), pubvars, fp); + } + free(pbvars); + + /* backtrack and write section's header info */ + curoffs = ftell(fp); + fseek(fp, offsets[3], SEEK_SET); + sfwrite(&lastsection, sizeof(uint32_t), 1, fp); + pubvars *= sizeof(sp_file_pubvars_t); + sfwrite(&pubvars, sizeof(uint32_t), 1, fp); + fseek(fp, curoffs, SEEK_SET); + lastsection = curoffs; + } + + if (strcmp(tables[4], ".natives") == 0) + { + sp_file_natives_t *nvtbl; + AMX_FUNCSTUBNT *stub; + uint32_t natives = (hdr->libraries - hdr->natives) / hdr->defsize; + + nvtbl = (sp_file_natives_t *)malloc(sizeof(sp_file_natives_t) * natives); + stub = (AMX_FUNCSTUBNT *)((unsigned char *)hdr + hdr->natives); + + for (i=0; inameofs - (hdr->nametable + sizeof(uint16_t)); + + stub += hdr->defsize; + } + if (natives) + { + sfwrite(nvtbl, sizeof(sp_file_natives_t), natives, fp); + } + free(nvtbl); + + /* backtrack and write header */ + curoffs = ftell(fp); + fseek(fp, offsets[4], SEEK_SET); + sfwrite(&lastsection, sizeof(uint32_t), 1, fp); + natives *= sizeof(sp_file_natives_t); + sfwrite(&natives, sizeof(uint32_t), 1, fp); + fseek(fp, curoffs, SEEK_SET); + lastsection = curoffs; + } + + if (strcmp(tables[5], ".names") == 0) + { + unsigned char *base; + uint32_t namelen; + + /* write the entire block */ + base = (unsigned char *)hdr + hdr->nametable + sizeof(uint16_t); + /** + * note - the name table will be padded to sizeof(cell) bytes. + * this may clip at most an extra three bytes in! + */ + namelen = hdr->cod - hdr->nametable; + sfwrite(base, namelen, 1, fp); + + /* backtrack and write header */ + curoffs = ftell(fp); + fseek(fp, offsets[5], SEEK_SET); + sfwrite(&lastsection, sizeof(uint32_t), 1, fp); + sfwrite(&namelen, sizeof(uint32_t), 1, fp); fseek(fp, curoffs, SEEK_SET); lastsection = curoffs; } diff --git a/compiler-init/sp_file.h b/compiler-init/sp_file.h index bd777ad0..59bddb29 100644 --- a/compiler-init/sp_file.h +++ b/compiler-init/sp_file.h @@ -58,6 +58,7 @@ typedef struct sp_file_code_s uint8_t cellsize; /* cellsize in bytes */ uint8_t codeversion; /* version of opcodes supported */ uint16_t flags; /* flags */ + uint32_t main; /* address to "main" if any */ uint32_t code; /* rel offset to code */ } sp_file_code_t; From a546f9ba0508603344947e1ebf118e881d35a441 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 15 Jul 2006 03:24:45 +0000 Subject: [PATCH 18/20] frees memory on exit fixed bug where autozero was defaulted to nozero --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4022 --- compiler-init/pawncc.c | 3 +++ compiler-init/sc1.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler-init/pawncc.c b/compiler-init/pawncc.c index 53ba7067..d7c40653 100644 --- a/compiler-init/pawncc.c +++ b/compiler-init/pawncc.c @@ -495,6 +495,9 @@ write_error: unlink(bin_file->name); fclose(fp); + memfile_destroy(bin_file); + bin_file = NULL; + return 1; } diff --git a/compiler-init/sc1.c b/compiler-init/sc1.c index 8a8f07d0..27ce05dc 100644 --- a/compiler-init/sc1.c +++ b/compiler-init/sc1.c @@ -135,7 +135,7 @@ static int *readwhile(void); static void inst_datetime_defines(void); static int norun = 0; /* the compiler never ran */ -static int autozero = 0; /* if 1 will zero out the variable, if 0 omit the zeroing */ +static int autozero = 1; /* if 1 will zero out the variable, if 0 omit the zeroing */ static int lastst = 0; /* last executed statement type */ static int nestlevel = 0; /* number of active (open) compound statements */ static int rettype = 0; /* the type that a "return" expression should have */ From 8543afd6042459e7b55ae289fa1a24835fa76d95 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 15 Jul 2006 04:51:28 +0000 Subject: [PATCH 19/20] added compression flags into header, compiler now finalized --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4023 --- compiler-init/pawncc.c | 47 +- compiler-init/sp_file.h | 9 +- compiler-init/spcomp.vcproj | 106 +- compiler-init/zlib/adler32.c | 149 +++ compiler-init/zlib/compress.c | 79 ++ compiler-init/zlib/crc32.c | 423 ++++++++ compiler-init/zlib/crc32.h | 441 +++++++++ compiler-init/zlib/deflate.c | 1736 +++++++++++++++++++++++++++++++++ compiler-init/zlib/deflate.h | 331 +++++++ compiler-init/zlib/gzio.c | 1026 +++++++++++++++++++ compiler-init/zlib/infback.c | 623 ++++++++++++ compiler-init/zlib/inffast.c | 318 ++++++ compiler-init/zlib/inffast.h | 11 + compiler-init/zlib/inffixed.h | 94 ++ compiler-init/zlib/inflate.c | 1368 ++++++++++++++++++++++++++ compiler-init/zlib/inflate.h | 115 +++ compiler-init/zlib/inftrees.c | 329 +++++++ compiler-init/zlib/inftrees.h | 55 ++ compiler-init/zlib/trees.c | 1219 +++++++++++++++++++++++ compiler-init/zlib/trees.h | 128 +++ compiler-init/zlib/uncompr.c | 61 ++ compiler-init/zlib/zconf.h | 281 ++++++ compiler-init/zlib/zlib.h | 1357 ++++++++++++++++++++++++++ compiler-init/zlib/zutil.c | 318 ++++++ compiler-init/zlib/zutil.h | 269 +++++ 25 files changed, 10882 insertions(+), 11 deletions(-) create mode 100644 compiler-init/zlib/adler32.c create mode 100644 compiler-init/zlib/compress.c create mode 100644 compiler-init/zlib/crc32.c create mode 100644 compiler-init/zlib/crc32.h create mode 100644 compiler-init/zlib/deflate.c create mode 100644 compiler-init/zlib/deflate.h create mode 100644 compiler-init/zlib/gzio.c create mode 100644 compiler-init/zlib/infback.c create mode 100644 compiler-init/zlib/inffast.c create mode 100644 compiler-init/zlib/inffast.h create mode 100644 compiler-init/zlib/inffixed.h create mode 100644 compiler-init/zlib/inflate.c create mode 100644 compiler-init/zlib/inflate.h create mode 100644 compiler-init/zlib/inftrees.c create mode 100644 compiler-init/zlib/inftrees.h create mode 100644 compiler-init/zlib/trees.c create mode 100644 compiler-init/zlib/trees.h create mode 100644 compiler-init/zlib/uncompr.c create mode 100644 compiler-init/zlib/zconf.h create mode 100644 compiler-init/zlib/zlib.h create mode 100644 compiler-init/zlib/zutil.c create mode 100644 compiler-init/zlib/zutil.h diff --git a/compiler-init/pawncc.c b/compiler-init/pawncc.c index d7c40653..2db16e1f 100644 --- a/compiler-init/pawncc.c +++ b/compiler-init/pawncc.c @@ -6,6 +6,7 @@ #include "sp_file.h" #include "amx.h" #include "osdefs.h" +#include "zlib/zlib.h" #define NUM_SECTIONS 6 @@ -328,6 +329,8 @@ int main(int argc, char *argv[]) } } cod.codesize = (uint32_t)(tptr - tbase); + cod.disksize = cod.codesize; + cod.compression = SPFILE_COMPRESSION_NONE; sfwrite(&cod, sizeof(cod), 1, fp); sfwrite(tbase, cod.codesize, 1, fp); free(tbase); @@ -346,24 +349,58 @@ int main(int argc, char *argv[]) { sp_file_data_t dat; unsigned char *dbase; + Bytef *cmp_dbase; + uLong disksize; + int err; dat.datasize = hdr->hea - hdr->dat; dat.memsize = hdr->stp; dat.data = sizeof(dat); - dbase = (unsigned char *)hdr + hdr->dat; + dat.compression = SPFILE_COMPRESSION_GZ; - sfwrite(&dat, sizeof(dat), 1, fp); if (dat.datasize) { - sfwrite(dbase, dat.datasize, 1, fp); + dat.disksize = (uint32_t)compressBound((uLong)dat.datasize); + + dbase = (unsigned char *)hdr + hdr->dat; + cmp_dbase = (Bytef *)malloc(dat.disksize); + + /* compress */ + err = compress2(cmp_dbase, &disksize, (Bytef *)dbase, dat.datasize, Z_BEST_COMPRESSION); + + if (err != Z_OK) + { + pc_printf("Failed to compress DAT section with error: %d\n", err); + pc_printf("Defaulting to no compression.\n"); + dat.compression = SPFILE_COMPRESSION_NONE; + dat.disksize = dat.datasize; + + /* write header */ + sfwrite(&dat, sizeof(dat), 1, fp); + /* write data */ + sfwrite(&dbase, dat.datasize, 1, fp); + } else { + dat.disksize = (uint32_t)disksize; + + /* write header */ + sfwrite(&dat, sizeof(dat), 1, fp); + /* write data */ + sfwrite(&cmp_dbase, dat.disksize, 1, fp); + } + + free(cmp_dbase); + } else { + /* should be 0 */ + dat.disksize = dat.datasize; + sfwrite(&dat, sizeof(dat), 1, fp); } /* backtrack and write this section's header info */ curoffs = ftell(fp); fseek(fp, offsets[1], SEEK_SET); sfwrite(&lastsection, sizeof(uint32_t), 1, fp); - dat.datasize += sizeof(dat); - sfwrite(&dat.datasize, sizeof(uint32_t),1, fp); + disksize += sizeof(dat); + sfwrite(&disksize, sizeof(uint32_t),1, fp); fseek(fp, curoffs, SEEK_SET); lastsection = curoffs; } diff --git a/compiler-init/sp_file.h b/compiler-init/sp_file.h index 59bddb29..b0aeb2c3 100644 --- a/compiler-init/sp_file.h +++ b/compiler-init/sp_file.h @@ -59,14 +59,21 @@ typedef struct sp_file_code_s uint8_t codeversion; /* version of opcodes supported */ uint16_t flags; /* flags */ uint32_t main; /* address to "main" if any */ + uint32_t disksize; /* disksize in bytes */ + uint32_t compression; /* compression */ uint32_t code; /* rel offset to code */ } sp_file_code_t; +#define SPFILE_COMPRESSION_NONE 0 +#define SPFILE_COMPRESSION_GZ 1 + /* section is .data */ typedef struct sp_file_data_s { - uint32_t datasize; /* size of data section */ + uint32_t datasize; /* size of data section in memory */ uint32_t memsize; /* total mem required (includes data) */ + uint32_t disksize; /* size of data on disk (compressed) */ + uint8_t compression; /* compression */ uint32_t data; /* file offset to data (helper) */ } sp_file_data_t; diff --git a/compiler-init/spcomp.vcproj b/compiler-init/spcomp.vcproj index 9653b567..be9cafcc 100644 --- a/compiler-init/spcomp.vcproj +++ b/compiler-init/spcomp.vcproj @@ -115,7 +115,7 @@ /> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/compiler-init/zlib/adler32.c b/compiler-init/zlib/adler32.c new file mode 100644 index 00000000..f201d670 --- /dev/null +++ b/compiler-init/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/compiler-init/zlib/compress.c b/compiler-init/zlib/compress.c new file mode 100644 index 00000000..d37e84f5 --- /dev/null +++ b/compiler-init/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/compiler-init/zlib/crc32.c b/compiler-init/zlib/crc32.c new file mode 100644 index 00000000..32814c20 --- /dev/null +++ b/compiler-init/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/compiler-init/zlib/crc32.h b/compiler-init/zlib/crc32.h new file mode 100644 index 00000000..5de49bc9 --- /dev/null +++ b/compiler-init/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/compiler-init/zlib/deflate.c b/compiler-init/zlib/deflate.c new file mode 100644 index 00000000..529f716b --- /dev/null +++ b/compiler-init/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/compiler-init/zlib/deflate.h b/compiler-init/zlib/deflate.h new file mode 100644 index 00000000..222c53e0 --- /dev/null +++ b/compiler-init/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/compiler-init/zlib/gzio.c b/compiler-init/zlib/gzio.c new file mode 100644 index 00000000..5e20a4aa --- /dev/null +++ b/compiler-init/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/compiler-init/zlib/infback.c b/compiler-init/zlib/infback.c new file mode 100644 index 00000000..1e03e1ba --- /dev/null +++ b/compiler-init/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/compiler-init/zlib/inffast.c b/compiler-init/zlib/inffast.c new file mode 100644 index 00000000..fa31cad9 --- /dev/null +++ b/compiler-init/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/compiler-init/zlib/inffast.h b/compiler-init/zlib/inffast.h new file mode 100644 index 00000000..614fa787 --- /dev/null +++ b/compiler-init/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/compiler-init/zlib/inffixed.h b/compiler-init/zlib/inffixed.h new file mode 100644 index 00000000..423d5c5b --- /dev/null +++ b/compiler-init/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/compiler-init/zlib/inflate.c b/compiler-init/zlib/inflate.c new file mode 100644 index 00000000..33ea9029 --- /dev/null +++ b/compiler-init/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/compiler-init/zlib/inflate.h b/compiler-init/zlib/inflate.h new file mode 100644 index 00000000..fbbc8714 --- /dev/null +++ b/compiler-init/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/compiler-init/zlib/inftrees.c b/compiler-init/zlib/inftrees.c new file mode 100644 index 00000000..38ded81c --- /dev/null +++ b/compiler-init/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/compiler-init/zlib/inftrees.h b/compiler-init/zlib/inftrees.h new file mode 100644 index 00000000..dc0fd567 --- /dev/null +++ b/compiler-init/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/compiler-init/zlib/trees.c b/compiler-init/zlib/trees.c new file mode 100644 index 00000000..7a048028 --- /dev/null +++ b/compiler-init/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/compiler-init/zlib/trees.h b/compiler-init/zlib/trees.h new file mode 100644 index 00000000..1ca868b8 --- /dev/null +++ b/compiler-init/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/compiler-init/zlib/uncompr.c b/compiler-init/zlib/uncompr.c new file mode 100644 index 00000000..ad6db0a6 --- /dev/null +++ b/compiler-init/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/compiler-init/zlib/zconf.h b/compiler-init/zlib/zconf.h new file mode 100644 index 00000000..59b5e7a6 --- /dev/null +++ b/compiler-init/zlib/zconf.h @@ -0,0 +1,281 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/compiler-init/zlib/zlib.h b/compiler-init/zlib/zlib.h new file mode 100644 index 00000000..62d0e467 --- /dev/null +++ b/compiler-init/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/compiler-init/zlib/zutil.c b/compiler-init/zlib/zutil.c new file mode 100644 index 00000000..0f4bd787 --- /dev/null +++ b/compiler-init/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/compiler-init/zlib/zutil.h b/compiler-init/zlib/zutil.h new file mode 100644 index 00000000..0ba6e020 --- /dev/null +++ b/compiler-init/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ From 5151880f2785af4e81a333524fb3f4b750b8ab02 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Sat, 15 Jul 2006 05:02:38 +0000 Subject: [PATCH 20/20] final build --HG-- branch : dvander extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/branches/dvander%4024 --- compiler-init/spcomp.vcproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler-init/spcomp.vcproj b/compiler-init/spcomp.vcproj index be9cafcc..db52d001 100644 --- a/compiler-init/spcomp.vcproj +++ b/compiler-init/spcomp.vcproj @@ -43,7 +43,7 @@ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE" MinimalRebuild="true" BasicRuntimeChecks="3" - RuntimeLibrary="3" + RuntimeLibrary="1" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="false" @@ -115,8 +115,8 @@ />