home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GAMES
/
informosk.lha
/
inform.c
< prev
next >
Wrap
Text File
|
1993-11-27
|
159KB
|
4,231 lines
/* -------------------------------------------------------------------------------- */
/* Inform: Version 3 Z-code compiler */
/* */
/* (c) Graham Nelson, 1993 */
/* A manual for this language is available from the if-archive at ftp.gmd.de. */
/* Please read the legal note below. */
/* -------------------------------------------------------------------------------- */
#define RELEASE_STRING "Release 3 (November 16th 1993)"
#define RELEASE_NUMBER 796
/* -------------------------------------------------------------------------------- */
/* Our machine for today is... */
/* */
/* [ Inform should compile and work without trouble if you simply: */
/* */
/* #define ARCHIMEDES - Norcroft C for the Acorn Archimedes */
/* (link with ansilib only, no need for RISC_OSlib) */
/* #define UNIX - gcc under Unix (see below) */
/* #define VAX - for Digital's VAX C ] */
/* */
/* #define PC sets up sensible definitions for IBM PCs, but I don't yet know */
/* whether Inform then works. */
/* */
/* (If no machine is defined, then cautious #defines will be made.) */
/* -------------------------------------------------------------------------------- */
#define UNIX
#define ALLOCATE_BIG_ARRAYS
/* -------------------------------------------------------------------------------- */
/* The other #definable options (some of them set by the above) are: */
/* */
/* USE_TEMPORARY_FILES - use scratch files for workspace rather than memory */
/* ALLOCATE_BIG_ARRAYS - use calloc() for arrays rather than global variables */
/* PROMPT_INPUT - prompt input rather than use Unix-style command line */
/* TIME_UNAVAILABLE - don't use ANSI time routines to work out today's date */
/* ARC_PROFILING - do clumsy profiling in Norcroft C on the Archimedes */
/* GRAHAM - for Graham's machine only */
/* -------------------------------------------------------------------------------- */
/* */
/* Any use of this program may be made, provided that no profit is involved and */
/* that this message is preserved in all modified versions; see the documentation */
/* for fuller legal details. Note that it is not public domain. */
/* */
/* -------------------------------------------------------------------------------- */
/* Hello, Porter! */
/* */
/* The code is in ANSI C. At present it assumes that at least long integers are */
/* 32 bit, though ordinary integers can be either 16 or 32 bit, and stored in */
/* either order (high to low or vice versa). It no longer uses strtoul, and can */
/* manage without strftime. See the details below. */
/* */
/* To use Inform (once it's compiled) you need about 700K maximum of filespace */
/* (much less for small games) and at most 200K of spare memory (i.e., memory */
/* not physically occupied by Inform). */
/* */
/* If you succeed in porting Inform to a new compiler, please email the author */
/* to say how, so that whatever you did can be incorporated in the next release. */
/* -------------------------------------------------------------------------------- */
/* UNIX port (by Dilip Sequeira): The right optimisation to use is */
/* gcc -O2 -fwritable-strings -finline-functions -fomit-frame-pointer inform.c */
/* (making an executable of size about 100K on a SparcStation II). */
/* The temporary files option is not on by default, but if it is used, temporary */
/* file names are used which contain the process ID. */
/* -------------------------------------------------------------------------------- */
/* */
/* The first archive release (0.5) was on April 30th 1993. */
/* The second archive release (0.6) had the following improvements: */
/* */
/* One #ifdef ARCHIMEDES altered to correct a bug in non-Archimedes version */
/* (the Acorn Archimedes A5000 being the author's make of computer) */
/* Checking on the MAX_ACTIONS limit put in ("Curses" finally exceeded 100!) */
/* Checking on MAX_STATIC_STRINGS put in; -m information extended */
/* -x (hash printing) option introduced */
/* -a (list assembly lines only) option, and ATRACE/NOATRACE introduced */
/* Void prototypes explicitly declared (void) */
/* Defunct Inform directives "STRING" and "SET" removed */
/* Opcode data now made static, and faster opcode-parsing routine put in */
/* Preprocessor stack rewritten, and now checking for overflow */
/* Showdict produces more useful output */
/* Filename extension #defines added */
/* Command line parsing improved */
/* */
/* Some ASCII-esque assumptions in the first edition are now removed; */
/* tolower and toupper are used more cautiously and it should be possible to */
/* port to EBCDIC and other monstrosities, by altering the "character set" */
/* routines below */
/* */
/* The first edition presumed integers to be 32-bits long; */
/* some typedefs below attempt to force this on an otherwise unwilling compiler */
/* (but will give up with an error if even long ints are only 16-bit) */
/* */
/* The main improvement over the first edition is in memory management which */
/* has been heavily reformed, at the expense of a certain charm. It can now */
/* malloc() less than 75K memory (as opposed to over 800K before!). (See below) */
/* */
/* USE_TEMPORARY_FILES version: if this is #defined, scratch files amounting */
/* to at most about 100K and 50K respectively are used to hold partial */
/* results; this saves about another 150K. */
/* (At worst three files are simultaneously open under this regime.) */
/* The temporary file names are #define'd below. They are automatically */
/* deleted. */
/* */
/* The third release (1.0) has been generally tidied up and reorganised: most */
/* of the sillier variable and routine names have been made more comprehensible. */
/* It is also 3 to 6 times faster; I wish to thank Dilip Sequeira for giving */
/* me profiling output, and also David Moore for his... comments. */
/* */
/* The program itself has the following improvements: */
/* */
/* @xx string indirection via the synonyms table added */
/* Objects allowed to have multiple internal names */
/* New constant form #n$word... added */
/* And #r$routine... */
/* New high-level commands "write" and "give" for easier object alteration */
/* Fatal errors fractionally more informative */
/* Non-fatal errors quite a lot more informative, and more sensibly worded */
/* Grievous bug in stack long slot routines fixed */
/* The checksum and length words are now properly set (though few interpreters */
/* need them) */
/* Error checking on exceeding MAX_VERBS */
/* -e (economy mode) added: causes abbreviations to be worked out, slowly */
/* #SWITCHES directive added */
/* -i (ignore switches) and -o (print offsets) added */
/* Checking added on whether routines have too many local variables (the */
/* Z-machine crashes in a very strange way if so!) */
/* Minor bug in printing object tree fixed */
/* Two unused bytes spare at end of property defaults table are now zeroed */
/* Temporary files now deleted after use */
/* Checking on excessively long variable names added */
/* STATUSLINE directive added (for games with hours/minutes on the status line) */
/* The former SMALL_MEMORY compilation option is now mandatory. (Previously, */
/* Inform could be compiled so that it read source files into an enormous */
/* buffer, rather than reading them twice through a bit at a time. This */
/* could only be useful on machines with huge memory and very slow filing */
/* systems, of which there are few, and it complicated the code.) */
/* The way input file names are processed has been reformed: they are now not */
/* altered if they contain a '.' or a '/' */
/* INCLUDE directive added, so that Inform #includes files like C */
/* Old -p (both passes) directive renamed -b, and new -p (percentage breakdown) */
/* Warnings added: variables not used; checking that Main behaves properly; */
/* small bug in line counting fixed; checking on number of function arguments */
/* Meta-verbs added */
/* -f (frequencies) and -t (assembly trace) switches added */
/* Small bug to do with stubbed routines removed */
/* Possibly unused bytes (due to word alignment) in data, now zeroed */
/* (so that different machines will not produce different game files) */
/* -f now calculates bytes yielded by abbreviations */
/* New SERIAL directive for machines without access to today's date */
/* Now handles more complicated multiple expressions within the same command */
/* New STRING command added for writing to the synonyms table */
/* New FONT command for proportional fonts control */
/* New DEFAULT and STUB directives, for stubbing undeclared CONSTANTs and code */
/* Checking on no. of attributes and properties added, and property-counting */
/* */
/* Speed improvements in the third release: */
/* */
/* The following have been rewritten in the interests of speed and generally */
/* not being O(n^2) for the sake of it: the line reader and tokeniser, */
/* management of local variables, the dictionary builder, the text */
/* translator, the line parser and the symbols table (courtesy of hash coding */
/* by Dilip). */
/* Curses Dejavu (compiling times in seconds */
/* on my machine) */
/* Release 2... 300 45 (including 1-2 seconds for */
/* Tokeniser & locals 205 26 printing statistics) */
/* Dictionary 89 19 */
/* Symbols hashing 74 17 */
/* Tokeniser II 69 16 */
/* Abbreviations 55 16 */
/* Hashing reserveds 49 14 */
/* */
/* Compatibility improvements in the third release: */
/* */
/* The sort_number routine has been rewritten at the suggestion of Jon Drukman */
/* in order to defend against compilers determined to sign chars; and so have */
/* some structure definitions and variable types */
/* Subtraction of pointers is now done by an easily altered macro (the point */
/* being that you can't always subtract by casting to int, if int is 16 bit) */
/* File naming improved slightly */
/* The two points where ASCII is used now go through translate_to_ascii */
/* Some stupid alterations made for VAX C compatibility */
/* (in the idiot world of VAX C, # commands must start on column 1, */
/* x=-1 is read as x-=1, typedef isn't ANSI, the word "signed" is rejected, */
/* values like MAX_INT are wrongly set and string consts don't concatenate) */
/* A general rewrite has been made to sort out 16-bit from 32-bit integers: */
/* Inform now properly works when int is 16 bit by default. */
/* VAX version now working (so presumably Inform does not rely on the order of */
/* bytes in a word) */
/* Long constants explicitly declared so (to keep Borland C++ happy) */
/* */
/* Because some C compilers (especially PC ones) don't like large static arrays */
/* there's now an ALLOCATE_BIG_ARRAYS option (#define PC forces it) which uses */
/* calloc to allocate memory from the heap for them. */
/* */
/* Altogether Inform is going to need about 190K of workspace, and that's that: */
/* in a big flat memory machine, this will split about equally between static */
/* arrays and dynamic allocation. With ALLOCATE_BIG_ARRAYS set it will be */
/* almost entirely dynamically allocated. */
/* */
/* If PROMPT_INPUTS is defined (and the VAX and PC versions force this), Inform */
/* gets file names and options by prompting for keyboard input, rather than */
/* using a Unix-style command line. */
/* */
/* If TIME_UNAVAILABLE is defined, Inform doesn't try to use strftime and */
/* doesn't enter today's date for the serial number: the programmer will have */
/* to use a SERIAL directive in Inform, instead. */
/* -------------------------------------------------------------------------------- */
/* A rough tourist's map of this program: */
/* */
/* Comments */
/* #defines */
/* Integer types, local character set */
/* Structures */
/* Arrays */
/* Global variables */
/* */
/* Text translation Routines used throughout */
/* File handling (cast in order of appearance) */
/* Preprocessor stack */
/* Character-level parsing */
/* Error reporting */
/* Dictionary maker */
/* Symbols table maker */
/* Printing diagnostics */
/* Action maker */
/* */
/* Main Higher level routines */
/* Initialisation (second half; in logical order) */
/* Command line switches */
/* Top level line parser */
/* Compiler */
/* Expression evaluator */
/* Assembler directives */
/* Make objects */
/* Make globals */
/* Make verbs */
/* Line assembler */
/* Z-code database */
/* Construct output file */
/* */
/* -------------------------------------------------------------------------------- */
/* By setting up the prefixes and extensions in the definitions below, you should */
/* be able to get something sensible for your filing system. */
/* In the last resort, the clumsy "z3" prefix below is chosen to cause least */
/* offense to different filing systems. */
/* Note that if both Code_Prefix and Code_Extension are empty, then Inform may */
/* overwrite its source code with the object code... so don't allow this. */
/* (For Unix the extension is ".z3" rather than ".zip" to avoid looking like the */
/* file compression trailer...) */
/* */
/* On an Archimedes or a PC, set USE_TEMPORARY_FILES: otherwise don't, by default */
/* -------------------------------------------------------------------------------- */
#ifdef GRAHAM
#include "h.version"
#define ARCHIMEDES
#else
#define VNUMBER RELEASE_NUMBER
#endif
#ifdef ARCHIMEDES
#define MACHINE_STRING "Archimedes"
#define Source_Prefix "Zcode."
#define Source_Extension ""
#define Include_Prefix "Zcode.h."
#define Code_Prefix "Zgames."
#define Code_Extension ""
#define USE_TEMPORARY_FILES
#define Temp1_Name "ram:InfTemp1"
#define Temp2_Name "ram:InfTemp2"
#ifdef ARC_PROFILING
extern int _fmapstore(char *);
#endif
#endif
#ifdef UNIX
#define MACHINE_STRING "Unix"
#define Source_Prefix ""
#define Source_Extension ".inf"
#define Include_Extension ".h"
#define Code_Prefix ""
#define Code_Extension ".z3"
char Temp1_Name[50], Temp2_Name[50];
#define Temp1_Hdr "/tmp/InformTemp1"
#define Temp2_Hdr "/tmp/InformTemp2"
#endif
#ifdef PC
#define PROMPT_INPUT
#define MACHINE_STRING "PC"
#define Source_Prefix ""
#define Source_Extension ".inf"
#define Include_Extension ".h"
#define Code_Prefix ""
#define Code_Extension ".zip"
#define Temp1_Name "Inftmp1.tmp"
#define Temp2_Name "Inftmp2.tmp"
#define USE_TEMPORARY_FILES
#define ALLOCATE_BIG_ARRAYS
#endif
#ifdef VAX
#define PROMPT_INPUT
#define TIME_UNAVAILABLE
#define MACHINE_STRING "VAX"
#define Source_Prefix ""
#define Source_Extension ".inf"
#define Include_Extension ".h"
#define Code_Prefix ""
#define Code_Extension ".zip"
#define Temp1_Name "Inftmp1.tmp"
#define Temp2_Name "Inftmp2.tmp"
#endif
#ifndef Source_Prefix
#define Source_Prefix ""
#define Source_Extension ""
#define Code_Prefix "z3"
#define Code_Extension ""
#define Temp1_Name "Inftemp1"
#define Temp2_Name "Inftemp2"
#endif
#ifndef Include_Prefix
#define Include_Prefix Source_Prefix
#endif
#ifndef Include_Extension
#define Include_Extension Source_Extension
#endif
/* -------------------------------------------------------------------------------- */
/* What the #defines mean: */
/* */
/* 1. Memory-expensive ones, which could be reduced for smaller games */
/* */
/* MAX_DICT_ENTRIES Most full games need at least 500; "Curses" needs 750 */
/* MAX_SYMBOLS Total symbols (system and user); eg, "Curses" has 3400 */
/* MAX_BANK_SIZE Symbols are indexed in 7 "banks" for quick searching */
/* MAX_VERBS About 130 bytes each; eg, "Curses" has 105 */
/* */
/* BUFFER_LENGTH Source code lines (between ;'s) must be < this */
/* Some routines have local strings this large, so it eats */
/* the C stack a little. Inform has two global buffers */
/* plus STACK_LONG_SLOTS more, all this size. */
/* */
/* MAX_EXPRESSION_NODES Measures of how complicated expressions can be: */
/* MAX_ARITY about (7+MA)*4*MEN bytes are consumed by the expression */
/* evaluator's main array; less than 8K even when large */
/* (since MA is at most 5 for the Z-machine, there's no */
/* point in increasing it) */
/* */
/* MAX_OLDEPTH Maximum "objectloop" nesting (costs c. 40 bytes each) */
/* */
/* STACK_SIZE The preprocessor stack malloc's */
/* STACK_LONG_SLOTS SS*SSL + SLS*BL */
/* STACK_SHORT_LENGTH bytes. SS must be at least 10 or so. SSL could */
/* probably be reduced to 50 or so at a pinch. */
/* SLS is small anyway (but so it should be!) */
/* */
/* MAX_INCLUSION_DEPTH How deeply source files can #include each other */
/* (these cost about 72 bytes each) */
/* */
/* 2. Cheap ones; sizes of integer arrays and the like */
/* */
/* MAX_ACTIONS Actions are not very expensive; eg "Curses" has 120 */
/* MAX_ADJECTIVES Again, cheap. Typical Infocom games have 16-20 */
/* MAX_STATIC_DATA Size of an int array. Must be >= 1024; "Curses" 1300 */
/* MAX_TOKENS Tokens per source line; this is cheap to increase */
/* MAX_BLOCK_NESTING Nesting of braces {, } */
/* MAX_ROUTINES eg, "Curses" only has 350 */
/* MAX_GCONSTANTS Too complicated for this margin, but cheap and rare */
/* MAX_ERRORS Number of errors allowed before Inform gives up */
/* MAX_ABBREVS Maximum declared abbreviations (must be <=64) */
/* MAX_ABBREV_LENGTH Storage for abbrevs = product of these two */
/* MAX_IDENTIFIER_LENGTH Max size of variable names, etc: say 32 */
/* */
/* 3. Sizes in bytes of malloc'ated memory */
/* */
/* MAX_INITIAL_DATA_SIZE Holds story file up to the code area; don't reduce it */
/* MAX_PROP_TABLE_SIZE Holds properties table; eg, "Curses" needs 7500 */
/* MAX_INPUT_LENGTH Cache for holding source code; eg, "Curses" is 350K */
/* (only used in large memory version) */
/* MAX_STATIC_STRINGS In temporary files version, must be >= as large as */
/* MAX_ZCODE_SIZE largest likely string; */
/* in large memory version, must be >= as large as strings */
/* area (c. 40K) and code area (c. 80K) respectively */
/* */
/* SYMBOLS_CHUNK_SIZE Symbols table is malloc'd in chunks as needed */
/* */
/* With ALLOCATE_BIG_ARRAYS set, (and with the other two options on) Inform */
/* malloc's about 190K, of which the largest continuous segment is 48000 bytes. */
/* -------------------------------------------------------------------------------- */
/* Inclusions and the important macro definitions: */
/* -------------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#define BUFFER_LENGTH 2000
#define MAX_SYMBOLS 3500
#define MAX_BANK_SIZE 2000
#define SYMBOLS_CHUNK_SIZE 5000
#define HASH_TAB_SIZE 512
#define MAX_ACTIONS 125
#define MAX_ADJECTIVES 50
#define MAX_DICT_ENTRIES 750
#define MAX_STATIC_DATA 1500
#define MAX_TOKENS 100
#define MAX_BLOCK_NESTING 32
#define MAX_OLDEPTH 8
#define MAX_ROUTINES 400
#define MAX_GCONSTANTS 50
#define MAX_ERRORS 100
#define MAX_IDENTIFIER_LENGTH 32
#define MAX_INITIAL_DATA_SIZE 25000
#define MAX_PROP_TABLE_SIZE 10000
#define MAX_ARITY 5
#define STACK_SIZE 20
#define STACK_LONG_SLOTS 5
#define STACK_SHORT_LENGTH 80
#define MAX_ABBREVS 64
#define MAX_ABBREV_LENGTH 64
#define MAX_EXPRESSION_NODES 40
#define MAX_VERBS 110
#define MAX_INCLUSION_DEPTH 4
#ifdef USE_TEMPORARY_FILES
#define MAX_STATIC_STRINGS 2000
#define MAX_ZCODE_SIZE 2000
#else
#define MAX_STATIC_STRINGS 50000
#define MAX_ZCODE_SIZE 100000
#endif
/* -------------------------------------------------------------------------------- */
/* Twisting the C compiler's arm to get a convenient 32-bit integer type */
/* Warning: chars are presumed unsigned in this code, which I think is ANSI std; */
/* but they were presumed signed by K&R, so confusion reigns. Anyway a compiler */
/* ought to be able to cast either way as needed. */
/* Subtracting pointers is in a macro here for convenience: if even 32 bit ints */
/* won't reliably hold pointers on your machine, rewrite properly using ptrdiff_t */
/* -------------------------------------------------------------------------------- */
#ifndef VAX
#if SCHAR_MAX >= 0x7FFFFFFFL && SCHAR_MIN <= -0x7FFFFFFFL
typedef signed char int32;
typedef unsigned char uint32;
#elif SHRT_MAX >= 0x7FFFFFFFL && SHRT_MIN <= -0x7FFFFFFFL
typedef signed short int int32;
typedef unsigned short int uint32;
#elif INT_MAX >= 0x7FFFFFFFL && INT_MIN <= -0x7FFFFFFFL
typedef signed int int32;
typedef unsigned int uint32;
#elif LONG_MAX >= 0x7FFFFFFFL && LONG_MIN <= -0x7FFFFFFFL
typedef signed long int int32;
typedef unsigned long int uint32;
#else
#error No type large enough to support 32-bit integers.
#endif
#else
typedef int int32;
typedef unsigned int uint32;
#endif
#define subtract_pointers(p1,p2) (((int32) p1)-((int32) p2))
/* -------------------------------------------------------------------------------- */
/* This hideous line is here only for checking on my machine that Inform runs */
/* properly when int is 16-bit */
/* -------------------------------------------------------------------------------- */
/* #define int short int */
/* -------------------------------------------------------------------------------- */
/* Routines which use unusual ANSI library functions: */
/* */
/* write_serialnumber writes today's date in the form YYMMDD as a string */
/* (as can be seen, the VAX doesn't know it) */
/* -------------------------------------------------------------------------------- */
int time_set=0; char time_given[7];
void write_serialnumber(char *buffer)
{ time_t tt; tt=time(0);
if (time_set==0)
#ifdef TIME_UNAVAILABLE
sprintf(buffer,"930000");
#else
strftime(buffer,10,"%y%m%d",localtime(&tt));
#endif
else
sprintf(buffer,"%06s",time_given);
}
/* -------------------------------------------------------------------------------- */
/* Character set */
/* (Alter translate_to_ascii if need be to convert your local character set) */
/* -------------------------------------------------------------------------------- */
void make_lower_case(char *str)
{ int i;
for (i=0; str[i]!=0; i++)
if (isupper(str[i])) str[i]=tolower(str[i]);
}
void make_upper_case(char *str)
{ int i;
for (i=0; str[i]!=0; i++)
if (islower(str[i])) str[i]=toupper(str[i]);
}
int translate_to_ascii(char c)
{ return((int) c);
}
#define SINGLE_QUOTE '\''
/* -------------------------------------------------------------------------------- */
/* Structure definitions */
/* -------------------------------------------------------------------------------- */
typedef struct sourcefile
{ FILE *handle;
char filename[64];
int source_line;
} Sourcefile;
typedef struct opcode
{ char *name;
int code, offset, type1, type2, no;
} opcode;
typedef struct operand_t
{ int32 value; int type;
} operand_t;
typedef struct treenode {
int arity;
int g[MAX_ARITY];
int wnumber;
int type;
int gcount;
int up;
int priority;
char *op;
} treenode;
typedef struct verbl {
unsigned char e[8];
} verbl;
typedef struct verbt {
int lines;
verbl l[16];
} verbt;
typedef struct prop {
unsigned char l, num, p[10];
} prop;
typedef struct propt {
char l;
prop pp[32];
} propt;
typedef struct objectt {
unsigned char atts[4], parent, next, child;
int propsize;
} objectt;
/* -------------------------------------------------------------------------------- */
/* All the arrays of larger than tiny size */
/* -------------------------------------------------------------------------------- */
#ifndef ALLOCATE_BIG_ARRAYS
verbt vs[MAX_VERBS];
objectt objects[256];
int table_init[MAX_STATIC_DATA];
int32 actions[MAX_ACTIONS],
preactions[MAX_ACTIONS],
adjectives[MAX_ADJECTIVES],
adjcomps[MAX_ADJECTIVES];
char * symbs[MAX_SYMBOLS];
int32 svals[MAX_SYMBOLS],
gvalues[240];
int gflags[240];
#ifdef VAX
char stypes[MAX_SYMBOLS];
#else
signed char stypes[MAX_SYMBOLS];
#endif
int abbrev_values[MAX_ABBREVS];
int abbrev_quality[MAX_ABBREVS];
int abbrev_freqs[MAX_ABBREVS];
char buffer[BUFFER_LENGTH];
int banks[7][MAX_BANK_SIZE];
int bank1_next[MAX_BANK_SIZE];
int32 bank1_hash[HASH_TAB_SIZE];
int bank6_next[MAX_BANK_SIZE];
int32 bank6_hash[HASH_TAB_SIZE];
int routine_keys[MAX_ROUTINES];
int dict_places_list[MAX_DICT_ENTRIES],
dict_places_back[MAX_DICT_ENTRIES],
dict_places_inverse[MAX_DICT_ENTRIES];
int32 dict_sorts[MAX_DICT_ENTRIES];
treenode woods[MAX_EXPRESSION_NODES];
#else
verbt *vs;
objectt *objects;
int *table_init;
int32 *actions,
*preactions,
*adjectives,
*adjcomps;
char * *symbs;
int32 *svals,
*gvalues;
int *gflags;
#ifdef VAX
char *stypes;
#else
signed char *stypes;
#endif
int *abbrev_values;
int *abbrev_quality;
int *abbrev_freqs;
char *buffer;
int *banks_are_at;
int *routine_keys;
int *dict_places_list,
*dict_places_back,
*dict_places_inverse;
int32 *dict_sorts;
int *banks[7];
int *bank1_next;
int32 *bank1_hash;
int *bank6_next;
int32 *bank6_hash;
treenode *woods;
#endif
void *my_calloc(int, int);
void allocate_the_arrays()
{
#ifdef ALLOCATE_BIG_ARRAYS
int i;
vs = my_calloc(sizeof(verbt), MAX_VERBS);
objects = my_calloc(sizeof(objectt), 256);
table_init = my_calloc(sizeof(int), MAX_STATIC_DATA);
actions = my_calloc(sizeof(int32), MAX_ACTIONS);
preactions = my_calloc(sizeof(int32), MAX_ACTIONS);
adjectives = my_calloc(sizeof(int32), MAX_ADJECTIVES);
adjcomps = my_calloc(sizeof(int32), MAX_ADJECTIVES);
symbs = my_calloc(sizeof(char *), MAX_SYMBOLS);
svals = my_calloc(sizeof(int32), MAX_SYMBOLS);
gvalues = my_calloc(sizeof(int32), 240);
gflags = my_calloc(sizeof(int), 240);
stypes = my_calloc(sizeof(char),MAX_SYMBOLS);
abbrev_values = my_calloc(sizeof(int), MAX_ABBREVS);
abbrev_quality = my_calloc(sizeof(int), MAX_ABBREVS);
abbrev_freqs = my_calloc(sizeof(int), MAX_ABBREVS);
buffer = my_calloc(sizeof(char), BUFFER_LENGTH);
banks_are_at = my_calloc(sizeof(int), 7*MAX_BANK_SIZE);
bank1_next = my_calloc(sizeof(int), MAX_BANK_SIZE);
bank1_hash = my_calloc(sizeof(int32), MAX_BANK_SIZE);
bank6_next = my_calloc(sizeof(int), MAX_BANK_SIZE);
bank6_hash = my_calloc(sizeof(int32), MAX_BANK_SIZE);
for (i=0; i<7; i++)
banks[i] = banks_are_at + i*MAX_BANK_SIZE;
routine_keys = my_calloc(sizeof(int), MAX_ROUTINES);
dict_places_list = my_calloc(sizeof(int), MAX_DICT_ENTRIES);
dict_places_back = my_calloc(sizeof(int), MAX_DICT_ENTRIES);
dict_places_inverse = my_calloc(sizeof(int), MAX_DICT_ENTRIES);
dict_sorts = my_calloc(sizeof(int32), MAX_DICT_ENTRIES);
woods = my_calloc(sizeof(treenode), MAX_EXPRESSION_NODES);
#else
return;
#endif
}
/* -------------------------------------------------------------------------------- */
/* The important global variables */
/* -------------------------------------------------------------------------------- */
int no_verbs=0, no_actions=0, no_adjectives=0, no_abbrevs=0, no_attributes,
no_properties, no_globals=0, no_objects, dict_entries;
int no_symbols=0, no_routines, no_dummy_labels, no_blocks_made, no_gconstants=0,
no_errors=0, no_warnings=0, no_locals, no_stubbed;
int fp_no_actions, in_routine_flag;
int brace_stack[MAX_BLOCK_NESTING], brace_sp, next_block_type, forloop_flag;
int stub_flags[32];
int ppstack_openb, ppstack_closeb;
int globals_size, properties_size;
int32 prop_defaults[32]; int prop_longflag[32]; char *properties_table;
int release_number=1, statusline_flag=0;
propt full_object;
char *zcode, *zcode_p,
*symbols_p, *symbols_top,
*strings, *strings_p,
*dictionary, *dict_p,
*output_p, *abbreviations_at;
int statistics_mode=0, offsets_mode=0, memout_mode=0, economy_mode=0,
ignoreswitches_mode=0, bothpasses_mode=0, double_spaced=0, hash_mode=0,
percentages_mode=0, trace_mode, ltrace_mode, etrace_mode,
listing_mode=0, concise_mode=0, nowarnings_mode=0,
frequencies_mode=0, tracing_mode;
int abbrev_mode=1;
Sourcefile InputFiles[MAX_INCLUSION_DEPTH];
int input_file, total_files_read;
int32 marker_in_file;
int internal_line, total_source_line, pass_number, return_flag,
firsthash_flag=1, recenthash_flag=0, endofpass_flag=0;
int code_offset = 0x400,
actions_offset = 0x800,
preactions_offset = 0x800,
dictionary_offset = 0x800,
adjectives_offset = 0x800,
variables_offset = 0,
strings_offset = 0x800;
int32 Out_Size, Write_Code_At, Write_Strings_At;
char Source_Name[100], Code_Name[100];
#ifdef USE_TEMPORARY_FILES
FILE *Temp1_fp=NULL, *Temp2_fp=NULL;
char *utf_zcode_p;
#endif
/* -------------------------------------------------------------------------------- */
/* Text translation routines (using synonyms only as told) */
/* -------------------------------------------------------------------------------- */
const char *alphabet[3] = {
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
" ^0123456789.,!?_#'~/\\-:()"
};
int chars_lookup[256];
int abbrevs_lookup[256], almade_flag=0;
void make_lookup()
{ int i, j, k;
for (j=0; j<256; j++)
{ chars_lookup[j]=127; abbrevs_lookup[j]= -1; }
for (j=0; j<3; j++)
for (k=0; k<26; k++)
{ i=(int) ((alphabet[j])[k]);
chars_lookup[i]=k+j*26;
}
}
void make_abbrevs_lookup()
{ int i, j, k, l; char p[MAX_ABBREV_LENGTH]; char *p1, *p2;
do
{ for (i=0, j=0; j<no_abbrevs; j++)
for (k=j+1; k<no_abbrevs; k++)
{ p1=abbreviations_at+j*MAX_ABBREV_LENGTH;
p2=abbreviations_at+k*MAX_ABBREV_LENGTH;
if (strcmp(p1,p2)>0)
{ i=1; strcpy(p,p1); strcpy(p1,p2); strcpy(p2,p);
l=abbrev_values[j]; abbrev_values[j]=abbrev_values[k];
abbrev_values[k]=l;
l=abbrev_quality[j]; abbrev_quality[j]=abbrev_quality[k];
abbrev_quality[k]=l;
}
}
} while (i==1);
for (j=no_abbrevs-1; j>=0; j--)
{ p1=abbreviations_at+j*MAX_ABBREV_LENGTH;
abbrevs_lookup[p1[0]]=j;
abbrev_freqs[j]=0;
}
almade_flag=1;
}
int z_chars[3], uptothree;
int total_chars_trans, total_zchars_trans, total_bytes_trans, trans_length;
unsigned char *text_pc;
void write_z_char(int i)
{ uint32 j;
total_zchars_trans++;
z_chars[uptothree++]=(i%32);
if (uptothree!=3) return;
j= z_chars[0]*0x0400 + z_chars[1]*0x0020 + z_chars[2];
text_pc[0] = j/256;
text_pc[1] = j%256;
uptothree=0; text_pc+=2;
total_bytes_trans+=2;
}
void end_z_chars(void)
{ unsigned char *p;
trans_length=total_zchars_trans-trans_length;
while (uptothree!=0) write_z_char(5);
p=(unsigned char *) text_pc;
*(p-2)= *(p-2)+128;
}
int try_abbreviations_from(unsigned char *text, int i, int from)
{ int j, k; char *p, c;
c=text[i];
for (j=from, p=abbreviations_at+from*MAX_ABBREV_LENGTH;
(j<no_abbrevs)&&(c==p[0]); j++, p+=MAX_ABBREV_LENGTH)
{ if (text[i+1]==p[1])
{ for (k=2; p[k]!=0; k++)
if (text[i+k]!=p[k]) goto NotMatched;
for (k=0; p[k]!=0; k++) text[i+k]=1;
abbrev_freqs[j]++;
return(j);
NotMatched: ;
}
}
return(-1);
}
char *translate_text(char *p, char *s_text)
{ int i, j, k, newa, cc, value, value2;
unsigned char *text;
trans_length=total_zchars_trans;
if ((almade_flag==0)&&(no_abbrevs!=0)&&(abbrev_mode!=0))
make_abbrevs_lookup();
text=(unsigned char *) s_text;
uptothree=0; text_pc=(unsigned char *) p;
for (i=0; text[i]!=0; i++)
{ total_chars_trans++;
if (double_spaced==1)
{ if ((text[i]=='.')&&(text[i+1]==' ')&&(text[i+2]==' ')) text[i+2]=1;
}
if ((economy_mode==1)&&(abbrev_mode!=0)
&&((k=abbrevs_lookup[text[i]])!=-1))
{ if ((j=try_abbreviations_from(text, i, k))!=-1)
{ if (j<32) { write_z_char(2); write_z_char(j); }
else { write_z_char(3); write_z_char(j-32); }
}
}
if (text[i]=='@')
{ value= -1;
switch(text[i+1])
{ case '0': value=0; break;
case '1': value=1; break;
case '2': value=2; break;
case '3': value=3; break;
case '4': value=4; break;
case '5': value=5; break;
case '6': value=6; break;
case '7': value=7; break;
case '8': value=8; break;
case '9': value=9; break;
}
value2= -1;
switch(text[i+2])
{ case '0': value2=0; break;
case '1': value2=1; break;
case '2': value2=2; break;
case '3': value2=3; break;
case '4': value2=4; break;
case '5': value2=5; break;
case '6': value2=6; break;
case '7': value2=7; break;
case '8': value2=8; break;
case '9': value2=9; break;
}
if ((value!=-1)&&(value2!=-1))
{ i++; i++;
write_z_char(1); write_z_char(value*10+value2);
}
}
else
{ if (text[i]!=1)
{ if (text[i]==' ') write_z_char(0);
else
{ cc=chars_lookup[(int) (text[i])];
if (cc==127)
{ write_z_char(5); write_z_char(6);
j=translate_to_ascii(text[i]);
write_z_char(j/32); write_z_char(j%32);
}
else
{ newa=cc/26; value=cc%26;
if (newa==1) write_z_char(4);
if (newa==2) write_z_char(5);
write_z_char(value+6);
}
}
}
}
}
end_z_chars();
return((char *) text_pc);
}
/* ---------------------------------------------------------------------------------- */
/* The (static) Z-code database (using a table adapted from that in the InfoToolkit */
/* disassembler "txd") */
/* ---------------------------------------------------------------------------------- */
#define NONE 0
#define STORE 1
#define BRANCH 2
#define CALL 3
#define JUMP 4
#define RETURN 5
#define NCALL 6
#define PCHAR 7
#define VATTR 8
#define ILLEGAL 9
#define INDIR 10
#define VAR 1
#define TEXT 2
#define OBJECT 3
#define VARI -1
#define ZERO 0
#define ONE 1
#define TWO 2
opcode the_opcode(int i,char *s,int k,int l,int m)
{ opcode op; op.name=s; op.code=i; op.type1=k; op.type2=l; op.no=m;
return(op);
}
opcode opcs(int i)
{
switch(i)
{
case 0: return(the_opcode(0x01, "JE", BRANCH, NONE, TWO));
case 1: return(the_opcode(0x02, "JLE", BRANCH, NONE, TWO));
case 2: return(the_opcode(0x03, "JGE", BRANCH, NONE, TWO));
case 3: return(the_opcode(0x04, "DEC_CHK", BRANCH, VAR, TWO));
case 4: return(the_opcode(0x05, "INC_CHK", BRANCH, VAR, TWO));
case 5: return(the_opcode(0x06, "COMPARE_POBJ", BRANCH, NONE, TWO));
case 6: return(the_opcode(0x07, "TEST", BRANCH, NONE, TWO));
case 7: return(the_opcode(0x08, "OR", STORE, NONE, TWO));
case 8: return(the_opcode(0x09, "AND", STORE, NONE, TWO));
case 9: return(the_opcode(0x0A, "TEST_ATTR", BRANCH, NONE, TWO));
case 10: return(the_opcode(0x0B, "SET_ATTR", NONE, NONE, TWO));
case 11: return(the_opcode(0x0C, "CLEAR_ATTR", NONE, NONE, TWO));
case 12: return(the_opcode(0x0D, "STORE", NONE, VAR, TWO));
case 13: return(the_opcode(0x0D, "LSTORE", NONE, VAR, VARI));
case 14: return(the_opcode(0x0E, "INSERT_OBJ", NONE, NONE, TWO));
case 15: return(the_opcode(0x0F, "LOADW", STORE, NONE, TWO));
case 16: return(the_opcode(0x10, "LOADB", STORE, NONE, TWO));
case 17: return(the_opcode(0x11, "GET_PROP", STORE, NONE, TWO));
case 18: return(the_opcode(0x12, "GET_PROP_ADDR", STORE, NONE, TWO));
case 19: return(the_opcode(0x13, "GET_NEXT_PROP", STORE, NONE, TWO));
case 20: return(the_opcode(0x14, "ADD", STORE, NONE, TWO));
case 21: return(the_opcode(0x15, "SUB", STORE, NONE, TWO));
case 22: return(the_opcode(0x16, "MUL", STORE, NONE, TWO));
case 23: return(the_opcode(0x17, "DIV", STORE, NONE, TWO));
case 24: return(the_opcode(0x18, "MOD", STORE, NONE, TWO));
case 25: return(the_opcode(0x01, "VJE", BRANCH, NONE, VARI));
case 26: return(the_opcode(0x20, "CALL", CALL, NONE, VARI));
case 27: return(the_opcode(0x20, "ICALL", STORE, NONE, VARI));
case 28: return(the_opcode(0x21, "STOREW", NONE, NONE, VARI));
case 29: return(the_opcode(0x22, "STOREB", NONE, NONE, VARI));
case 30: return(the_opcode(0x23, "PUT_PROP", NONE, NONE, VARI));
case 31: return(the_opcode(0x24, "READ", NONE, NONE, VARI));
case 32: return(the_opcode(0x25, "PRINT_CHAR", PCHAR, NONE, VARI));
case 33: return(the_opcode(0x26, "PRINT_NUM", NONE, NONE, VARI));
case 34: return(the_opcode(0x27, "RANDOM", STORE, NONE, VARI));
case 35: return(the_opcode(0x28, "PUSH", NONE, NONE, VARI));
case 36: return(the_opcode(0x29, "PULL", NONE, VAR, VARI));
case 37: return(the_opcode(0x2A, "STATUS_SIZE", NONE, NONE, VARI));
case 38: return(the_opcode(0x2B, "SET_WINDOW", NONE, NONE, VARI));
case 39: return(the_opcode(0x33, "SET_PRINT", NONE, NONE, VARI));
case 40: return(the_opcode(0x34, "#RECORD_MODE", NONE, NONE, VARI));
case 41: return(the_opcode(0x35, "SOUND", NONE, NONE, VARI));
case 42: return(the_opcode(0x00, "JZ", BRANCH, NONE, ONE));
case 43: return(the_opcode(0x01, "GET_SIBLING", STORE, OBJECT, ONE));
case 44: return(the_opcode(0x02, "GET_CHILD", STORE, OBJECT, ONE));
case 45: return(the_opcode(0x03, "GET_PARENT", STORE, NONE, ONE));
case 46: return(the_opcode(0x04, "GET_PROP_LEN", STORE, NONE, ONE));
case 47: return(the_opcode(0x05, "INC", NONE, VAR, ONE));
case 48: return(the_opcode(0x06, "DEC", NONE, VAR, ONE));
case 49: return(the_opcode(0x07, "PRINT_ADDR", NONE, NONE, ONE));
case 50: return(the_opcode(0x09, "REMOVE_OBJ", NONE, NONE, ONE));
case 51: return(the_opcode(0x0A, "PRINT_OBJ", NONE, NONE, ONE));
case 52: return(the_opcode(0x0B, "RET", RETURN, NONE, ONE));
case 53: return(the_opcode(0x0C, "JUMP", JUMP, NONE, ONE));
case 54: return(the_opcode(0x0D, "PRINT_PADDR", NONE, NONE, ONE));
case 55: return(the_opcode(0x0E, "LOAD", STORE, VAR, ONE));
case 56: return(the_opcode(0x0F, "NOT", STORE, NONE, ONE));
case 57: return(the_opcode(0x00, "RET#TRUE", RETURN, NONE, ZERO));
case 58: return(the_opcode(0x01, "RET#FALSE", RETURN, NONE, ZERO));
case 59: return(the_opcode(0x02, "PRINT", NONE, TEXT, ZERO));
case 60: return(the_opcode(0x03, "PRINT_RET", RETURN, TEXT, ZERO));
case 61: return(the_opcode(0x05, "SAVE", BRANCH, NONE, ZERO));
case 62: return(the_opcode(0x06, "RESTORE", BRANCH, NONE, ZERO));
case 63: return(the_opcode(0x07, "RESTART", NONE, NONE, ZERO));
case 64: return(the_opcode(0x08, "RET(SP)+", RETURN, NONE, ZERO));
case 65: return(the_opcode(0x09, "POP", NONE, NONE, ZERO));
case 66: return(the_opcode(0x0A, "QUIT", NONE, NONE, ZERO));
case 67: return(the_opcode(0x0B, "NEW_LINE", NONE, NONE, ZERO));
case 68: return(the_opcode(0x0C, "SHOW_SCORE", NONE, NONE, ZERO));
case 69: return(the_opcode(0x0D, "VERIFY", BRANCH, NONE, ZERO));
}
return(the_opcode(0xff,"???",NONE,NONE,ZERO));
}
/* -------------------------------------------------------------------------------- */
/* File handling */
/* */
/* Arguably the temporary files should be made using "tmpfile" in ANSI C, but */
/* we do it by hand since tmpfile is a bit uncommon */
/* -------------------------------------------------------------------------------- */
int current_source_line()
{ return(InputFiles[input_file-1].source_line);
}
int override_error_line=0;
void print_error_line()
{ int i=override_error_line;
if (input_file>1) printf("\"%s\", ",InputFiles[input_file-1].filename);
if (i==0) i=current_source_line();
else override_error_line=0;
printf("line %d: ",i);
}
void fatalerror(char *s)
{ print_error_line();
printf("Fatal error: %s\n",s);
exit(1);
}
int malloced_bytes=0;
char *my_malloc(int size, char *whatfor)
{ char *c;
if (memout_mode==1)
printf("Allocating %d bytes for %s\n",size,whatfor);
c=malloc(size); malloced_bytes+=size;
if (c==0) fatalerror("Couldn't allocate memory");
return(c);
}
void *my_calloc(int size, int howmany)
{ void *c;
if (memout_mode==1)
printf("Allocating %d bytes: array of %d entries of size %d\n",
size*howmany,howmany,size);
c=calloc(howmany,size); malloced_bytes+=size*howmany;
if (c==0) fatalerror("Couldn't allocate memory for an array");
return(c);
}
void load_sourcefile(char *story_name)
{ char name[128], theerror[128]; int i, flag=0;
if (input_file==MAX_INCLUSION_DEPTH)
{ fatalerror("Too many files have included each other: \
increase #define MAX_INCLUSION_DEPTH");
}
strcpy(InputFiles[input_file].filename,story_name);
for (i=0; story_name[i]!=0; i++)
if ((story_name[i]=='/') || (story_name[i]=='.')) flag=1;
if (flag==0)
{ if (input_file>0)
sprintf(name,"%s%s%s",
Include_Prefix,story_name,Include_Extension);
else
sprintf(name,"%s%s%s",
Source_Prefix,story_name,Source_Extension);
}
else
strcpy(name,story_name);
InputFiles[input_file].handle = fopen(name,"r");
if (InputFiles[input_file].handle==NULL)
{ sprintf(theerror, "Couldn't open input file \"%s\"",name);
fatalerror(theerror);
}
InputFiles[input_file++].source_line = 1;
total_files_read++;
if ((ltrace_mode!=0)||(trace_mode!=0))
{ printf("\nOpening file \"%s\"\n",name);
}
}
void close_sourcefile()
{ fclose(InputFiles[--input_file].handle);
if ((ltrace_mode!=0)||(trace_mode!=0))
{ printf("\nClosing file\n");
}
}
int32 last_char_marker= -1; int last_char;
int file_char(int32 marker)
{ if (marker==last_char_marker) return(last_char);
last_char_marker=marker;
if (input_file==0) return(0);
last_char=fgetc(InputFiles[input_file-1].handle);
if (last_char==EOF)
{ close_sourcefile();
if (input_file==0) last_char=0; else last_char='\n';
}
return(last_char);
}
int file_end(int32 marker)
{ int i;
i=file_char(marker);
if (i==0) return(1);
return(0);
}
unsigned int checksum_code=0, checksum_string=0, checksum_body=0;
void output_file(void)
{ FILE *fout; char *t; int32 length=0, blanks=0, size=0;
fout=fopen(Code_Name,"wb");
if (fout==NULL) fatalerror("Couldn't open output file");
#ifndef USE_TEMPORARY_FILES
checksum_code=0;
for (t=zcode; t<zcode_p; t++) { checksum_code+=(unsigned) *t; }
checksum_string=0;
for (t=strings; t<strings_p; t++) { checksum_string+=(unsigned) *t; }
#endif
checksum_body=checksum_code+checksum_string;
for (t=output_p+0x0040; t<output_p+Write_Code_At; t++)
{ checksum_body+=(unsigned) *t; }
length=((int32) Write_Strings_At)+ subtract_pointers(strings_p,strings);
if ((length%2)==1) { length++; blanks=1; }
length=length/2;
output_p[26]=(length & 0xff00)/0x100;
output_p[27]=(length & 0xff);
while (((2*length)+blanks-1)%512 != 511) blanks++;
output_p[28]=(checksum_body & 0xff00)/0x100;
output_p[29]=(checksum_body & 0xff);
for (t=output_p; t<output_p+Write_Code_At; t++) { fputc(*t,fout); size++; }
#ifdef USE_TEMPORARY_FILES
{ FILE *fin;
fclose(Temp2_fp);
fin=fopen(Temp2_Name,"r");
if (fin==NULL) fatalerror("Couldn't reopen temporary file 2");
for (t=zcode; t<zcode_p; t++) { fputc(fgetc(fin),fout); size++; }
fclose(fin);
}
#else
for (t=zcode; t<zcode_p; t++) { fputc(*t,fout); size++; }
#endif
while (size<Write_Strings_At) { fputc(0,fout); size++; }
#ifdef USE_TEMPORARY_FILES
{ FILE *fin;
fclose(Temp1_fp);
fin=fopen(Temp1_Name,"r");
if (fin==NULL) fatalerror("Couldn't reopen temporary file 1");
for (t=strings; t<strings_p; t++) { fputc(fgetc(fin),fout); }
fclose(fin);
remove(Temp1_Name); remove(Temp2_Name);
}
#else
for (t=strings; t<strings_p; t++) { fputc(*t,fout); }
#endif
while (blanks>0) { fputc(0,fout); blanks--; }
fclose(fout);
if (statistics_mode==2) printf("%d bytes written to '%s'\n",length,Code_Name);
#ifdef ARCHIMEDES
sprintf(buffer,"settype %s 065",Code_Name);
system(buffer);
#endif
}
#ifdef USE_TEMPORARY_FILES
void open_temporary_files(void)
{
#ifdef UNIX
sprintf(Temp1_Name, "%s.proc%d",Temp1_Hdr,(int)getpid());
sprintf(Temp2_Name, "%s.proc%d",Temp2_Hdr,(int)getpid());
#endif
Temp1_fp=fopen(Temp1_Name,"wb");
if (Temp1_fp==NULL) fatalerror("Couldn't open temporary file 1");
Temp2_fp=fopen(Temp2_Name,"wb");
if (Temp2_fp==NULL) fatalerror("Couldn't open temporary file 2");
}
#endif
/* -------------------------------------------------------------------------------- */
/* Preprocessor stack routines */
/* This is a first-in first-out stack, used when (eg) a source line like */
/* "if 2*fish+5*loaves > multitude" is replaced by assembly lines; the assembly */
/* lines are stacked up and will be read in before the next source line. The */
/* stack needs a reasonable size (10 at the very least) but almost all its lines */
/* will be used for short assembler instructions, so it needs little provision */
/* for full-blown lines (probably only one "long slot" will ever be used). */
/* -------------------------------------------------------------------------------- */
int stacktop=0, stackbot=0;
char *stack[STACK_SIZE], *stack_longs[STACK_LONG_SLOTS];
void stack_create(void)
{ int i; char *stackp;
stackp=my_malloc(STACK_SIZE*STACK_SHORT_LENGTH,"preprocessor stack");
for (i=0; i<STACK_SIZE; i++) stack[i]=stackp+i*STACK_SHORT_LENGTH;
stackp=my_malloc(STACK_LONG_SLOTS*BUFFER_LENGTH,"pp stack long slots");
for (i=0; i<STACK_LONG_SLOTS; i++)
{ stack_longs[i]=stackp+i*BUFFER_LENGTH;
*(stack_longs[i])=0;
}
}
int stack_move(int sp)
{ sp++; if (sp==STACK_SIZE) sp=0;
return(sp);
}
void stack_line(char *p)
{ int i, f;
if (strlen(p)<STACK_SHORT_LENGTH)
strcpy(stack[stacktop],p);
else
{ *(stack[stacktop])=0; f=0;
for (i=0; i<STACK_LONG_SLOTS; i++)
if ((*(stack_longs[i])==0)&&(f==0))
{ strcpy(stack_longs[i],p);
*(stack[stacktop]+1)=i;
f=1;
}
if (f==0) {
fatalerror("The preprocessor stack has (amazingly) run out \
of long slots; increase #define STACK_LONG_SLOTS to extend it");
}
}
stacktop=stack_move(stacktop);
if (stacktop==stackbot)
fatalerror("The preprocessor stack has run out \
(probably due to huge expression): increase #define STACK_SIZE to extend it");
}
void destack_line(char *p)
{ int i;
i= *(stack[stackbot]);
if (i!=0)
strcpy(p,stack[stackbot]);
else
{ i= *(stack[stackbot]+1);
strcpy(p,stack_longs[i]);
*(stack_longs[i])=0;
}
stackbot=stack_move(stackbot);
}
/* -------------------------------------------------------------------------------- */
/* Character-level parsing and error reporting routines */
/* -------------------------------------------------------------------------------- */
void begin_pass(void)
{ total_source_line=0; total_files_read=1; almade_flag=0;
internal_line=0; endofpass_flag=0; marker_in_file=0;
trace_mode=tracing_mode; no_routines=0; no_stubbed=0;
no_abbrevs=0; in_routine_flag=1;
zcode_p=zcode; properties_size=0;
#ifdef USE_TEMPORARY_FILES
utf_zcode_p=zcode;
if (pass_number==2) open_temporary_files();
#endif
no_blocks_made=1; brace_sp=0; ltrace_mode=0; forloop_flag=0;
next_block_type=0; strings_p=strings; no_dummy_labels=0; no_objects=0;
no_verbs=0; fp_no_actions=no_actions; no_actions=0; no_adjectives=0;
dict_p=dictionary+7;
no_properties=2; no_attributes=0;
ppstack_openb=0; ppstack_closeb=0;
total_chars_trans=0; total_bytes_trans=0;
if (pass_number==2) ltrace_mode=listing_mode;
objects[0].parent=0; objects[0].child=0; objects[0].next=0;
firsthash_flag=1; recenthash_flag=0;
}
void print_hash()
{ if (firsthash_flag==1) { printf("%d:",pass_number); firsthash_flag=0; }
printf("#"); recenthash_flag=1; fflush(stdout);
}
int errors[MAX_ERRORS];
char forerrors_buff[BUFFER_LENGTH];
void message(int style, char *s)
{ if (recenthash_flag==1) printf("\n");
recenthash_flag=0;
print_error_line();
printf("%s: %s\n",(style==1)?"Error":"Warning",s);
if ((style==1)&&(concise_mode==0))
{ sprintf(forerrors_buff+68," ...etc");
printf("> %s\n",forerrors_buff);
}
}
void error(char *s)
{ int i;
if (no_errors==MAX_ERRORS) { fatalerror("Too many errors: giving up"); }
for (i=0; i<no_errors; i++)
if (errors[i]==internal_line) return;
errors[no_errors++]=internal_line;
message(1,s);
}
void warning_named(char *s1, char *s2)
{ char b[128];
sprintf(b,"%s \"%s\"",s1,s2);
no_warnings++;
message(2,b);
}
void error_named(char *s1, char *s2)
{ char b[128];
sprintf(b,"%s \"%s\"",s1,s2);
error(b);
}
void no_such_label(char *lname)
{ error_named("No such label as",lname);
}
void reached_new_line()
{ total_source_line++;
InputFiles[input_file-1].source_line++;
if ((hash_mode==1)&&(total_source_line%100==0)) print_hash();
}
int not_line_end(char c)
{ if (c=='\n') reached_new_line();
if ((c==0)||(c=='\n')) return(0);
return(1);
}
int quoted_mode;
int non_terminator(char c)
{ if (c=='\n') reached_new_line();
if (quoted_mode!=0)
{ if ((c==0)||(c=='\\')) return(0);
return(1);
}
if ((c==0)||(c==';')||(c=='!')||(c=='\\')||(c=='{')||(c=='}')) return(0);
return(1);
}
/* -------------------------------------------------------------------------------- */
/* Get the next line of input, and return 1 if it came from the preprocessor */
/* stack and 0 if it really came from the source files. */
/* So: */
/* If something's waiting on the stack, send that. */
/* If there are braces to be opened or closed, send those. */
/* If at the end of the source, send an "end" directive. */
/* Otherwise, keep going until a ; is reached which is not in 's or "s; */
/* throw away everything on any text line after a comment ! character; */
/* fold out characters between a \ and the first non-space on the next line. */
/* -------------------------------------------------------------------------------- */
int get_next_line(void)
{ int i, j; char d;
internal_line++;
quoted_mode=0;
do
{ if (stacktop!=stackbot) { destack_line(buffer); return(1); }
if (ppstack_openb>0) { strcpy(buffer,"{"); ppstack_openb--; return(1); }
if (ppstack_closeb>0) { strcpy(buffer,"}"); ppstack_closeb--; return(1); }
if (file_end(marker_in_file)==1) { strcpy(buffer,"#end"); return(1); }
i=0; j=0;
GNLL:
for (; non_terminator(d=file_char(marker_in_file+i)); i++, j++)
{ buffer[j]=d; if (d=='\"') quoted_mode=1-quoted_mode;
}
switch(d)
{ case '!': while (not_line_end(file_char(marker_in_file+i))) i++;
i++; goto GNLL;
case '{': ppstack_openb++; break;
case '}': ppstack_closeb++; break;
case '\\':
while (not_line_end(file_char(marker_in_file+i))) i++; i++;
while (file_char(marker_in_file+i)==' ') i++; goto GNLL;
}
buffer[j]=0;
marker_in_file+=i+1;
for (i=0; buffer[i]!=0; i++)
if (buffer[i]=='\n') buffer[i]=' ';
for (i=0; buffer[i]!=0; i++)
if (buffer[i]!=' ') return(0);
} while (1==1);
return(0);
}
/* -------------------------------------------------------------------------------- */
/* The Tokeniser (18)... coming to cinemas near you */
/* incorporating the martial arts classic */
/* Tokeniser II - This Time It's Optimal, */
/* with Dolph Lundgren as Dilip Sequeira and Gan as the two short planks */
/* -------------------------------------------------------------------------------- */
int no_tokens;
char *tokens, *tokens_p, *token_adds[MAX_TOKENS];
#define NUMBER_SEPARATORS 19
#define QUOTE_CODE 1000
#define DQUOTE_CODE 1001
#define NEWLINE_CODE 1002
#define NULL_CODE 1003
#define SPACE_CODE 1004
const char separators[NUMBER_SEPARATORS][4] = {
"+", "->", "-->", "-", "*", "/", "%", "|", "&", "==", "=", "~=", ">=", ">",
"<=", "<", "(", ")", "," };
int char_grid[256];
void make_s_grid()
{ int i, j;
for (i=0; i<256; i++) char_grid[i]=0;
for (i=0; i<NUMBER_SEPARATORS; i++)
{ j=separators[i][0];
if(char_grid[j]==0) char_grid[j]=i*16+1; else char_grid[j]++;
}
char_grid['\''] = QUOTE_CODE;
char_grid['\"'] = DQUOTE_CODE;
char_grid['\n'] = NEWLINE_CODE;
char_grid[0] = NULL_CODE;
char_grid[' '] = SPACE_CODE;
}
void tokenise_line(void)
{ char *p,*q; int i, j, k, bite, tok_l; const char *r;
no_tokens=0; tokens_p=tokens; token_adds[0]=tokens_p;
p=buffer;
for (i=0, tok_l=0; i<MAX_TOKENS; i++)
{ if(tok_l) {for(j=0;j<tok_l;j++) *tokens_p++= *p++; tok_l=0;goto got_tok;}
while(*p==' ') p++;
for(bite=0;1;)
{
switch(char_grid[*p])
{ case 0: *tokens_p++= *p++; bite=1; break;
case SPACE_CODE: goto got_tok;
case DQUOTE_CODE: do *tokens_p++= *p++; while (*p && *p!='\n' && *p!='\"');
if (*p=='\"') *tokens_p++= *p++; goto got_tok;
case NEWLINE_CODE: reached_new_line(); if (bite) goto got_tok; return;
case QUOTE_CODE: do *tokens_p++= *p++; while (*p && *p!='\n' && *p!='\'');
if (*p=='\'') *tokens_p++= *p++; goto got_tok;
case NULL_CODE: if (bite) goto got_tok; return;
default: for (j=char_grid[*p]>>4,k=j+(char_grid[*p]&15);j<k;j++)
{ for (q=p,r=separators[j];*q== *r && *r;q++,r++);
if (!*r)
{ if(bite) tok_l=q-p; else while(p<q) *tokens_p++= *p++;
goto got_tok;
}
}
*tokens_p++= *p++;bite=1;
}
}
got_tok:
*tokens_p++=0; token_adds[++no_tokens]=tokens_p;
}
error("Too many tokens on line"); no_tokens=MAX_TOKENS-1;
}
void word(char *b1, int w)
{ if (w>no_tokens) { b1[0]=0; return; }
strcpy(b1, token_adds[w-1]);
}
void dequote_text(char *b1)
{ int i;
if (*b1!='\"') error("Open quotes expected for text");
for (i=0; b1[i]!=0; i++) b1[i]=b1[i+1];
i=i-2;
if (b1[i]!='\"') error("Close quotes expected for text");
b1[i]=0;
}
void textword(char *b1, int w)
{ word(b1,w); dequote_text(b1);
}
/* -------------------------------------------------------------------------------- */
/* Dictionary table builder */
/* The dictionary is, so to speak, thumb-indexed: the beginning of each letter */
/* in the double-linked-list is marked. Experiments with increasing the number */
/* of markers (to the first two letters, say) result in extra bureaucracy which */
/* cancels out any speed gain. */
/* -------------------------------------------------------------------------------- */
#define NUMBER_DICT_MARKERS 26
int total_dict_entries; int32 letter_keys[NUMBER_DICT_MARKERS];
int letter_starts[NUMBER_DICT_MARKERS];
int start_list;
int prepared_bytes[4]; int32 prepared_sort; int initial_letter;
void dictionary_startpass(void)
{ int i, j;
total_dict_entries=dict_entries; dict_entries=0;
if (pass_number==1)
{ start_list=0; dict_places_list[0]= -2; dict_places_back[0]= -1;
for (i=0; i<NUMBER_DICT_MARKERS; i++)
{ letter_keys[i]=(int32) 0x7fffffffL;
letter_starts[i]= -1;
}
}
else
{ for (j=start_list, i=0; i<total_dict_entries; i++)
{ dict_places_inverse[j]=i; j=dict_places_list[j]; }
}
}
int32 sort_number(char *y)
{ unsigned char *x;
x= (unsigned char *) y;
return(((int32) 0x1000000L)*((x[0])%128)
+ ((int32) 0x10000L)*(x[1])
+ ((int32) 0x100L)*(x[2])
+ ((int32) (x[3])) );
}
uint32 dictionary_prepare(char *dword)
{ int i, wd[6]; uint32 tot;
for (i=0; (i<6)&&(dword[i]!=0); i++)
{ wd[i]=6+((chars_lookup[(int) (dword[i])])%26);
}
for (; i<6; i++) wd[i]=5;
initial_letter = wd[0]-6;
/* Note... this doesn't depend on A to Z being contiguous in the
machine's character set */
tot = wd[5] + wd[4]*(1<<5) + wd[3]*(1<<10)
+ wd[2]*(1<<16) + wd[1]*(1<<21) + wd[0]*(1<<26);
prepared_bytes[3]=tot%0x100;
prepared_bytes[2]=0x80 + (tot/0x100)%0x100;
prepared_bytes[1]=(tot/((uint32) 0x10000L))%0x100;
prepared_bytes[0]=(tot/((uint32) 0x1000000L))%0x100;
prepared_sort=tot;
return(tot);
}
int dictionary_find(char *dword, int scope)
{ int32 i, j, j2, k, jlim;
i=dictionary_prepare(dword);
if (scope==1) jlim=dict_entries; else jlim=total_dict_entries;
if (pass_number==1)
{ for (j=0; j<jlim; j++)
if (i==dict_sorts[j]) return(j+1);
return(0);
}
if ((k=letter_starts[initial_letter])==-1) return(0);
j=initial_letter+1;
while ((j<NUMBER_DICT_MARKERS)&&((j2=letter_starts[j])==-1)) j++;
if (j==NUMBER_DICT_MARKERS) { j2= -2; }
while (k!=j2)
{
if ((i==dict_sorts[k])&&(k<jlim))
return(dict_places_inverse[k]+1);
k=dict_places_list[k];
}
return(0);
}
void show_letter(int code)
{
if (code<6) { printf("."); return; }
printf("%c",(alphabet[0])[code-6]);
}
void show_dictionary(void)
{ int i, j, k; char *p;
k=dict_entries; if (k==0) k=total_dict_entries;
printf("Dictionary contains %d entries:\n",dict_entries);
for (i=0; i<dict_entries; i++)
{ p=dictionary+7+7*i;
if (dict_entries==0)
printf("Entry %03d (%03d > %03d) at %04x: ",
i,dict_places_back[i],dict_places_list[i],
dictionary_offset+7+7*i);
else
printf("Entry %03d at %04x: ",i,dictionary_offset+7+7*i);
show_letter( (((int) p[0])&0x7c)/4 );
show_letter( 8*(((int) p[0])&0x3) + (((int) p[1])&0xe0)/32 );
show_letter( ((int) p[1])&0x1f );
show_letter( (((int) p[2])&0x7c)/4 );
show_letter( 8*(((int) p[2])&0x3) + (((int) p[3])&0xe0)/32 );
show_letter( ((int) p[3])&0x1f );
printf(" ");
for (j=0; j<7; j++) printf("%02x ",p[j]);
printf("Sort number %d",sort_number(p));
printf("\n");
}
}
int dictionary_add(char *dword, int x, int y, int z)
{ int off, i, k, l; char *p; int32 pcomp, qcomp;
if (dict_entries==MAX_DICT_ENTRIES)
{ fatalerror("Dictionary full: increase #define MAX_DICT_ENTRIES"); }
i=dictionary_find(dword,1);
if (i!=0)
{ p=dictionary+i*7+4;
p[0]=(p[0])|x; p[1]=(p[1])|y; p[2]=(p[2])|z;
return(dictionary_offset+7*i);
}
if (pass_number==1) i=dict_entries;
else { i=dict_places_inverse[dict_entries]; }
off=7*i+7;
p=dictionary+off;
p[0]=prepared_bytes[0]; p[1]=prepared_bytes[1];
p[2]=prepared_bytes[2]; p[3]=prepared_bytes[3];
p[4]=x; p[5]=y; p[6]=z;
if (pass_number==1)
{ pcomp=prepared_sort;
if (dict_entries==0)
{ dict_places_list[0]= -2; dict_places_list[1]= -1; goto PlaceFound; }
l=initial_letter; do { k=letter_starts[l--]; } while ((l>=0)&&(k==-1));
if (k==-1) k=start_list;
for (; k!=-2; k=dict_places_list[k])
{ qcomp=dict_sorts[k];
if (pcomp<qcomp)
{ l=dict_places_back[k];
if (l==-1)
{ dict_places_list[dict_entries]=start_list;
dict_places_back[dict_entries]= -1;
dict_places_back[k]=dict_entries;
start_list=dict_entries; goto PlaceFound;
}
dict_places_list[l]=dict_entries;
dict_places_back[k]=dict_entries;
dict_places_list[dict_entries]=k;
dict_places_back[dict_entries]=l;
goto PlaceFound;
}
l=k;
}
dict_places_list[l]=dict_entries;
dict_places_back[dict_entries]=l;
dict_places_list[dict_entries]= -2;
PlaceFound: dict_sorts[dict_entries]=pcomp;
if (pcomp<letter_keys[initial_letter])
{ letter_keys[initial_letter]=pcomp;
letter_starts[initial_letter]=dict_entries;
}
}
dict_entries++; dict_p+=7;
/* show_dictionary(); */
return(dictionary_offset+off);
}
/* -------------------------------------------------------------------------------- */
/* Symbols table and address fixing */
/* -------------------------------------------------------------------------------- */
int banksize[7];
void init_symbol_banks(void)
{ int i, j;
banksize[0]=0; banksize[1]=0;
for (j=2; j<7; j++)
{ for (i=0; i<MAX_BANK_SIZE; i++)
{ banks[j][i]= -1;
}
}
for (i=0; i<MAX_ROUTINES; i++) routine_keys[i]= -1;
for (i=0;i<HASH_TAB_SIZE;i++) { bank1_hash[i]= -1; bank6_hash[i]= -1; }
}
int used_local_variable[16];
char *local_varname[16];
int routine_starts_line;
char reserveds_buffer[32];
void prim_new_symbol(char *p, int32 value, int type, int bank)
{ int i, j; int32 this, last, key, start=0; char *r;
if (p[0]==0) { error("Symbol name expected"); return; }
if (bank==6)
{ strcpy(reserveds_buffer,p); p=reserveds_buffer; }
make_lower_case(p);
if (bank==0)
{ start=routine_keys[no_routines]; if (start<0) goto NotDupl;
for (i=start; i<banksize[bank]; i++)
{ j=banks[bank][i];
if (strcmp(symbs[j],p)==0)
{ error_named("Duplicated symbol name",p);
return;
}
}
NotDupl: j=banksize[bank]++;
}
else
if (bank==1)
{ for(r=p,key=0; *r; r++) key=(key*67+*r)%HASH_TAB_SIZE;
for(this=bank1_hash[key], j=last= -1;
this!=-1 && (j=strcmp(symbs[banks[1][this]],p))<0;
last=this, this=bank1_next[this]);
if(!j)
{ if (pass_number==1)
{ error_named("Duplicated symbol name",p); return;}
return;
}
j=banksize[1]++;
}
else
if (bank==6)
{ for(r=p,key=0; *r; r++) key=(key*67+*r)%HASH_TAB_SIZE;
for(this=bank6_hash[key], j=last= -1;
this!=-1 && (j=strcmp(symbs[banks[6][this]],p))<0;
last=this, this=bank6_next[this]);
if(!j)
{ if (pass_number==1)
{ error_named("Duplicated symbol name",p); return;}
return;
}
j=banksize[6]++;
}
else
{ j=atoi(p+2);
if (banks[bank][j]!=-1)
{ error_named("Duplicated system symbol name",p);
return;
}
banks[bank][j]=no_symbols;
}
if (j>=MAX_BANK_SIZE)
{ fatalerror("Symbols bank exhausted: \
increase #define MAX_BANK_SIZE"); return; }
banks[bank][j]=no_symbols;
if (bank==0)
{ if (routine_keys[no_routines]==-1) routine_keys[no_routines]=j;
if (nowarnings_mode==0)
{ local_varname[value]=symbols_p;
}
}
if (bank==1)
{ if (last==-1) {bank1_next[j]=bank1_hash[key];bank1_hash[key]=j;}
else {bank1_next[j]=this; bank1_next[last]=j;}
}
if (bank==6)
{ if (last==-1) {bank6_next[j]=bank6_hash[key];bank6_hash[key]=j;}
else {bank6_next[j]=this; bank6_next[last]=j;}
}
if (no_symbols==MAX_SYMBOLS)
{ fatalerror("Symbols table exhausted: increase #define MAX_SYMBOLS"); }
if (symbols_p+strlen(p)+1 >= symbols_top)
{ symbols_p=my_malloc(SYMBOLS_CHUNK_SIZE,"symbols table chunk");
symbols_top=symbols_p+SYMBOLS_CHUNK_SIZE;
}
strcpy(symbols_p,p); symbs[no_symbols]=symbols_p;
symbols_p+=strlen(symbols_p)+1;
svals[no_symbols]=value; stypes[no_symbols]=type;
no_symbols++;
}
int prim_find_symbol(char *q, int bank)
{ char c[50], *r; int i, j, start=0, finish=banksize[bank];
int32 key, this;
strcpy(c,q); make_lower_case(c);
if (bank==0)
{ start=routine_keys[no_routines]; if (start<0) return -1;
i=routine_keys[no_routines+1]; if (i>=0) finish=i;
if (finish>start+15) finish=start+15;
for (i=start; i<finish; i++)
{ j=banks[bank][i];
if (strcmp(symbs[j],c)==0)
{ used_local_variable[svals[j]]=1;
return(j);
}
}
return(-1);
}
else
if (bank==1)
{ for(r=c, key=0; *r; r++) key=(key*67+*r)%HASH_TAB_SIZE;
for(this=bank1_hash[key],j= -1;
this!=-1 && (j=strcmp(symbs[banks[1][this]],c))<0;
this=bank1_next[this]);
if(!j) return banks[1][this];
return(-1);
}
else
if (bank==6)
{ for(r=c, key=0; *r; r++) key=(key*67+*r)%HASH_TAB_SIZE;
for(this=bank6_hash[key],j= -1;
this!=-1 && (j=strcmp(symbs[banks[6][this]],c))<0;
this=bank6_next[this]);
if(!j) return banks[6][this];
return(-1);
}
j=atoi(c+2);
return(banks[bank][j]);
}
int find_symbol(char *q)
{ if (q[0]!='_') return(prim_find_symbol(q,1));
if (q[1]=='s') return(prim_find_symbol(q,2));
if (q[1]=='S') return(prim_find_symbol(q,2));
if (q[1]=='w') return(prim_find_symbol(q,3));
if (q[1]=='W') return(prim_find_symbol(q,3));
if (q[1]=='f') return(prim_find_symbol(q,4));
if (q[1]=='F') return(prim_find_symbol(q,4));
if (q[1]=='x') return(prim_find_symbol(q,5));
if (q[1]=='X') return(prim_find_symbol(q,5));
error("Names are not permitted to start with an _");
return(-1);
}
int local_find_symbol(char *q)
{ return(prim_find_symbol(q,0));
}
void new_symbol(char *p, int32 value, int type)
{ if (pass_number==2) return;
if (strlen(p)>MAX_IDENTIFIER_LENGTH)
{ error_named("Name is too long:",p);
return;
}
if (type==3) { prim_new_symbol(p,value,type,0); return; }
if (p[0]!='_') { prim_new_symbol(p,value,type,1); return; }
if (p[1]=='s') { prim_new_symbol(p,value,type,2); return; }
if (p[1]=='S') { prim_new_symbol(p,value,type,2); return; }
if (p[1]=='w') { prim_new_symbol(p,value,type,3); return; }
if (p[1]=='W') { prim_new_symbol(p,value,type,3); return; }
if (p[1]=='f') { prim_new_symbol(p,value,type,4); return; }
if (p[1]=='F') { prim_new_symbol(p,value,type,4); return; }
if (p[1]=='x') { prim_new_symbol(p,value,type,5); return; }
if (p[1]=='X') { prim_new_symbol(p,value,type,5); return; }
error("Names are not permitted to start with an _");
}
/* -------------------------------------------------------------------------------- */
/* Creating reserved words */
/* -------------------------------------------------------------------------------- */
#define ABBREVIATE_CODE 0
#define ATTRIBUTE_CODE 1
#define CONSTANT_CODE 2
#define DICTIONARY_CODE 3
#define END_CODE 4
#define INCLUDE_CODE 5
#define GLOBAL_CODE 6
#define OBJECT_CODE 7
#define PROPERTY_CODE 8
#define RELEASE_CODE 9
#define SWITCHES_CODE 10
#define STATUSLINE_CODE 11
#define VERB_CODE 12
#define TRACE_CODE 13
#define NOTRACE_CODE 14
#define ETRACE_CODE 15
#define NOETRACE_CODE 16
#define BTRACE_CODE 17
#define NOBTRACE_CODE 18
#define LTRACE_CODE 19
#define NOLTRACE_CODE 20
#define ATRACE_CODE 21
#define NOATRACE_CODE 22
#define LISTSYMBOLS_CODE 23
#define LISTOBJECTS_CODE 24
#define LISTVERBS_CODE 25
#define LISTDICT_CODE 26
#define OPENBLOCK_CODE 27
#define CLOSEBLOCK_CODE 28
#define SERIAL_CODE 29
#define DEFAULT_CODE 30
#define STUB_CODE 31
#define PRINT_ADDR_CODE 0
#define PRINT_CHAR_CODE 1
#define PRINT_PADDR_CODE 2
#define PRINT_OBJ_CODE 3
#define PRINT_NUM_CODE 4
#define REMOVE_CODE 5
#define RETURN_CODE 6
#define DO_CODE 7
#define FOR_CODE 8
#define IF_CODE 9
#define OBJECTLOOP_CODE 10
#define UNTIL_CODE 11
#define WHILE_CODE 12
#define BREAK_CODE 13
#define ELSE_CODE 14
#define GIVE_CODE 15
#define INVERSION_CODE 16
#define MOVE_CODE 17
#define PUT_CODE 18
#define WRITE_CODE 19
#define STRING_CODE 20
#define FONT_CODE 21
#define ASSIGNMENT_CODE 100
#define FUNCTION_CODE 101
#define CreateD_(x,y) prim_new_symbol(x,y,14,6)
#define CreateC_(x,y) prim_new_symbol(x,y,15,6)
#define CreateB_(x,y,z) prim_new_symbol(x,z+y*100,16,6)
#define CreateA_(x,y) prim_new_symbol(x,y,17,6)
void stockup_symbols()
{ char *r1="RET#TRUE", *r2="RET#FALSE";
new_symbol("nothing",0,9);
new_symbol("sp",0,4); new_symbol("ret#true",1,4);
new_symbol("rtrue",1,4); new_symbol("ret#false",2,4); new_symbol("rfalse",2,4);
new_symbol("=",1,10); new_symbol("==",1,10); new_symbol(">",2,10);
new_symbol("<",3,10); new_symbol("has",4,10); new_symbol("near",5,10);
new_symbol("~=",6,10); new_symbol("<=",7,10); new_symbol(">=",8,10);
new_symbol("hasnt",9,10); new_symbol("far",10,10);
new_symbol("name",1,7);
CreateD_("ABBREVIATE", ABBREVIATE_CODE);
CreateD_("ATTRIBUTE", ATTRIBUTE_CODE);
CreateD_("CONSTANT", CONSTANT_CODE);
CreateD_("DICTIONARY", DICTIONARY_CODE);
CreateD_("END", END_CODE);
CreateD_("INCLUDE", INCLUDE_CODE);
CreateD_("GLOBAL", GLOBAL_CODE);
CreateD_("OBJECT", OBJECT_CODE);
CreateD_("PROPERTY", PROPERTY_CODE);
CreateD_("RELEASE", RELEASE_CODE);
CreateD_("SWITCHES", SWITCHES_CODE);
CreateD_("STATUSLINE", STATUSLINE_CODE);
CreateD_("VERB", VERB_CODE);
CreateD_("TRACE", TRACE_CODE);
CreateD_("NOTRACE", NOTRACE_CODE);
CreateD_("ETRACE", ETRACE_CODE);
CreateD_("NOETRACE", NOETRACE_CODE);
CreateD_("BTRACE", BTRACE_CODE);
CreateD_("NOBTRACE", NOBTRACE_CODE);
CreateD_("LTRACE", LTRACE_CODE);
CreateD_("NOLTRACE", NOLTRACE_CODE);
CreateD_("ATRACE", ATRACE_CODE);
CreateD_("NOATRACE", NOATRACE_CODE);
CreateD_("LISTSYMBOLS", LISTSYMBOLS_CODE);
CreateD_("LISTOBJECTS", LISTOBJECTS_CODE);
CreateD_("LISTVERBS", LISTVERBS_CODE);
CreateD_("LISTDICT", LISTDICT_CODE);
CreateD_("[", OPENBLOCK_CODE);
CreateD_("]", CLOSEBLOCK_CODE);
CreateD_("SERIAL", SERIAL_CODE);
CreateD_("DEFAULT", DEFAULT_CODE);
CreateD_("STUB", STUB_CODE);
CreateB_("PRINT_ADDR", PRINT_ADDR_CODE, 49);
CreateB_("PRINT_CHAR", PRINT_CHAR_CODE, 32);
CreateB_("PRINT_PADDR", PRINT_PADDR_CODE, 54);
CreateB_("PRINT_OBJ", PRINT_OBJ_CODE, 51);
CreateB_("PRINT_NUM", PRINT_NUM_CODE, 33);
CreateC_("REMOVE", REMOVE_CODE);
CreateC_("RETURN", RETURN_CODE);
CreateC_("DO", DO_CODE);
CreateC_("FOR", FOR_CODE);
CreateC_("IF", IF_CODE);
CreateC_("OBJECTLOOP", OBJECTLOOP_CODE);
CreateC_("UNTIL", UNTIL_CODE);
CreateC_("WHILE", WHILE_CODE);
CreateC_("BREAK", BREAK_CODE);
CreateC_("ELSE", ELSE_CODE);
CreateC_("GIVE", GIVE_CODE);
CreateC_("INVERSION", INVERSION_CODE);
CreateC_("MOVE", MOVE_CODE);
CreateC_("PUT", PUT_CODE);
CreateC_("WRITE", WRITE_CODE);
CreateC_("STRING", STRING_CODE);
CreateC_("FONT", FONT_CODE);
CreateA_("JE",0);
CreateA_("JLE",1);
CreateA_("JGE",2);
CreateA_("JZ",42);
CreateA_("JUMP",53);
CreateA_("READ",31);
CreateA_("RANDOM",34);
CreateA_("RET",52);
CreateA_(r1,57);
CreateA_(r2,58);
CreateA_("RTRUE",57);
CreateA_("RFALSE",58);
CreateA_("RESTORE",62);
CreateA_("RESTART",63);
CreateA_("RETSP",64);
CreateA_("REMOVE_OBJ",50);
CreateA_("PUT_PROP",30);
CreateA_("PUSH",35);
CreateA_("PULL",36);
CreateA_("PRINT",59);
CreateA_("PRINT_RET",60);
CreateA_("POP",65);
CreateA_("GET_SIBLING",43);
CreateA_("GET_CHILD",44);
CreateA_("GET_PARENT",45);
CreateA_("GET_PROP_LEN",46);
CreateA_("GET_PROP",17);
CreateA_("GET_PROP_ADDR",18);
CreateA_("GET_NEXT_PROP",19);
CreateA_("SET_ATTR",10);
CreateA_("STORE",12);
CreateA_("SUB",21);
CreateA_("STOREW",28);
CreateA_("STOREB",29);
CreateA_("STATUS_SIZE",37);
CreateA_("SET_WINDOW",38);
CreateA_("SET_PRINT",39);
CreateA_("SOUND",41);
CreateA_("SAVE",61);
CreateA_("SHOW_SCORE",68);
CreateA_("DEC_CHK",3);
CreateA_("INC_CHK",4);
CreateA_("COMPARE_POBJ",5);
CreateA_("TEST",6);
CreateA_("OR",7);
CreateA_("AND",8);
CreateA_("TEST_ATTR",9);
CreateA_("CLEAR_ATTR",11);
CreateA_("LSTORE",13);
CreateA_("INSERT_OBJ",14);
CreateA_("LOADW",15);
CreateA_("LOADB",16);
CreateA_("ADD",20);
CreateA_("MUL",22);
CreateA_("DIV",23);
CreateA_("MOD",24);
CreateA_("VJE",25);
CreateA_("CALL",26);
CreateA_("ICALL",27);
CreateA_("RECORD_MODE",40);
CreateA_("INC",47);
CreateA_("DEC",48);
CreateA_("LOAD",55);
CreateA_("NOT",56);
CreateA_("QUIT",66);
CreateA_("NEW_LINE",67);
CreateA_("VERIFY",69);
}
/* -------------------------------------------------------------------------------- */
/* Printing diagnostics */
/* -------------------------------------------------------------------------------- */
char *typename(int type)
{ switch(type)
{ case 1: return("Global label");
case 2: return("Global variable");
case 3: return("Local variable");
case 4: return("Reserved word");
case 5: return("Static string");
case 6: return("Local label");
case 7: return("Attribute");
case 8: return("Integer constant");
case 9: return("Object");
case 10: return("Condition");
case 11: return("Constant string address");
case 14: return("Assembler directive");
case 15: return("Compiler-modified opcode");
case 16: return("Compiled command");
case 17: return("Opcode");
default: return("(Unknown type)");
}
}
void list_symbols(void)
{ int i, j, k;
for (j=0; j<2; j++)
{ printf("In bank %d\n", j);
for (i=0; i<banksize[j]; i++)
{ k=banks[j][i];
printf("%4d %-16s %04x %s\n",
k,symbs[k],svals[k],typename(stypes[k]));
}
}
for (j=2; j<6; j++)
{ printf("In bank %d\n", j);
for (i=0; i<MAX_BANK_SIZE; i++)
{ k=banks[j][i];
if (k!=-1)
{ printf("%4d %-16s %04x %s\n",
k,symbs[k],svals[k],typename(stypes[k]));
}
}
}
printf("Full list:\n");
for (i=0; i<no_symbols; i++)
printf("%-16s %04x %s\n",
symbs[i],svals[i],typename(stypes[i]));
}
void list_object_tree(void)
{ int i;
printf("obj par nxt chl Object tree:\n");
for (i=0; i<no_objects; i++)
printf("%3d %3d %3d %3d\n",
i+1,objects[i].parent,objects[i].next, objects[i].child);
}
void list_verb_table(void)
{ int i, j, k;
for (i=0; i<no_verbs; i++)
{ printf("Verb entry %2d [%d]\n",i,vs[i].lines);
for (j=0; j<vs[i].lines; j++)
{ for (k=0; k<8; k++) printf("%03d ",vs[i].l[j].e[k]);
printf("\n");
}
}
}
/* -------------------------------------------------------------------------------- */
/* Keep track of actions */
/* -------------------------------------------------------------------------------- */
int make_action(int addr)
{ int i;
if (no_actions>=MAX_ACTIONS)
fatalerror("Limit on number of actions exceeded: \
increase #define MAX_ACTIONS");
for (i=0; i<no_actions; i++) if (actions[i]==addr) return(i);
actions[no_actions]=addr; preactions[no_actions]= -1;
return(no_actions++);
}
int find_action(int addr)
{ int i;
for (i=0; i<fp_no_actions; i++) if (actions[i]==addr) return(i);
if (pass_number==2) error("That's not an action routine");
return(0);
}
/* -------------------------------------------------------------------------------- */
/* Decode arguments as constants and variables */
/* (Gratuitous Space 1999 reference by Mr Dilip Sequeira of Edinburgh University) */
/* -------------------------------------------------------------------------------- */
int no_locals; char *nlp;
char lnb[MAX_IDENTIFIER_LENGTH+8];
char *localname(char *p)
{ sprintf(lnb,"#%d%s",no_routines,p);
return(lnb);
}
int cvflag;
int32 constant_value(char *b)
{ int32 i, j, k, base=10, f, rv, moon, alpha, victor;
cvflag=0;
if (b[0]=='#') b++;
if (b[0]=='\"')
{ dequote_text(b);
j= subtract_pointers(strings_p,strings);
#ifdef USE_TEMPORARY_FILES
{ char *c;
c=translate_text(strings,b);
i= subtract_pointers(c,strings);;
strings_p+=i;
if (pass_number==2)
{ for (c=strings; c<strings+i; c++)
{ fputc(*c,Temp1_fp); checksum_string+=(unsigned) *c; }
}
}
#else
strings_p=translate_text(strings_p,b);
i= subtract_pointers(strings_p,strings);
if (i>MAX_STATIC_STRINGS)
fatalerror("Constant strings space exhausted: \
increase #define MAX_STATIC_STRINGS.");
#endif
j=(strings_offset+j)/2;
/* printf("Translation at %d %04x\n",j,strings_offset); */
cvflag=1;
return(j);
}
if(*b=='$')
if(*++b=='$') {b++;base=2;} else base=16;
else for (i=0; b[i]; i++) if (!isdigit(b[i])) goto nonumber;
for(;isspace(*b);b++);
if(*b=='-') {victor=1; b++;} else victor=0;
for(moon=0;*b;b++)
{ alpha=isalpha(*b)?(tolower(*b)-'a'+10):*b-'0';
if(alpha>=base || alpha<0) break; else moon=moon*base+alpha;
}
return(victor?-moon:moon);
nonumber:
f=0;
if (strcmp(b,"adjectives_table")==0) return(adjectives_offset);
if (strcmp(b,"preactions_table")==0) return(preactions_offset);
if (strcmp(b,"actions_table")==0) return(actions_offset);
if ((b[0]=='a')&&(b[1]=='$')) { b+=2; f=1; }
if ((b[0]=='w')&&(b[1]=='$'))
{ k=dictionary_find(b+2,2);
rv=dictionary_offset+7*k;
if ((k==0)&&(pass_number==2))
error_named("Dictionary word not found for constant",b);
}
else
{ if ((b[0]=='n')&&(b[1]=='$'))
{ rv=dictionary_add(b+2,0x80,0,0);
}
else
{ if ((b[0]=='r')&&(b[1]=='$'))
{ i=find_symbol(b+2);
if (i<0)
{ if (pass_number==2)
error_named("Unrecognised constant value",b);
return(0);
}
rv=svals[i]; if (rv<256) rv=256;
}
else
{ i=find_symbol(b);
if (i<0)
{ if (pass_number==2)
error_named("Unrecognised constant value",b);
return(0);
}
rv=svals[i];
}
}
}
switch(stypes[i])
{ case 1: rv=(rv+code_offset)/2; break;
case 2:
case 3: error_named("Not a constant:",b); return(0);
case 4:
case 10: error_named("Reserved word as constant",b); return(0);
}
if (f==0) return(rv);
j=find_action(svals[i]);
return(j);
}
char known_unknowns[MAX_IDENTIFIER_LENGTH*16];
int no_knowns;
int32 parse_argument(char *b)
{ int i, flag=0;
if (b[0]=='#') return(1000+constant_value(b+1));
if ((b[0]=='$')||(b[0]=='\"')) return(1000+constant_value(b));
if ((b[0]==SINGLE_QUOTE)&&(b[2]==SINGLE_QUOTE))
{ return(1000+translate_to_ascii(b[1]));
}
for (i=0; b[i]!=0; i++) if (isdigit(b[i])==0) flag=1;
if (flag==0) return(1000+constant_value(b));
if (in_routine_flag==1)
{ i=local_find_symbol(b);
if (i>=0) return(1+svals[i]);
}
i=find_symbol(b);
if (i>=0)
{ switch(stypes[i])
{ case 1: return(1000+svals[i]);
case 2: return(16+svals[i]);
case 4: if (svals[i]==0) return(0);
case 7:
case 8:
case 9: return(1000+constant_value(b));
default: error_named("Type mismatch in argument",b);
return(0);
}
}
if (pass_number==2) { return(0); }
for (i=0; i<no_knowns; i++)
if (strcmp(known_unknowns+i*MAX_IDENTIFIER_LENGTH,b)==0)
return(0);
if (no_knowns<16)
strcpy(known_unknowns+(no_knowns++)*MAX_IDENTIFIER_LENGTH,b);
error_named("Unknown variable",b); return(0);
}
/* -------------------------------------------------------------------------------- */
/* Assembler of individual lines */
/* -------------------------------------------------------------------------------- */
void byteout(int i)
{ *zcode_p=(unsigned char) i; zcode_p++;
#ifdef USE_TEMPORARY_FILES
utf_zcode_p++;
#endif
if (subtract_pointers(zcode_p,zcode) >= MAX_ZCODE_SIZE)
{ fatalerror("Too much code: increase MAX_ZCODE_SIZE");
}
}
operand_t parse_operand(opcode o, char *b, int wn)
{ int32 j, opt; operand_t rval;
word(b,wn); j=parse_argument(b);
if (j>=1256) { opt=0; j=j-1000; }
else if (j>=1000) { opt=1; j=j-1000; }
else opt=2;
if ((o.type2==VAR)&&(opt==2)&&(wn==2)) opt=1;
rval.value=j; rval.type=opt;
return(rval);
}
void write_operand(operand_t op)
{ int32 j;
j=op.value;
if (j<256) byteout(j);
else { byteout(j/256); byteout(j%256); }
}
int assemble_opcode(char *b, int32 offset, opcode opco)
{ char *opc, *opcname, *ac;
int32 j, topbits, fullcode, addr, cargs, ccode, oldccode,
multi, mask, flag, longf, branchword;
operand_t oper1, oper2;
return_flag=0;
if (opco.type1==RETURN) return_flag=1;
opcname=opco.name;
switch(opco.no)
{ case VARI: topbits=0xc0; break;
case ZERO: topbits=0xb0; break;
case ONE: topbits=0x80; break;
case TWO: topbits=0x00; break;
}
fullcode=topbits+opco.code; opc=zcode_p;
if (opco.type1==INDIR)
{ byteout(0xE0); byteout(0xBF); byteout(0); byteout(0);
goto Line_Done;
}
byteout(fullcode);
if (opco.type1==JUMP)
{ word(b,2);
if (pass_number==1) addr=0;
else
{ j=find_symbol(b);
if (j<0) { no_such_label(b); return(1); }
if (stypes[j]!=6) { error_named("Not a label:",b); return(1); }
addr=svals[j]-offset-1;
if (addr<0) addr+=(int32) 0x10000L;
}
byteout(addr/256); byteout(addr%256);
goto Line_Done;
}
if (opco.type2==TEXT)
{ char *tmp;
textword(b,2); tmp=zcode_p; zcode_p=translate_text(zcode_p,b);
j=subtract_pointers(zcode_p,tmp);
#ifdef USE_TEMPORARY_FILES
utf_zcode_p+=j;
#endif
}
switch(opco.no)
{ case VARI:
ac=zcode_p; byteout(0);
cargs= -1; ccode=0xff;
while (word(b,(++cargs)+2),(b[0]!=0))
{ if (b[0]=='?') { branchword=cargs+2; break; }
switch(cargs)
{ case 0: multi=0x40; mask=0xc0; break;
case 1: multi=0x10; mask=0x30; break;
case 2: multi=0x04; mask=0x0c; break;
case 3: multi=0x01; mask=0x03; break;
case 4: multi=0;
if ((opco.type1!=CALL)&&(opco.type1!=STORE))
error("Too many arguments");
break;
default: error("Too many arguments"); break;
}
if ((opco.type1==CALL)&&(cargs==0))
{ if (pass_number==2)
{ j=find_symbol(b);
if (j==-1) { no_such_label(b); }
if (stypes[j]!=1) { error_named("Not a label:",b); }
oper1.value=(code_offset+svals[j])/2; oper1.type=0;
}
else { oper1.value=0x1000; oper1.type=0; }
}
else
oper1=parse_operand(opco,b,cargs+2);
write_operand(oper1);
oldccode=ccode; ccode = (ccode & (~mask)) + oper1.type*multi;
}
if ((opco.type1==CALL)||(opco.type1==STORE))
{ if (oper1.type!=2) { error("Can't store to that"); }
*ac=oldccode;
}
else *ac=ccode;
break;
case ONE:
oper1=parse_operand(opco,b,2);
*opc=(*opc) + oper1.type*0x10;
write_operand(oper1);
break;
case TWO:
oper1=parse_operand(opco,b,2);
oper2=parse_operand(opco,b,3);
if ((oper1.type==0)||(oper2.type==0))
{ *opc=(*opc) + 0xc0; byteout(oper1.type*0x40 + oper2.type*0x10 + 0x0f);
}
else
{ if (oper1.type==2) *opc=(*opc) + 0x40;
if (oper2.type==2) *opc=(*opc) + 0x20;
}
write_operand(oper1);
write_operand(oper2);
break;
case ZERO:
break;
}
if ((opco.no==ONE) || (opco.no==TWO))
{ if (opco.type1==STORE)
{ if (opco.no==ONE) oper1=parse_operand(opco,b,3);
if (opco.no==TWO) oper1=parse_operand(opco,b,4);
if (oper1.type!=2) { error("Can't store to that"); }
byteout(oper1.value);
}
}
if ((opco.type1==BRANCH)||(opco.type2==OBJECT))
{ int o=0, pca;
pca= subtract_pointers(zcode_p,opc) -1;
switch(opco.no)
{ case ZERO: word(b,2); break;
case ONE: if (opco.type2!=OBJECT) { word(b,3); break; }
case TWO: word(b,4); break;
case VARI: word(b,branchword); break;
}
if (b[0]=='?') { longf=1; o++; }
else
{ int o2=0;
if (b[0]=='~') o2=1;
if (pass_number==1)
{ j=find_symbol(b+o2);
if (j<0) longf=0;
else longf=1;
}
else
{ j=find_symbol(b+o2);
if (j<0) longf=0;
else
{ if (offset-svals[j]>0) longf=1;
else longf=0;
if ((svals[j]-offset-pca)>30)
{ error("Branch too far forward: use '?'"); return(1); }
}
}
}
/* printf("Branch at %04x has longf=%d\n",offset,longf); */
if (pass_number==1) { byteout(0); if (longf==1) byteout(0); }
else
{ if (b[o]=='~') { flag=0; o++; } else flag=1;
j=find_symbol(b+o);
if (j<0) { no_such_label(b+o); return(1); }
switch(stypes[j])
{ case 4:
switch(svals[j])
{ case 1: addr=1; longf=0; break;
case 2: addr=0x20; longf=0; break;
default: error("Unknown return condition"); return(1);
}
break;
case 1: error("Can't branch to a routine label"); return(1);
case 6:
if (longf==1) pca++;
addr=svals[j]-offset-pca;
if (addr<0) addr+=(int32) 0x10000L; break;
default: error_named("Not a label:",b+o); return(1);
}
addr=addr&0x3fff;
if (longf==1)
{ byteout(flag*0x80 + addr/256); byteout(addr%256); }
else
byteout(flag*0x80+ 0x40 + (addr&0x3f));
}
}
Line_Done:
if (trace_mode==1)
{ printf("%04d %04x %-14s ", current_source_line(),offset,opcname);
for (j=0;opc<zcode_p; j++, opc++)
{ printf("%02x ", *opc);
if (j%16==15) printf("\n ");
}
printf("\n");
}
#ifdef USE_TEMPORARY_FILES
{ char *c; int i;
i= subtract_pointers(zcode_p,zcode);
if (pass_number==2)
{ for (c=zcode; c<zcode+i; c++)
{ fputc(*c,Temp2_fp); checksum_code+=(unsigned) *c; }
}
zcode_p=zcode;
}
#endif
return(1);
}
/* -------------------------------------------------------------------------------- */
/* Parsing the grammar table, and making new adjectives and verbs */
/* (see the documentation for what the verb table it makes looks like) */
/* -------------------------------------------------------------------------------- */
#define On_(x) if (strcmp(b,x)==0)
#define IfPass2 if (pass_number==2)
int make_adjective(char *c)
{ int i; char dump[32]; int32 acomp;
abbrev_mode=0; translate_text(dump,c); abbrev_mode=1;
acomp=sort_number(c);
for (i=0; i<no_adjectives; i++)
{ if (acomp==adjcomps[i]) return(0xff-i);
}
adjectives[no_adjectives]=dictionary_add(c,8,0,0xff-no_adjectives);
adjcomps[no_adjectives]=acomp;
return(0xff-no_adjectives++);
}
void make_verb(char *b)
{ int i, j, k, flag=0;
int lines=0, vargs, vtokens, vinsert;
i=2;
if (no_verbs==MAX_VERBS)
{ error("Too many verbs: increase #define MAX_VERBS"); return; }
word(b,i);
On_("meta") { i++; flag=2; }
do
{ word(b,i);
if (b[0]!='\"') break;
textword(b,i++);
dictionary_add(b,0x41+flag,0xff-no_verbs,0);
} while (1==1);
do
{ word(b,i++); flag=2;
if (b[0]==0) break;
if (strcmp(b,"*")!=0)
{ error_named("* divider expected, but found",b); return; }
vtokens=1; vargs=0; for (j=0; j<8; j++) vs[no_verbs].l[lines].e[j]=0;
do
{ word(b,i++);
if (b[0]==0) { error("-> clause missing"); return; }
if (strcmp(b,"->")==0) break;
if (b[0]=='\"')
{ textword(b,i-1); vinsert=make_adjective(b);
}
else On_("noun") { vargs++; vinsert=0; }
else On_("held") { vargs++; vinsert=1; }
else On_("multi") { vargs++; vinsert=2; }
else On_("multiheld") { vargs++; vinsert=3; }
else On_("multiexcept") { vargs++; vinsert=4; }
else On_("multiinside") { vargs++; vinsert=5; }
else On_("creature") { vargs++; vinsert=6; }
else On_("special") { vargs++; vinsert=7; }
else { error_named("Unknown particle of grammar",b); return; }
vs[no_verbs].l[lines].e[vtokens]=vinsert;
vtokens++;
} while (1==1);
word(b,i++);
j=find_symbol(b);
if ((j==-1)&&(pass_number==2)) { no_such_label(b); return; }
if (j==-1) k=0;
else
{ if (stypes[j]!=1) { error_named("Not a label",b); return; }
k=svals[j];
}
vs[no_verbs].l[lines].e[0]=vargs;
vs[no_verbs].l[lines].e[7]=make_action(k);
lines++;
} while (1==1);
if (lines==0) error("No lines of grammar given for verb");
vs[no_verbs].lines=lines;
no_verbs++;
}
/* -------------------------------------------------------------------------------- */
/* Object manufacture. Note that property lists are not kept for each object, */
/* only written in game-file format and then forgotten; but the object tree */
/* structure so far, is kept */
/* -------------------------------------------------------------------------------- */
int properties(int w)
{ int i, j, x, y; char b[BUFFER_LENGTH];
do
{ word(b,w++);
if (b[0]==0) return(w-1);
if (strcmp(b,"has")==0) return(w-1);
i=find_symbol(b);
if ((i==-1)||(stypes[i]!=7))
{ error_named("Bad property name",b); return(w); }
i=svals[i];
x=full_object.l++;
full_object.pp[x].num=i;
y=0;
do
{ word(b,w++);
if (strcmp(b,",")==0) break;
if (strcmp(b,"has")==0) { w--; break; }
if (b[0]==0) break;
if (i==1)
{ textword(b,w-1); j=dictionary_add(b,0x80,0,0); }
else
{ j=constant_value(b);
if (j==0)
{ if (prop_defaults[i]>=256) j=0x1000;
}
}
if ((j>=256)||(prop_longflag[i]==1))
full_object.pp[x].p[y++]=j/256;
full_object.pp[x].p[y++]=j%256;
} while (1==1);
full_object.pp[x].l=y;
} while (1==1);
return(0);
}
int attributes(int w)
{ int i; char b[BUFFER_LENGTH]; uint32 z;
do
{ word(b,w++);
if (b[0]==0) return(w-1);
if (strcmp(b,"with")==0) return(w-1);
i=find_symbol(b);
if ((i==-1)||(stypes[i]!=7))
{ error_named("Bad attribute name",b); return(w); }
i=svals[i];
z= ((int32) 0x1000000L)*objects[no_objects].atts[0]
+ ((int32) 0x10000L)*objects[no_objects].atts[1]
+ ((int32) 0x100L)*objects[no_objects].atts[2]
+ objects[no_objects].atts[3];
z=z | (1<<(31-i));
objects[no_objects].atts[0]=z/((int32) 0x1000000L);
objects[no_objects].atts[1]=z/((int32) 0x10000L);
objects[no_objects].atts[2]=z/((int32) 0x100L);
objects[no_objects].atts[3]=z;
} while (1==1);
return(0);
}
int write_properties(char *p, char *shortname)
{ int props=0, oldprops=0, l, j, k; char *tmp;
p[props]=3;
oldprops=props;
tmp=translate_text(p+props+1,shortname);
props=subtract_pointers(tmp,p);
p[oldprops]=(props-oldprops-1)/2;
for (l=31; l>0; l--)
{ for (j=0; j<full_object.l; j++)
{ if (full_object.pp[j].num == l)
{ p[props++]=full_object.pp[j].num + (full_object.pp[j].l - 1)*32;
for (k=0; k<full_object.pp[j].l; k++)
{ p[props++]=full_object.pp[j].p[k];
}
}
}
}
p[props]=0; props++;
properties_size+=props;
if (properties_size >= MAX_PROP_TABLE_SIZE)
fatalerror("Properties table exhausted: \
increase #define MAX_PROP_TABLE_SIZE");
return(props);
}
void make_object(char *b)
{ int i, j, k, non=0;
char object_shortname[100];
word(b,2); new_symbol(b,no_objects+1,9);
do
{ word(b,3+non);
if (b[0]!='\"') { new_symbol(b,no_objects+1,9); non++; }
} while (b[0]!='\"');
textword(b,3+non);
strcpy(object_shortname,b);
word(b,4+non);
i=find_symbol(b);
if (i<0) { error_named("No such object as",b); return; }
if (stypes[i]!=9) { error_named("Not an object:",b); return; }
objects[no_objects].atts[0]=0;
objects[no_objects].atts[1]=0;
objects[no_objects].atts[2]=0;
objects[no_objects].atts[3]=0;
objects[no_objects].parent=svals[i];
objects[no_objects].next=0;
objects[no_objects].child=0;
full_object.l=0;
if (svals[i]>0)
{ j=svals[i]-1; k=objects[j].child;
if (k==0)
{ objects[j].child=no_objects+1; }
else
{ while(objects[k-1].next!=0) { k=objects[k-1].next; }
objects[k-1].next=no_objects+1;
}
}
j=5+non;
do
{ word(b,j++);
if (b[0]==0) break;
if (strcmp(b,"with")==0) j=properties(j);
else if (strcmp(b,"has")==0) j=attributes(j);
else error("Expected \"with\" or \"has\" in object definition");
} while (1==1);
j=objects[no_objects].propsize;
objects[no_objects].propsize=
write_properties(properties_table+properties_size,object_shortname);
if (pass_number==2)
{ if (j != objects[no_objects].propsize)
{ error("Object has altered in memory usage between passes");
}
}
no_objects++;
}
/* -------------------------------------------------------------------------------- */
/* Making (and, which is trickier, initialising) globals and global arrays */
/* -------------------------------------------------------------------------------- */
int32 gcs[MAX_GCONSTANTS], gcvals[MAX_GCONSTANTS];
void fix_gconstants(char *globs)
{ int i;
for (i=0; i<no_gconstants; i++)
globs[gcs[i]]=gcvals[i];
}
void make_global(char *b)
{ int i, j, iflag, sflag;
if (pass_number>1) return;
word(b,2); new_symbol(b,no_globals,2); no_globals++;
word(b,3); iflag=0;
if (b[0]==0) return;
if (strcmp(b,"string")==0) sflag=1;
else if (strcmp(b,"data")==0) sflag=0;
else if (strcmp(b,"initial")==0) { sflag=0; iflag=1; }
else if (strcmp(b,"initstr")==0) { sflag=0; iflag=2; }
else
{ if (strcmp(b,"=")!=0)
{ error_named("Expected \"string\", \"data\", \"initial\", \
\"initstr\" or \"=\" but found",b); return; }
word(b,4); i=constant_value(b);
gvalues[no_globals-1]=i; gflags[no_globals-1]=0;
return;
}
if (iflag==0)
{ word(b,4);
i=constant_value(b);
}
if (iflag==1)
{ i=0;
do
{ word(b,4+i); if (b[0]==0) break;
j=constant_value(b); table_init[globals_size+i]=j; i++;
} while (1==1);
}
if (iflag==2)
{ textword(b,4);
for (i=0; b[i]!=0; i++)
{ j=b[i]; table_init[globals_size+i]=j;
}
}
if (sflag==1)
{ gcs[no_gconstants]=globals_size; gcvals[no_gconstants++]=i++;
}
gvalues[no_globals-1]=globals_size;
gflags[no_globals-1]=2;
globals_size+=i;
}
/* -------------------------------------------------------------------------------- */
/* Assembler directives: for diagnosis, and making the non-code part of the file */
/* -------------------------------------------------------------------------------- */
void switches(char *, int);
void assemble_label(int offset, char *b)
{ int i;
if (pass_number==1) new_symbol(b+1,offset,6);
else
{ i=find_symbol(b+1);
if (svals[i]!=offset)
error("A label has moved between passes. This suggests either \
a problem in Inform, or an improper use of a routine address as a constant value");
}
if (trace_mode==1) printf(".%s\n",b+1);
return_flag=0;
}
void stack_sline(char *s1, char *b)
{ char rw[100];
sprintf(rw,s1,b); stack_line(rw);
}
void assemble_directive(char *b, int offset, int code)
{ int i, j;
switch(code)
{ case OPENBLOCK_CODE:
{ if ((offset%2) ==1) { byteout(0); offset++; }
word(b,2);
new_symbol(b,offset,1);
if (trace_mode==1) printf("<Routine %d, '%s' begins at %04x; ",
no_routines,b,offset);
no_locals= -1; no_knowns=0;
routine_starts_line=current_source_line();
no_routines++; in_routine_flag=1; return_flag=0;
while (word(b,(++no_locals)+3),(b[0]!=0))
new_symbol(b,no_locals,3);
byteout(no_locals);
for (i=0; i<no_locals; i++) { byteout(0); byteout(0); }
if (trace_mode==1) printf("%d locals>\n",no_locals);
if (no_locals>15) error("Routine has more than 15 local variables");
if ((no_routines==1)&&(pass_number==1))
{ word(b,2); make_lower_case(b);
if (strcmp(b,"main")!=0)
{ warning_named("Control will begin from the routine",b);
}
if (no_locals!=0)
error("The \"Main\" routine is not allowed to have local variables");
}
if (nowarnings_mode==0)
for (i=0; i<16; i++) used_local_variable[i]=0;
break;
}
case CLOSEBLOCK_CODE:
{ if (trace_mode==1) printf("<Routine ends>\n");
if (return_flag==0) stack_line(" @ret#true");
if (brace_sp>0)
{ error("Brace mismatch in previous routine");
brace_sp=0;
}
in_routine_flag=0;
if ((nowarnings_mode==0)&&(pass_number==1))
{ for (i=0; i<no_locals; i++)
{ if (used_local_variable[i]==0)
{ override_error_line = routine_starts_line;
warning_named("Local variable unused:",
local_varname[i]);
}
}
}
break;
}
case ABBREVIATE_CODE:
{ textword(b,2);
if (pass_number==1)
{ if (no_abbrevs==MAX_ABBREVS)
{ error("Too many abbreviations declared"); break; }
if (almade_flag==1)
{ error("All abbreviations must be declared together"); break; }
if (strlen(b)<2)
{ error("Abbreviation not worth bothering with"); break; }
}
strcpy(abbreviations_at+no_abbrevs*MAX_ABBREV_LENGTH, b);
word(b,2);
abbrev_mode=0;
abbrev_values[no_abbrevs]=constant_value(b);
abbrev_quality[no_abbrevs++]=trans_length-2;
abbrev_mode=1;
break;
}
case ATTRIBUTE_CODE:
{ if (no_attributes==32)
{ error("All 32 attributes already declared."); return; }
word(b,2); new_symbol(b,no_attributes++,7); break; }
case CONSTANT_CODE:
{ word(b,3);
IfPass2
{ i=constant_value(b);
word(b,2);
i=find_symbol(b);
if (stypes[i]==11)
svals[i]=svals[i]-0x400+strings_offset/2;
}
else
{ i=constant_value(b);
word(b,2);
if (cvflag==1) new_symbol(b,i,11);
else new_symbol(b,i,8);
}
break;
}
case DEFAULT_CODE:
{ word(b,3);
IfPass2
{
}
else
{ i=constant_value(b);
word(b,2);
if (find_symbol(b)==-1)
{ if (cvflag==1)
{ error("Defaulted constants can't be strings"); return; }
new_symbol(b,i,8);
}
}
break;
}
case STUB_CODE:
{ i=0;
IfPass2 { if (stub_flags[no_stubbed++]==1) i=1; }
else
{ word(b,2); if (find_symbol(b)==-1) i=1;
stub_flags[no_stubbed++]=i;
}
if (i==1)
{
word(b,3); i=constant_value(b); word(b,2);
switch(i)
{ case 0: stack_sline("[ %s",b); stack_line("rfalse"); stack_line("]");
break;
case 1: stack_sline("[ %s x1",b); stack_line("@store x1 0");
stack_line("rfalse"); stack_line("]"); break;
case 2: stack_sline("[ %s x1 x2",b); stack_line("@store x1 0"); stack_line("@store x2 0");
stack_line("rfalse"); stack_line("]");
case 3: stack_sline("[ %s x1 x2 x3",b);
stack_line("@store x1 0"); stack_line("@store x2 0"); stack_line("@store x3 0");
stack_line("rfalse"); stack_line("]");
default: error("Must specify 0 to 3 variables in stubbed routine");
return;
}
}
break;
}
case DICTIONARY_CODE:
{ textword(b,3); i=dictionary_add(b,4,0,0);
word(b,2);
IfPass2
{ j=find_symbol(b); svals[j]=i; } else new_symbol(b,i,8);
break;
}
case END_CODE:
{ endofpass_flag=1;
if (trace_mode==1) printf("<end>\n");
break;
}
case INCLUDE_CODE:
{ textword(b,2); load_sourcefile(b); break;
}
case GLOBAL_CODE: { make_global(b); break; }
case OBJECT_CODE: { make_object(b); break; }
case PROPERTY_CODE:
{ int32 def=0, fl=0;
if (no_properties==32)
{ error("All 30 properties already declared."); return; }
word(b,2);
if (strcmp(b,"long")==0) { fl=1; }
word(b,3+fl);
if (b[0]!=0) def=constant_value(b);
word(b,2+fl); prop_defaults[no_properties]=def;
prop_longflag[no_properties]=fl;
new_symbol(b,no_properties++,7); break;
}
case RELEASE_CODE:
{ word(b,2); release_number=constant_value(b); break; }
case SWITCHES_CODE:
{ if (ignoreswitches_mode==0) { word(b,2); switches(b,0); }
break;
}
case STATUSLINE_CODE:
{ word(b,2);
On_("score") { statusline_flag=0; break; }
On_("time") { statusline_flag=1; break; }
error("Status line must be \"score\" or \"time\""); break;
}
case SERIAL_CODE:
{ textword(b,2);
if (strlen(b)!=6)
{ error("The serial number must be a 6-digit date"); break; }
for (i=0; i<6; i++)
if (isdigit(b[i])==0)
{ error("The serial number must be a 6-digit date"); break; }
strcpy(time_given,b); time_set=1; break;
}
case VERB_CODE: { make_verb(b); break; }
case TRACE_CODE: { IfPass2 trace_mode=1; break; }
case NOTRACE_CODE: { IfPass2 trace_mode=tracing_mode; break; }
case ETRACE_CODE: { IfPass2 etrace_mode=1; break; }
case NOETRACE_CODE: { IfPass2 etrace_mode=0; break; }
case BTRACE_CODE: { trace_mode=1; break; }
case NOBTRACE_CODE: { trace_mode=0; break; }
case LTRACE_CODE: { IfPass2 ltrace_mode=1; break; }
case NOLTRACE_CODE: { IfPass2 ltrace_mode=listing_mode; break; }
case ATRACE_CODE: { IfPass2 ltrace_mode=2; break; }
case NOATRACE_CODE: { IfPass2 ltrace_mode=listing_mode; break; }
case LISTSYMBOLS_CODE: { IfPass2 list_symbols(); break; }
case LISTOBJECTS_CODE: { IfPass2 list_object_tree(); break; }
case LISTVERBS_CODE: { IfPass2 list_verb_table(); break; }
case LISTDICT_CODE: { IfPass2 show_dictionary(); break; }
default: error("Internal error - no such directive code");
}
return;
}
/* -------------------------------------------------------------------------------- */
/* Compiler expression evaluator */
/* */
/* This works in a rather lugubrious way, making a tree out of the expression and */
/* then clearing it off again by clipping off nodes and stacking up corresponding */
/* assembly lines. The tricky point is to get it in the right order, since the */
/* stack can't very conveniently be re-ordered. */
/* */
/* Logically this process ought to end with an answer on the stack assigned to */
/* something, but a little optimisation avoids the waste of lines like */
/* sp=23+i, j=sp and instead does the obvious j=23+i */
/* */
/* To see it working in grisly detail, try compiling with expression tracing on */
/* -------------------------------------------------------------------------------- */
int treenodes, tlevel, tflag, targ, top_exps, tsave;
void show_tree(char *c)
{ int i, j; char b[BUFFER_LENGTH];
printf("%s\n",c);
for (i=0; i<treenodes; i++)
{ printf("%d %s up=%d ",i,(i==tlevel)?"*":" ",woods[i].up);
for (j=0; j<woods[i].arity; j++)
{ if (j<woods[i].gcount) printf("%d ",woods[i].g[j]);
else printf(". ");
}
switch(woods[i].type)
{ case -3: printf(" <root>"); break;
case -2: printf(" <blank>"); break;
case -1: printf(" <sp>"); break;
case 0: word(b,woods[i].wnumber); printf(" <leaf '%s'>",b); break;
default: printf(" %s",woods[i].op); break;
}
if (woods[i].priority!=0) { printf(" (%d)",woods[i].priority); }
printf("\n");
}
}
void sprout(int a, int wn, int type, char *opcode, int prio)
{ int i, tup;
if (a>=MAX_ARITY)
{ error("A function may be called with up to 3 arguments only"); return; }
tup=woods[tlevel].up;
if ((tflag==0)&&(targ==0))
{ error("Operator has too few arguments"); return; }
if (targ!=0) { targ=0; tsave=tlevel; }
while (woods[tup].priority>prio)
{ tlevel=tup; tup=woods[tlevel].up;
}
while (woods[tlevel].type== -2)
{ for (i=1; i<woods[tup].arity; i++)
if (woods[tup].g[i]==tlevel)
{ tlevel=woods[tup].g[i-1]; break; }
}
woods[treenodes]=woods[tlevel];
woods[tlevel].arity=a;
woods[tlevel].wnumber=wn;
woods[tlevel].gcount=1;
woods[tlevel].type=type;
woods[tlevel].op=opcode;
woods[tlevel].priority=prio;
woods[tlevel].g[0]=treenodes;
woods[treenodes++].up=tlevel;
for (i=1; i<a; i++)
{ woods[tlevel].g[i]=treenodes;
woods[treenodes].type= -2;
woods[treenodes].arity=0;
woods[treenodes].gcount=0;
woods[treenodes].priority=0;
woods[treenodes++].up=tlevel;
}
tlevel=woods[tlevel].g[1];
tflag=0;
if (etrace_mode==1) show_tree("Sprout to");
}
void attach(int wn)
{ int tup;
tup=woods[tlevel].up;
woods[tlevel].arity=0;
woods[tlevel].wnumber=wn;
woods[tlevel].gcount=0;
woods[tlevel].type=0;
woods[tlevel].priority=0;
woods[tup].gcount++;
if (woods[tup].gcount<woods[tup].arity)
{ tlevel=woods[tup].g[woods[tup].gcount];
tflag=0;
}
else tflag=1;
if (etrace_mode==1) show_tree("Attach to");
return;
}
void eword(char *b, int bn)
{ if (woods[bn].type==-1) strcpy(b,"sp");
else word(b,woods[bn].wnumber);
/* printf("Eword %d -> %d <%s>\n",bn,woods[bn].wnumber,b); */
}
int exp_hwm;
int expression(int fromword)
{ int i, j, pp, t, countas, brackets[32], blev, clev, npri=0, sarity, sflag;
char rwb[BUFFER_LENGTH], b[BUFFER_LENGTH];
woods[0].up= -1;
woods[0].type= -3;
woods[0].g[0]=1;
woods[0].arity=1;
woods[0].gcount=0;
woods[0].priority=0;
treenodes=2; targ=0;
tlevel=1; tflag=0; woods[1].up=0; woods[1].type= -2; woods[1].priority=0;
blev=0;
pp=fromword;
do
{ word(b,pp++);
if (b[0]==0) break;
On_("=") sprout(2,pp-1,1,"store",2+npri);
else On_("+") sprout(2,pp-1,2,"add", 3+npri);
else On_("-") sprout(2,pp-1,2,"sub", 3+npri);
else On_("*") sprout(2,pp-1,2,"mul", 4+npri);
else On_("/") sprout(2,pp-1,2,"div", 4+npri);
else On_("%") sprout(2,pp-1,2,"mod", 4+npri);
else On_("&") sprout(2,pp-1,2,"and", 4+npri);
else On_("|") sprout(2,pp-1,2,"or", 4+npri);
else On_("->") sprout(2,pp-1,2,"loadb",5+npri);
else On_("-->") sprout(2,pp-1,2,"loadw",5+npri);
else On_(",")
{ if ((blev==0)||(brackets[blev-1]==1))
error("Spurious comma");
targ=1;
if (woods[tlevel].type!=-2)
{ tlevel=woods[tsave].up;
for (i=0; i<woods[tlevel].arity; i++)
if (woods[woods[tlevel].g[i]].type==-2)
{ tlevel=woods[tlevel].g[i]; break; }
}
}
else On_("(")
{ npri+=10;
if (tflag==0)
{ brackets[blev++]=1;
}
else
{ brackets[blev++]=0;
j=pp; countas=2; clev=blev; word(b,pp);
On_(")") countas=1;
else
do { word(b,j++);
if (b[0]==0) { error("Missing ) in function call"); break; }
if ((strcmp(b,",")==0)&&(blev==clev)) countas++;
On_("(") clev++;
On_(")") clev--;
} while (clev>=blev);
sprout(countas,pp-1,2,"call",1+npri);
targ=1;
}
}
else On_(")")
{ if (blev--==0) break;
npri-=10;
}
else if ((tflag==1)&&(targ==0))
{ if (blev>0) error("Operator has too many arguments");
break;
}
else if (woods[tlevel].type==-2) attach(pp-1);
else break;
} while (1==1);
if (b[0]==0) exp_hwm= -1; else exp_hwm=pp-1;
if (blev>0) error("Too many (s in expression");
if (etrace_mode==1) show_tree("Made the tree:");
do
{ i=0;
DownDown:
for (j=woods[i].gcount-1; j>=0; j--)
{ t=woods[woods[i].g[j]].type;
if ((t!=0)&&(t!=-1)) { i=woods[i].g[j]; goto DownDown; }
}
if (etrace_mode==1) printf("Detaching %d\n",i);
if (i==0)
{ if (woods[woods[0].g[0]].type==-1) j= -1;
else j=woods[woods[0].g[0]].wnumber;
if (etrace_mode==1) printf("Answer is in %d and word is %d\n",j,exp_hwm);
return(j);
}
if (woods[i].gcount<woods[i].arity)
error("Not enough arguments in operator");
sarity=0; sflag=0;
if (strcmp(woods[i].op,"call")==0)
{ eword(b,woods[i].g[0]);
On_("parent") { sarity=1; woods[i].op="get_parent"; }
On_("sibling") { sarity=1; woods[i].op="get_sibling"; sflag=1; }
On_("child") { sarity=1; woods[i].op="get_child"; sflag=1; }
On_("random") { sarity=1; woods[i].op="random"; }
On_("prop_len") { sarity=1; woods[i].op="get_prop_len"; }
On_("prop_addr") { sarity=2; woods[i].op="get_prop_addr"; }
On_("prop") { sarity=2; woods[i].op="get_prop"; }
On_("indirect")
{ if (woods[i].gcount<2)
{ error("Two few arguments on indirect function call"); }
sarity=1; woods[i].op="icall";
}
else
if (sarity!=0)
{ if (sarity+1!=woods[i].gcount)
{ error("Wrong number of arguments to system function");
}
}
}
sprintf(rwb,"@%s ",woods[i].op);
t=woods[i].type;
if (sarity>0) sarity=1;
for (j=sarity; j<woods[i].gcount; j++)
{ eword(b,woods[i].g[j]);
sprintf(rwb+strlen(rwb),"%s ",b);
}
if (t!=1)
{ if (woods[woods[i].up].type==1)
{ word(b,woods[woods[woods[i].up].g[0]].wnumber);
sprintf(rwb+strlen(rwb),"%s",b);
i=woods[i].up; t=1;
}
else sprintf(rwb+strlen(rwb),"sp");
}
if (sflag==1)
{ sprintf(rwb+strlen(rwb)," _x%d",no_dummy_labels);
}
stack_line(rwb);
if (sflag==1)
{ sprintf(rwb,"@._x%d",no_dummy_labels++);
stack_line(rwb);
}
if (t==1) woods[i]=woods[woods[i].g[0]];
else
{ woods[i].arity=0;
woods[i].type= -1;
woods[i].gcount=0;
}
if (etrace_mode==1) show_tree("to");
} while (1==1);
return(1);
}
/* -------------------------------------------------------------------------------- */
/* Compiler top level: block structures, loops, if statements and commands */
/* (all using the expression evaluator) */
/* -------------------------------------------------------------------------------- */
int objlb[MAX_OLDEPTH]; char objlv[36][MAX_OLDEPTH]; int oldepth=0;
char forvariable[MAX_IDENTIFIER_LENGTH];
void cword(char *b, int n)
{ if (n==-1) strcpy(b,"sp");
else word(b,n);
}
void compile_openbrace()
{
char rwb[BUFFER_LENGTH];
brace_stack[brace_sp++]=next_block_type+no_blocks_made++;
if (forloop_flag==1)
{ sprintf(rwb,"@inc %s",forvariable);
stack_line(rwb);
forloop_flag=0;
}
}
void compile_closebrace(char *b)
{ int j;
char rwb[BUFFER_LENGTH];
get_next_line(); tokenise_line(); word(b,1);
make_upper_case(b);
j=brace_stack[--brace_sp];
if (j>=10000)
{ j-=10000;
if (oldepth>0)
{ if (brace_sp==objlb[oldepth-1])
{ oldepth--;
sprintf(rwb,"@get_sibling %s %s _x%d",
objlv[oldepth],objlv[oldepth],no_dummy_labels);
stack_line(rwb);
sprintf(rwb,"@._x%d",no_dummy_labels++); stack_line(rwb);
}
}
sprintf(rwb," @jump _w%d",j);
stack_line(rwb);
}
On_("ELSE")
{ sprintf(rwb,"@jump _f%d",no_blocks_made);
stack_line(rwb);
}
sprintf(rwb,"@._f%d",j); return_flag=0;
stack_line(rwb);
stack_line(buffer);
}
void rearrange_stack(int a, int b, int c)
{
if ((a==-1)&&(b==-1)&&(c==-1))
{ stack_line(" @pull #253");
stack_line(" @pull #254");
stack_line(" @pull #255");
stack_line(" @push #253");
stack_line(" @push #254");
stack_line(" @push #255"); return;
}
if ( ((a==-1)&&(b==-1))
|| ((a==-1)&&(c==-1))
|| ((b==-1)&&(c==-1)))
{ stack_line(" @pull #254");
stack_line(" @pull #255");
stack_line(" @push #254");
stack_line(" @push #255");
}
}
void compiler(char *b, int code)
{ int i, j, k, trans, pflag, dir, labnum,
brace_spc, doexp, dofrom, popflag, cnum;
char rwb[BUFFER_LENGTH];
char *cond="";
doexp=0; trans=0;
switch(code)
{ case ASSIGNMENT_CODE: doexp=1; dofrom=1; popflag=0; goto DoExpression;
case FUNCTION_CODE: doexp=1; dofrom=1; popflag=1; goto DoExpression;
case DO_CODE: return;
case FOR_CODE:
{ forloop_flag=1;
word(b,2);
strcpy(forvariable,b);
i=expression(3);
cword(b,i);
sprintf(rwb," @store %s %s",forvariable,b);
stack_line(rwb);
sprintf(rwb," @dec %s",forvariable);
stack_line(rwb);
if (exp_hwm==-1) { error("'to' missing"); return; }
word(b,exp_hwm++);
if (strcmp(b,"to")!=0) { error("'to' expected"); return; }
word(b,exp_hwm);
if (b[0]==0) { error("Final value missing"); return; }
i=expression(exp_hwm);
if (exp_hwm!=-1) { error("'{' expected after for"); return; }
if (i==-1) { error("For loops must have simple final values"); return; }
cword(b,i);
sprintf(rwb," while %s < %s",forvariable,b);
stack_line(rwb);
return;
}
case IF_CODE: trans=1; goto Translation;
case UNTIL_CODE: trans=4; goto Translation;
case WHILE_CODE: trans=2; goto Translation;
case BREAK_CODE:
{ brace_spc=brace_sp;
do { j=brace_stack[--brace_spc]; } while ((j<10000)&&(brace_spc>=0));
sprintf(rwb,"@jump _f%d",j-10000);
stack_line(rwb);
return;
}
case ELSE_CODE: return;
case FONT_CODE:
word(b,2);
On_("on") { stack_line("put 0 word 8 $fffd&(0-->8)"); return; }
On_("off") { stack_line("put 0 word 8 2|(0-->8)"); return; }
error("Font must be switched \"on\" or \"off\"."); return;
case GIVE_CODE:
{ i=expression(2);
if (exp_hwm==-1) { error("Expected some attributes"); return; }
if (i==-1)
{ error("The object must be simply a variable or a constant"); return; }
do
{ char *bb;
word(b,exp_hwm);
if (b[0]!=0)
{ if (b[0]=='~') { sprintf(rwb, "@clear_attr "); bb=b+1; }
else { bb=b; sprintf(rwb, "@set_attr "); }
cword(b,i);
sprintf(rwb+strlen(rwb), "%s ",b);
word(b,exp_hwm); exp_hwm++;
sprintf(rwb+strlen(rwb), "%s",bb);
stack_line(rwb);
}
} while (b[0]!=0);
return;
}
case INVERSION_CODE:
{ sprintf(rwb,"@print \"%d\"",VNUMBER);
stack_line(rwb);
return;
}
case MOVE_CODE:
{ i=expression(2);
if (exp_hwm==-1) { error("Expected 'to'"); return; }
word(b,exp_hwm++);
if (strcmp(b,"to")!=0)
{ error_named("Expected \"to\" but found",b); return; }
j=expression(exp_hwm);
cword(b,i);
sprintf(rwb, "@insert_obj %s",b);
cword(b,j);
sprintf(rwb+strlen(rwb), " %s",b);
rearrange_stack(i,j,0);
stack_line(rwb);
return;
}
case OBJECTLOOP_CODE:
{ forloop_flag=1;
word(b,2);
strcpy(forvariable,b);
word(b,3);
On_("from") { sprintf(rwb," %s = ",forvariable); pflag=0; }
else On_("in") { sprintf(rwb," %s = child(",forvariable); pflag=1; }
else { error("Objectloops can only be from or in something"); return; }
i=expression(4);
cword(b,i);
sprintf(rwb+strlen(rwb),"%s%s",b,(pflag==1)?")":"");
stack_line(rwb);
if (exp_hwm!=-1) { error("'{' expected after objectloop"); return; }
sprintf(rwb," while %s ~= 0",forvariable);
stack_line(rwb);
objlb[oldepth]=brace_sp; strcpy(objlv[oldepth++],forvariable);
if (oldepth==MAX_OLDEPTH) { oldepth--;
error("Maximum object loop nesting exceeded (increase MAX_OLDEPTH)"); }
return;
}
case PRINT_ADDR_CODE: doexp=2; dofrom=2; cond="print_addr"; goto DoExpression;
case PRINT_CHAR_CODE: doexp=2; dofrom=2; cond="print_char"; goto DoExpression;
case PRINT_PADDR_CODE: doexp=2; dofrom=2; cond="print_paddr"; goto DoExpression;
case PRINT_OBJ_CODE: doexp=2; dofrom=2; cond="print_obj"; goto DoExpression;
case PRINT_NUM_CODE: doexp=2; dofrom=2; cond="print_num"; goto DoExpression;
case PUT_CODE:
{ i=expression(2);
if (exp_hwm==-1) { error("Expected 'byte' or 'word'"); return; }
word(b,exp_hwm++);
On_("byte") cond="B";
else On_("word") cond="W";
else
{ error_named("Expected 'byte' or 'word' but found",b); return; }
j=expression(exp_hwm);
k=expression(exp_hwm);
cword(b,i);
sprintf(rwb, "@store%s %s",cond,b);
cword(b,j);
sprintf(rwb+strlen(rwb), " %s",b);
cword(b,k);
sprintf(rwb+strlen(rwb), " %s",b);
rearrange_stack(i,j,k);
stack_line(rwb);
return;
}
case REMOVE_CODE: doexp=2; dofrom=2; cond="remove_obj"; goto DoExpression;
case RETURN_CODE:
word(b,2); if (b[0]==0) { stack_line("@ret#true"); return; }
doexp=2; dofrom=2; cond="ret"; goto DoExpression;
case STRING_CODE:
i=expression(2);
if (exp_hwm==-1) { error("Expected a string value"); return; }
cword(b,i);
sprintf(rwb, "put $0042 word %s ",b);
word(b,exp_hwm);
sprintf(rwb+strlen(rwb), "%s",b);
stack_line(rwb);
return;
case WRITE_CODE:
{ i=expression(2);
if (exp_hwm==-1) { error("Expected some properties"); return; }
if (i==-1)
{ error("The object must be simply a variable or a constant"); return; }
do
{ if (exp_hwm!=-1)
{ j=expression(exp_hwm);
if (exp_hwm==-1) { error("Expected property value"); return; }
k=expression(exp_hwm);
cword(b,i);
sprintf(rwb, "@put_prop %s",b);
cword(b,j);
sprintf(rwb+strlen(rwb), " %s",b);
cword(b,k);
sprintf(rwb+strlen(rwb), " %s",b);
rearrange_stack(j,k,0);
stack_line(rwb);
}
} while (exp_hwm!=-1);
return;
}
default: error("Internal error - unknown compiler code");
}
return;
DoExpression:
i=expression(dofrom);
if (i>=1) word(b,i); else strcpy(b,"sp");
switch(doexp)
{ case 1: if ((popflag==1)&&(i==-1)) stack_line("@pop"); break;
case 2: sprintf(rwb,"@%s %s",cond, b); stack_line(rwb); break;
}
if (exp_hwm!=-1) error("Spurious terms after expression");
return;
Translation:
next_block_type=0;
if (trans==2)
{ sprintf(rwb,"@._w%d",no_blocks_made);
next_block_type=10000;
stack_line(rwb);
}
k=expression(2);
if (exp_hwm==-1) { error("No condition"); return; }
word(b,exp_hwm);
i=find_symbol(b);
if ((i<0)||(stypes[i]!=10)) { error_named("Bad condition",b); return; }
if (svals[i]>=6) { j=svals[i]-5; pflag=1; }
else { j=svals[i]; pflag=0; }
cnum=j;
switch(cnum)
{ case 1: cond="je"; break;
case 2: cond="jge"; break;
case 3: cond="jle"; break;
case 4: cond="test_attr"; break;
case 5: cond="compare_pobj"; break;
}
j=expression(exp_hwm+1);
if (exp_hwm>=0)
{ word(b,exp_hwm);
On_("or")
{ if (cnum!=1)
{ error("You can only use 'or' on the '==' condition.");
return; }
cond="vje";
}
}
cword(b,k);
rearrange_stack(j,k,0);
sprintf(rwb," @%s %s ",cond,b);
if (trans!=4)
{ dir=1; labnum=no_blocks_made; }
else
{ dir=0; labnum=brace_stack[brace_sp]; }
cword(b,j);
sprintf(rwb+strlen(rwb),"%s ",b);
if (exp_hwm!=-1)
{ do
{ word(b,exp_hwm++);
if (b[0]==0) break;
if (strcmp(b,"or")!=0)
{ error("'{' expected before block of code"); return; }
word(b,exp_hwm++);
if (b[0]==0)
{ error("Missing alternative value"); return; }
sprintf(rwb+strlen(rwb),"%s ",b);
} while (1==1);
}
sprintf(rwb+strlen(rwb),"?%s_%s%d",
(pflag==1)?"":"~",(dir==1)?"f":"s",labnum);
stack_line(rwb);
return;
}
/* -------------------------------------------------------------------------------- */
/* Line parser: decides whether to send line to compiler or assembler */
/* -------------------------------------------------------------------------------- */
void parse_line(void)
{ char b[BUFFER_LENGTH]; int i, j; opcode opco;
int32 offset, expect=0;
#ifdef USE_TEMPORARY_FILES
offset=(subtract_pointers(utf_zcode_p,zcode));
#else
offset=(subtract_pointers(zcode_p,zcode));
#endif
word(b,2);
if (strcmp(b,"=")==0) { compiler(b,ASSIGNMENT_CODE); return; }
word(b,1); if (b[0]==0) return;
make_upper_case(b);
if (b[0]=='.') { assemble_label(offset,b); return; }
if ((b[0]=='@')&&(b[1]=='.')) { assemble_label(offset,b+1); return; }
if (b[0]=='{') { compile_openbrace(); return; }
if (b[0]=='}') { compile_closebrace(b); return; }
if (b[0]=='#') expect=1;
if (b[0]=='@') expect=2;
if (expect==0) i=prim_find_symbol(b,6);
else i=prim_find_symbol(b+1,6);
if ((expect==1) && ((i==-1)||(stypes[i]!=14)))
{ error_named("Unknown # directive",b); return;
}
if ((expect==2) && ((i==-1)||( (stypes[i]!=16) && (stypes[i]!=17) )))
{ error_named("Unknown assembly opcode",b); return;
}
if (i==-1)
{ word(b,2);
if (strcmp(b,"(")==0) { compiler(b,FUNCTION_CODE); return; }
word(b,1);
error_named("Unknown command, directive or opcode: ",b); return;
}
j=stypes[i];
if (j==17)
{ opco=opcs(svals[i]); assemble_opcode(b,offset,opco); return; }
if (j==16)
{ if (expect==2)
{ opco=opcs((svals[i])%100); assemble_opcode(b,offset,opco); return; }
compiler(b,(svals[i])/100); return;
}
if (j==14) { assemble_directive(b,offset,svals[i]); return; }
compiler(b,svals[i]);
return;
}
/* -------------------------------------------------------------------------------- */
/* Construct story file up as far as code area */
/* (see documentation for description of what goes on here) */
/* -------------------------------------------------------------------------------- */
void percentage(char *name, int x, int total)
{ printf(" %-20s %2d.%d%%\n",name,x*100/total,(x*1000/total)%10);
}
void construct_storyfile(void)
{ char *p; int32 i, j, k; int32 excess;
int32 syns, objs, props, vars, parse, code, strs, dict, nparse,
actshere, preactshere;
int32 synsat, glpat, objat, propat, parsat;
int32 code_length, strings_length;
p=output_p;
for (i=0; i<=0x3f; i++) p[i]=0;
p[0]=3; p[1]=statusline_flag*2;
p[2]=(release_number/256); p[3]=(release_number%256);
p[16]=0; p[17]=0;
write_serialnumber(buffer);
for (i=0; i<6; i++) p[18+i]=buffer[i];
syns=0x40;
p[syns]=0x80; p[syns+1]=0; syns+=2;
p[24]=syns/256; p[25]=syns%256; synsat=syns;
for (i=0; i<3*32; i++)
{ p[syns++]=0; p[syns++]=0x20;
}
for (i=0; i<no_abbrevs; i++)
{ p[synsat+64+2*i]=(abbrev_values[i])/256;
p[synsat+65+2*i]=(abbrev_values[i])%256;
}
objs=syns;
p[10]=objs/256; p[11]=objs%256; glpat=objs;
p[objs]=0; p[objs+1]=0;
for (i=2; i<32; i++)
{ p[objs+2*i-2]=prop_defaults[i]/256;
p[objs+2*i-1]=prop_defaults[i]%256;
}
objs+=62; props=objs+9*no_objects; objat=objs; propat=props;
for (i=0; i<properties_size; i++)
p[props+i]=properties_table[i];
for (i=0; i<no_objects; i++)
{ p[objs]=objects[i].atts[0];
p[objs+1]=objects[i].atts[1];
p[objs+2]=objects[i].atts[2];
p[objs+3]=objects[i].atts[3];
p[objs+4]=objects[i].parent;
p[objs+5]=objects[i].next;
p[objs+6]=objects[i].child;
p[objs+7]=props/256;
p[objs+8]=props%256;
objs+=9;
props+=objects[i].propsize;
}
vars=props;
p[12]=(vars/256); p[13]=(vars%256);
for (i=vars; i<vars+globals_size; i++) p[i]=table_init[i-vars];
parse=vars+globals_size;
p[14]=(parse/256); p[15]=(parse%256); parsat=parse;
nparse=parse+no_verbs*2;
for (i=0; i<no_verbs; i++)
{ p[parse]=(nparse/256); p[parse+1]=(nparse%256);
parse+=2;
p[nparse]=vs[i].lines; nparse++;
for (j=0; j<vs[i].lines; j++)
{ for (k=0; k<8; k++) p[nparse+k]=vs[i].l[j].e[k];
nparse+=8;
}
}
actshere=nparse; nparse+=2*no_actions;
preactshere=nparse; nparse+=2*no_actions;
p[nparse]=0; p[nparse+1]=no_adjectives; nparse+=2;
dict=nparse+4*no_adjectives;
adjectives_offset=nparse;
for (i=0; i<no_adjectives; i++)
{ j=adjectives[no_adjectives-i-1];
p[nparse]=j/256; p[nparse+1]=j%256; p[nparse+2]=0;
p[nparse+3]=(256-no_adjectives+i); nparse+=4;
}
dictionary[5]=(dict_entries/256); dictionary[6]=(dict_entries%256);
p[8]=(dict/256); p[9]=(dict%256);
for (i=0; i+dictionary<dict_p; i++) p[dict+i]=dictionary[i];
code=dict+i; if ((code%2)==1) { p[code++]=0; }
p[4]=(code/256); p[5]=(code%256);
p[6]=((code+1)/256); p[7]=((code+1)%256);
Write_Code_At = code;
code_length=subtract_pointers(zcode_p,zcode);
strs=code+code_length; if ((strs%2) != 0) p[strs++]=0;
Write_Strings_At = strs;
strings_length=subtract_pointers(strings_p,strings);
Out_Size=strs+strings_length; excess=Out_Size-((int32) 0x20000L);
if (excess>0)
{ sprintf(buffer,
"Story file exceeds 128K by %d bytes",excess);
fatalerror(buffer);
}
code_offset = code;
dictionary_offset = dict;
variables_offset = vars;
strings_offset = strs;
actions_offset = actshere;
preactions_offset = preactshere;
j=(Out_Size/2);
p[26]=j/256; p[27]=j%256; p[28]=0; p[29]=0;
for (i=0; i<no_actions; i++)
{ j=(actions[i]+code)/2;
p[actshere+i*2]=j/256; p[actshere+i*2+1]=j%256;
if (preactions[i]==-1) j=0; else j=(preactions[i]+code)/2;
p[preactshere+i*2]=j/256; p[preactshere+i*2+1]=j%256;
}
for (i=0; i<240; i++)
{ j=gvalues[i];
switch(gflags[i])
{ case 1: j+=(code/2); break;
case 2: j+=vars; break;
}
p[vars+i*2] = j/256;
p[vars+i*2+1] = j%256;
}
fix_gconstants(p+vars);
if (statistics_mode==1)
{ int k_long, rate; char *k_str;
k_long=(Out_Size/1024);
if ((Out_Size-1024*k_long) >= 512) { k_long++; k_str=""; }
else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; }
rate=total_bytes_trans*1000/total_chars_trans;
if ((pass_number==2)||(bothpasses_mode==1))
{ printf("Input %d lines (%d statements, %d chars)",
total_source_line,internal_line,marker_in_file);
if (total_files_read > 1) { printf(" from %d files",
total_files_read); }
printf(
"\n%4d objects (maximum 255) %4d dictionary entries (maximum %d)\n\
%4d attributes (maximum 32) %4d properties (maximum 30)\n\
%4d adjectives (maximum 240) %4d verbs (maximum %d)\n\
%4d actions (maximum %3d) %4d abbreviations (maximum %d)\n\
%4d globals (maximum 240) %4d variable space (maximum %d)\n\
%4d symbols (maximum %4d) %4d routines (maximum %d)\n\
%4d characters of text (compressed to %d bytes, rate 0.%d)\n\
Output story file is %3d%sK long (maximum 128K)\n",
no_objects,dict_entries,MAX_DICT_ENTRIES,
no_attributes,no_properties-2,
no_adjectives,no_verbs,MAX_VERBS,
no_actions,MAX_ACTIONS,no_abbrevs,MAX_ABBREVS,
no_globals,globals_size,MAX_STATIC_DATA,
no_symbols,MAX_SYMBOLS,no_routines,MAX_ROUTINES,
total_chars_trans,total_bytes_trans,rate,k_long,k_str);
}
}
if (offsets_mode==1)
{ if ((pass_number==2)||(bothpasses_mode==1))
{ printf(
"\nOffsets in story file:\n\
%04x Synonyms %04x Defaults %04x Objects %04x Properties\n\
%04x Variables %04x Parse table %04x Actions %04x Preactions\n\
%04x Adjectives %04x Dictionary %04x Code %04x Strings\n\n",
synsat, glpat, objat, propat, vars, parsat, actshere, preactshere,
adjectives_offset, dict, code, strs);
}
}
if (percentages_mode==1)
{ if ((pass_number==2)||(bothpasses_mode==1))
{ printf("Approximate percentage breakdown of story file:\n");
percentage("Z-code",code_length,Out_Size);
percentage("Static strings",strings_length,Out_Size);
percentage("Dictionary",code-dict,Out_Size);
percentage("Objects",vars-glpat,Out_Size);
percentage("Globals",parsat-vars,Out_Size);
percentage("Parsing tables",dict-parsat,Out_Size);
percentage("Header and synonyms",glpat,Out_Size);
percentage("Total of save area",parsat,Out_Size);
percentage("Total of text",total_bytes_trans,Out_Size);
}
}
if (frequencies_mode==1)
{ if ((pass_number==2)||(bothpasses_mode==1))
{ printf("How frequently abbreviations were used, and rough measure\n");
printf("of how many bytes each saved:\n");
for (i=0; i<no_abbrevs; i++)
{ printf("%5d %5d %10s ",abbrev_freqs[i],
2*(abbrev_freqs[i]*abbrev_quality[i])/3,
abbreviations_at+i*MAX_ABBREV_LENGTH);
if ((i%3)==2) printf("\n");
}
if ((i%3)!=0) printf("\n");
if (no_abbrevs==0) printf("None were declared.\n");
}
}
if (((statistics_mode==1)||(economy_mode==1))&&(pass_number==2))
{ printf("Essential size %d bytes: %d remaining\n",Out_Size,128*1024-Out_Size);
}
}
/* -------------------------------------------------------------------------------- */
/* Initialisation and main */
/* -------------------------------------------------------------------------------- */
void initialise(void)
{
abbreviations_at = my_malloc(MAX_ABBREVS*MAX_ABBREV_LENGTH,"abbreviations");
zcode =my_malloc(MAX_ZCODE_SIZE,"zcode"); zcode_p=zcode;
dictionary =my_malloc(7*MAX_DICT_ENTRIES+7,"dictionary"); dict_p=dictionary;
output_p =my_malloc(MAX_INITIAL_DATA_SIZE,"output buffer");
strings =my_malloc(MAX_STATIC_STRINGS,"static strings"); strings_p=strings;
tokens =my_malloc(2*BUFFER_LENGTH,"tokens");
properties_table =my_malloc(MAX_PROP_TABLE_SIZE,"properties table");
symbols_p=NULL; symbols_top=symbols_p;
stack_create();
dictionary[0]=3; dictionary[1]='.'; dictionary[2]=','; dictionary[3]='"';
dictionary[4]=7; dict_p=dictionary+7; dict_entries=0;
globals_size=0x1e0;
no_globals=0;
init_symbol_banks();
no_symbols=0;
stockup_symbols();
make_s_grid(); make_lookup();
}
void switches(char *p, int cmode)
{ int i;
if (cmode==1)
{ if (p[0]!='-')
{ printf("Ignoring second word which should be a -list of switches.\n"); return; }
}
for (i=cmode; p[i]!=0; i++)
{ if (p[i]=='l') listing_mode=1;
else if (p[i]=='a') listing_mode=2;
else if (p[i]=='c') concise_mode=1;
else if (p[i]=='e') economy_mode=1;
else if (p[i]=='f') frequencies_mode=1;
else if (p[i]=='i') ignoreswitches_mode=1;
else if (p[i]=='s') statistics_mode=1;
else if (p[i]=='t') tracing_mode=1;
else if (p[i]=='o') offsets_mode=1;
else if (p[i]=='m') memout_mode=1;
else if (p[i]=='d') double_spaced=1;
else if (p[i]=='b') { statistics_mode=1; bothpasses_mode=1; }
else if (p[i]=='p') percentages_mode=1;
else if (p[i]=='x') hash_mode=1;
else if (p[i]=='w') nowarnings_mode=1;
else if (p[i]=='h')
{ printf(RELEASE_STRING); printf("\n");
#ifdef ALLOCATE_BIG_ARRAYS
printf("(allocating memory for arrays) ");
#endif
#ifdef PROMPT_INPUT
printf("(prompting input) ");
#endif
#ifdef USE_TEMPORARY_FILES
printf("(temporary files) ");
#endif
printf(
"\n\nThis program is a compiler to version-3 Infocom format story files.\n\
It is copyright (C) Graham Nelson, 1993.\n\n");
#ifndef PROMPT_INPUT
printf("Its syntax is \"inform [-list] <file1> [<file2>]\"\n\n\
<file1> is the name of the Inform source file; Inform translates this into\n\
\"");
printf(Source_Prefix); printf("<file1>"); printf(Source_Extension);
printf("\"\n\
(unless <file1> contains a '.' or '/', in which case it is left alone).\n\
<file2> may optionally be given as the name of the story file to make.\n\
If it isn't given, Inform writes to\n\
\"");
printf(Code_Prefix); printf("<file1>"); printf(Code_Extension);
printf("\"\n\
but if it is, then Inform takes <file2> as the full filename.\n\n");
#endif
printf("\
-list is an optional list of switch letters following the initial hyphen:\n\
a list assembly-level instructions compiled\n\
b give statistics after both passes\n\
c more concise error messages\n\
d contract double spaces after full stops in text\n\
e economy mode (slower): make use of declared abbreviations\n\
f frequencies mode: show how useful abbreviations are\n\
h print this information\n\
i ignore default switches set within the file\n\
l list all assembly lines\n\
m say how much memory has been allocated\n\
o print offset addresses\n\
p give percentage breakdown of story file\n\
s give statistics\n\
t trace Z-code assembly\n\
w disable warning messages\n\
x print # for every 100 lines compiled (in both passes)\n\n");
#ifndef PROMPT_INPUT
printf("For example: \"inform -dex curses ram:curses\".\n");
#endif
}
else { printf("Switch \"-%c\" unknown (try \"inform -h\" for help)\n",p[i]); break; }
}
}
void banner()
{
#ifdef MACHINE_STRING
printf(MACHINE_STRING); printf(" ");
#endif
printf("Inform 1.0 (v%d/",VNUMBER);
#ifdef ALLOCATE_BIG_ARRAYS
printf("a");
#endif
#ifdef PROMPT_INPUT
printf("p");
#endif
#ifdef USE_TEMPORARY_FILES
printf("t");
#endif
#ifdef TIME_UNAVAILABLE
printf("u");
#endif
printf(")\n");
}
void trace_line(int origin)
{ int i; char b[BUFFER_LENGTH];
word(b,1);
if ((ltrace_mode==1)||(b[0]=='@')||(b[0]=='.')||(b[0]=='[')||(b[0]==']'))
{ printf("%4d%s ",current_source_line(),(origin==0)?" ":"*");
i=1; do { word(b,i++); printf("%s ",b); } while (b[0]!=0);
printf("\n");
}
}
int main(int argc, char **argv)
{ char *story_name="source", *code_name="output"; int origin, t1, t2, i, flag=0;
#ifdef PROMPT_INPUT
char buffer1[100], buffer2[100], buffer3[100];
#endif
t1=time(0);
banner();
#ifdef PROMPT_INPUT
i=0;
printf("Source filename?\n> ");
while (gets(buffer1)==NULL); story_name=buffer1;
printf("Output filename (RETURN for the same)?\n> ");
while (gets(buffer2)==NULL); code_name=buffer2;
if (buffer2[0]!=0) flag=1;
do
{ printf("List of switches (RETURN to finish; \"h\" for help)?\n> ");
while (gets(buffer3)==NULL); switches(buffer3,0);
} while (buffer3[0]!=0);
#else
if (argc==1) { switches("-h",1); return(0); }
i=1; while ((*(argv[i]))=='-') switches(argv[i++],1);
if (argc==i) { printf("[No input file named.]\n"); return(0); }
story_name=argv[i++];
if (argc==i) { flag=0; } else { flag=1; code_name=argv[i]; }
if (argc>i+1)
{ printf("Ignoring rest of command line (only first %d words used)\n",i+1); }
#endif
allocate_the_arrays();
if (flag==0)
{ sprintf(Code_Name,"%s%s%s",Code_Prefix,story_name,Code_Extension); }
else
{ sprintf(Code_Name,"%s",code_name); }
initialise();
for (pass_number=1; pass_number<=2; pass_number++)
{ input_file = 0;
load_sourcefile(story_name);
begin_pass();
dictionary_startpass();
do
{ origin=get_next_line();
if (origin==0) strcpy(forerrors_buff,buffer);
tokenise_line();
if (ltrace_mode>=1) trace_line(origin);
parse_line();
} while (endofpass_flag==0);
if (hash_mode==1) printf("\n");
#ifdef USE_TEMPORARY_FILES
zcode_p=utf_zcode_p;
#endif
construct_storyfile();
}
if (no_errors==0) output_file();
t2=time(0)-t1;
if (memout_mode==1)
{ printf("Static strings table used %d\n", subtract_pointers(strings_p,strings));
printf("Output buffer used %d\n", Write_Code_At);
printf("Code area table used %d\n", subtract_pointers(zcode_p,zcode));
printf("Properties table used %d\n", properties_size);
#ifdef USE_TEMPORARY_FILES
printf("(NB: strings and code area can safely be larger than allocation)\n");
#endif
printf("Allocated a total of %d bytes of memory\n",malloced_bytes); }
if ((no_errors+no_warnings)!=0)
printf("Compiled with %d error%s and %d warning%s%s\n",
no_errors,(no_errors==1)?"":"s",
no_warnings,(no_warnings==1)?"":"s",
(no_errors>0)?" (no output)":"");
if (statistics_mode==1)
printf("Completed in %d seconds.\n",t2);
if (no_errors!=0) return(1);
#ifdef ARC_PROFILING
_fmapstore("ram:profile");
#endif
return(0);
}
/* -------------------------------------------------------------------------------- */
/* End of code */
/* -------------------------------------------------------------------------------- */