From 8e63120c630c94c598d4e2d6ba823dac59bce8fa Mon Sep 17 00:00:00 2001 From: Jason Self Date: Sun, 29 May 2022 12:13:00 -0700 Subject: [PATCH] Update to commit a469d404a7dc4e87e18f367eb4d8e05fc32d20a7 Dated May 28 2022. These changes are similiarly relicensed to GPL per Section 4(c)(ii) of the Artistic License 2.0. --- DebugFileFormat.txt | 401 ---------------- src/arrays.c | 287 ++++++----- src/asm.c | 838 +++++++++++++++++++++++---------- src/bpatch.c | 167 +++---- src/chars.c | 4 +- src/directs.c | 298 ++++++++---- src/errors.c | 79 ++-- src/expressc.c | 231 ++++++--- src/expressp.c | 451 ++++++++++-------- src/files.c | 341 ++++---------- src/header.h | 562 ++++++++++++---------- src/inform.c | 349 ++++++-------- src/lexer.c | 527 ++++++++++++++------- src/linker.c | 255 +++++----- src/memory.c | 920 +++++++++++++----------------------- src/objects.c | 893 ++++++++++++++++++++--------------- src/states.c | 374 +++++++++------ src/symbols.c | 528 +++++++++++++-------- src/syntax.c | 111 +++-- src/tables.c | 789 +++++++++++++++---------------- src/text.c | 1097 +++++++++++++++++++++++++++++-------------- src/veneer.c | 88 ++-- src/verbs.c | 545 +++++++++++++++------ 23 files changed, 5623 insertions(+), 4512 deletions(-) delete mode 100644 DebugFileFormat.txt diff --git a/DebugFileFormat.txt b/DebugFileFormat.txt deleted file mode 100644 index fceef6c..0000000 --- a/DebugFileFormat.txt +++ /dev/null @@ -1,401 +0,0 @@ -Format of Inform 6 Debugging Information Files - -Version 1.0 - -0: Introduction - -This is a specification of the Version 1 format for the debugging information -files emitted by the Inform 6 compiler. It replaces Version 0, which is -documented in Section 12.5 of the Inform Technical Manual. - -1: Overview - -Debugging information files are written in XML and encoded in UTF-8. They -therefore begin with the following declaration: - - - -Beyond the usual requirements for well-formed XML, the file adheres to the -conventions that all numbers are written in decimal, all strings are -case-sensitive, and all excerpts from binary files are Base64-encoded. - -2: The Top Level - -The root element is given by the tag with three attributes, -the version of the debug file format being used, the name of the program that -produced the file, and that program's version. For instance, - - - ... - - -The elements from Sections 3--8 may appear in the ellipses. - -3: Story File Prefix - -The story file prefix contains a Base64 encoding of the story file's first bytes -so that a debugging tool can easily check whether the story and the debug -information file are mismatched. For example, the prefix for a Glulx story -might appear as - - - R2x1bAADAQEACqEAAAwsAAAMLAAAAQAAAAAAPAAIo2Jc - 6B2XSW5mbwABAAA2LjMyMC4zOAABMTIxMDE1wQAAMA== - - -The story file prefix is mandatory, but its length is unspecified. Version 6.33 -of the Inform compiler records 64 bytes, which seems sufficient. - -4: Story File Sections - -Story file sections partition the story file according to how the data will be -used. For the Inform 6 compiler, this partitioning is the same as the one that -the `z' flag prints. - -A record for a story file section gives a name for that section, its beginning -address (inclusive), and its end address (exclusive): - - - abbreviations table -
64
- 128 -
- -The names currently in use include those from Section 12.5 of the Inform -Technical Manual: - - abbreviations table - header extension (Z-code only) - alphabets table (Z-code only) - Unicode table (Z-code only) - property defaults - object tree - common properties - class numbers - individual properties (Z-code only) - global variables - array space - grammar table - actions table - parsing routines (Z-code only) - adjectives table (Z-code only) - dictionary - code area - strings area - -plus one addition for Z-code: - - abbreviations - -two additions for Glulx: - - memory layout id - string decoding table - -and three additions for both targets: - - header - identifier names - zero padding - -Names may repeat; Glulx story files, for example, sometimes have two zero -padding sections. - -A compiler that does not wish to subdivide the story file should emit one -section for the entirety and give it the name - - story - -5: Source Files - -Source files are encoded as in the example below. Each file has a unique index, -which is used by other elements when referring to source code locations; these -indices count from zero. The file's path is recorded in two forms, first as it -was given to the compiler via a command-line argument or include directive but -without any path abbreviations like `>' (the form suitable for presentation to a -human) and second after resolution to an absolute path (the form suitable for -loading the file contents). All paths are written according to the conventions -of the host OS. The language is, at present, either "Inform 6" or "Inform 7". -More languages may added in the future. - - - example.inf - /home/user/directory/example.inf - Inform 6 - - -If the source file is known to appear in the story's Blorb, its chunk number -will also be recorded: - - - example.inf - /home/user/directory/example.inf - Inform 6 - 18 - - -6: Table Entries; Grammar Lines - -Table entries are data defined by particular parts of the source code, but -without any corresponding identifiers. The element notes the -entry's type, the address where it begins (inclusive), the address where it ends -(exclusive), and the defining source code location(s), if any: - - - grammar line -
1004
- 1030 - ... -
- -Version 6.33 of the Inform compiler only emits tags for grammar -lines; these data are all located in the grammar table section. - -7: Named Values; Constants, Attributes, Properties, Actions, Fake Actions, - Objects, Classes, Arrays, and Routines - -Records for named values store their identifier, their value, and the source -code location(s) of their definition, if any. For instance, - - - MAX_SCORE - 40 - ... - - -would represent a named constant. Attributes, properties, actions, fake -actions, objects, arrays, and routines are also names for numbers, and differ -only in their use; they are represented in the same format under the tags -, , , , , , and -. (Moreover, unlike Version 0 of the debug information format, fake -actions are not recorded as both fake actions and actions.) - -The records for constants include some extra entries for the system constants -tabulated in Section 12.2 of the Inform Technical Manual, even though these are -not created by Constant directives. Entries for #undefed constants are also -included, but necessarily without values. - -Some records for objects will represent class objects. In that case, they will -be given with the tag rather than and include an additional -child to indicate their class number: - - - lamp - 5 - 1560 - ... - - -Records for arrays also have extra children, which record their size, their -element size, and the intended semantics for their zeroth element: - - - route - 1500 - 20 - 4 - true - ... - - -And finally, records contain an
and a element, -along with any number of the and elements, -which are described in Sections 9 and 10. The address is provided because the -identifier's value may be packed. - -Sometimes what would otherwise be a named value is in fact anonymous; unnamed -objects, embedded routines, some replaced routines, veneer properties, and the -Infix attribute are all examples. In such a case, the subelement -will carry the XML attribute - - artificial - -to indicate that the compiler is providing a sensible name of its own, which -could be presented to a human, but is not actually an identifier. For instance: - - - lantern.time_left - 1820 - 80 - ... - ... - - -Artificial identifiers may contain characters, like the full stop in -``lantern.time_left'', that would not be legal in the source language. - -8: Global Variables - -Globals are similar to named values, except that they are not interpreted as a -fixed value, but rather have an address where their value can be found. Their -records therefore contain an
tag in place of the tag, as in: - - - darkness_witnessed -
1520
- ... -
- -9: Local Variables - -The format for local variables mimics the format for global variables, except -that a source code location is never included, and their memory locations are -not given by address. For Z-code, locals are specified by index: - - - parameter - 1 - - -whereas for Glulx they are specified by frame offset: - - - parameter - 4 - - -If a local variable identifier is only in scope for part of a routine, it's -scope will be encoded as a beginning instruction address (inclusive) and an -ending instruction address (exclusive): - - - rulebook - 0 - 1628 - 1678 - - -Identifiers with noncontiguous scopes are recorded as one -element per contiguous region. It is possible for the same identifier to map to -different variables, so long as the corresponding scopes are disjoint. - -10: Sequence Points - -Sequence points are stored as an instruction address and the corresponding -single location in the source code: - - -
1628
- ... -
- -The source code location will always be exactly one position with overlapping -endpoints. - -Sequence points are defined as in Section 12.4 of the Inform Technical Manual, -but with the further stipulation that labels do not influence their source code -locations, as they did in Version 0 of the debug information format. For -instance, in code like - - say__p = 1; ParaContent(); .L_Say59; .LSayX59; - t_0 = 0; - -the sequence points are to be placed like this: - - <*> say__p = 1; <*> ParaContent(); .L_Say59; .LSayX59; - <*> t_0 = 0; - -rather than like this: - - <*> say__p = 1; <*> ParaContent(); <*> .L_Say59; .LSayX59; - t_0 = 0; - -11: Source Code Locations - -Most source code locations take the following format, which describes their -file, the line and character number where they begin (inclusive), the line and -character number where they end (exclusive), and the file positions (in bytes) -corresponding to those endpoints: - - - 0 - 1024 - 4 - 44153 - 1025 - 1 - 44186 - - -Line numbers and character numbers begin at one, but file positions count from -zero. - -In the special case where the endpoints coincide, as happens with sequence -points, the end elements may be elided: - - - 0 - 1024 - 4 - 44153 - - -At the other extreme, sometimes definitions span several source files or appear -in two different languages. The former case is dealt with by including multiple -code location elements and indexing them to indicate order: - - - - - 0 - 1024 - 4 - 44153 - 1025 - 1 - 44186 - - - - - 1 - 1 - 0 - 0 - 3 - 1 - 59 - - -The latter case is also handled with multiple elements. Note that indexing is -only used to indicated order among locations in the same language. - - - - - 2 - 12 - 0 - 308 - 12 - 112 - 420 - - - - - 0 - 1024 - 4 - 44153 - 1025 - 1 - 44186 - - --- -This file is part of Inform. - -Inform is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation, either version 3 of the License, or (at your -option) any later version. - -Inform is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with Inform. If not, see https://gnu.org/licenses/ \ No newline at end of file diff --git a/src/arrays.c b/src/arrays.c index 9c001cc..320287b 100644 --- a/src/arrays.c +++ b/src/arrays.c @@ -3,8 +3,8 @@ /* likewise global variables, which are in some ways a */ /* simpler form of the same thing. */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -26,29 +26,30 @@ /* ------------------------------------------------------------------------- */ /* Arrays defined below: */ /* */ -/* int dynamic_array_area[] Initial values for the bytes of */ +/* uchar dynamic_array_area[] Initial values for the bytes of */ /* the dynamic array area */ -/* int static_array_area[] Initial values for the bytes of */ +/* uchar static_array_area[] Initial values for the bytes of */ /* the static array area */ /* int32 global_initial_value[n] The initialised value of the nth */ -/* global variable (counting 0 - 239) */ +/* global variable (counting 0 - 239, */ +/* or higher for Glulx) */ /* */ /* The "dynamic array area" is the Z-machine area holding the current */ /* values of the global variables (in 240x2 = 480 bytes) followed by any */ -/* (dynamic) arrays which may be defined. Owing to a poor choice of name */ -/* some years ago, this is also called the "static data area", which is */ -/* why the memory setting for its maximum extent is "MAX_STATIC_DATA". */ +/* (dynamic) arrays which may be defined. */ /* */ -/* In Glulx, that 240 is changed to MAX_GLOBAL_VAR_NUMBER, and we take */ -/* correspondingly more space for the globals. This *really* ought to be */ -/* split into two segments. */ +/* In Glulx, we don't keep the global variables in dynamic_array_area. */ +/* Array data starts at the start. */ /* */ /* We can also store arrays (but not globals) into static memory (ROM). */ -/* The storage for these goes, unsurprisingly, into static_array_area -- */ -/* a separate allocation of MAX_STATIC_DATA bytes. */ +/* The storage for these goes, unsurprisingly, into static_array_area. */ /* ------------------------------------------------------------------------- */ -int *dynamic_array_area; /* See above */ -int32 *global_initial_value; +uchar *dynamic_array_area; /* See above */ +memory_list dynamic_array_area_memlist; +int dynamic_array_area_size; /* Size in bytes */ + +int32 *global_initial_value; /* Allocated to no_globals */ +static memory_list global_initial_value_memlist; int no_globals; /* Number of global variables used by the programmer (Inform itself @@ -57,17 +58,13 @@ int no_globals; /* Number of global variables used /* In Glulx, Inform uses the bottom ten. */ -int dynamic_array_area_size; /* Size in bytes */ - -int *static_array_area; +uchar *static_array_area; +memory_list static_array_area_memlist; int static_array_area_size; int no_arrays; -int32 *array_symbols; -int *array_sizes, *array_types, *array_locs; -/* array_sizes[N] gives the length of array N; array_types[N] is one of - the constants BYTE_ARRAY, WORD_ARRAY, etc; array_locs[N] is true for - static arrays, false for dynamic arrays. */ +arrayinfo *arrays; +static memory_list arrays_memlist; static int array_entry_size, /* 1 for byte array, 2 for word array */ array_base; /* Offset in dynamic array area of the @@ -81,16 +78,24 @@ static int array_entry_size, /* 1 for byte array, 2 for word array */ /* In Glulx, of course, that will be 4 instead of 2. */ +static memory_list current_array_name; /* The name of the global or array + currently being compiled. */ + +/* Complete the array. Fill in the size field (if it has one) and + advance foo_array_area_size. +*/ extern void finish_array(int32 i, int is_static) { - int *area; + uchar *area; int area_size; if (!is_static) { + ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size+array_base+1*array_entry_size); area = dynamic_array_area; area_size = dynamic_array_area_size; } else { + ensure_memory_list_available(&static_array_area_memlist, static_array_area_size+array_base+1*array_entry_size); area = static_array_area; area_size = static_array_area_size; } @@ -146,16 +151,22 @@ extern void finish_array(int32 i, int is_static) } +/* Fill in array entry i (in either the static or dynamic area). + When this is called, foo_array_area_size is the end of the previous + array; we're writing after that. +*/ extern void array_entry(int32 i, int is_static, assembly_operand VAL) { - int *area; + uchar *area; int area_size; if (!is_static) { + ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size+(i+1)*array_entry_size); area = dynamic_array_area; area_size = dynamic_array_area_size; } else { + ensure_memory_list_available(&static_array_area_memlist, static_array_area_size+(i+1)*array_entry_size); area = static_array_area; area_size = static_array_area_size; } @@ -163,9 +174,6 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL) if (!glulx_mode) { /* Array entry i (initial entry has i=0) is set to Z-machine value j */ - if (area_size+(i+1)*array_entry_size > MAX_STATIC_DATA) - memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); - if (array_entry_size==1) { area[area_size+i] = (VAL.value)%256; @@ -198,9 +206,6 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL) else { /* Array entry i (initial entry has i=0) is set to value j */ - if (area_size+(i+1)*array_entry_size > MAX_STATIC_DATA) - memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); - if (array_entry_size==1) { area[area_size+i] = (VAL.value) & 0xFF; @@ -223,22 +228,19 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL) if (VAL.marker != 0) { if (!is_static) { backpatch_zmachine(VAL.marker, DYNAMIC_ARRAY_ZA, - addr - 4*MAX_GLOBAL_VARIABLES); + addr); } else { /* We can't use backpatch_zmachine() because that only applies to RAM. Instead we add an entry to staticarray_backpatch_table. A backpatch entry is five bytes: *_MV followed by the array offset (in static array area). */ - write_byte_to_memory_block(&staticarray_backpatch_table, - staticarray_backpatch_size++, - VAL.marker); - write_byte_to_memory_block(&staticarray_backpatch_table, - staticarray_backpatch_size++, ((addr >> 24) & 0xFF)); - write_byte_to_memory_block(&staticarray_backpatch_table, - staticarray_backpatch_size++, ((addr >> 16) & 0xFF)); - write_byte_to_memory_block(&staticarray_backpatch_table, - staticarray_backpatch_size++, ((addr >> 8) & 0xFF)); - write_byte_to_memory_block(&staticarray_backpatch_table, - staticarray_backpatch_size++, (addr & 0xFF)); + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d staticarray %04x\n", VAL.marker, addr); + ensure_memory_list_available(&staticarray_backpatch_table_memlist, staticarray_backpatch_size+5); + staticarray_backpatch_table[staticarray_backpatch_size++] = VAL.marker; + staticarray_backpatch_table[staticarray_backpatch_size++] = ((addr >> 24) & 0xFF); + staticarray_backpatch_table[staticarray_backpatch_size++] = ((addr >> 16) & 0xFF); + staticarray_backpatch_table[staticarray_backpatch_size++] = ((addr >> 8) & 0xFF); + staticarray_backpatch_table[staticarray_backpatch_size++] = (addr & 0xFF); } } } @@ -271,7 +273,11 @@ extern void array_entry(int32 i, int is_static, assembly_operand VAL) /* ------------------------------------------------------------------------- */ extern void set_variable_value(int i, int32 v) -{ global_initial_value[i]=v; +{ + /* This can be called during module-load to create a new global, + so we call ensure. */ + ensure_memory_list_available(&global_initial_value_memlist, i+1); + global_initial_value[i]=v; } /* There are four ways to initialise arrays: */ @@ -289,6 +295,7 @@ extern void make_global(int array_flag, int name_only) array_flag is always FALSE in that case. */ int32 i; + int name_length; int array_type, data_type; int is_static = FALSE; assembly_operand AO; @@ -296,7 +303,6 @@ extern void make_global(int array_flag, int name_only) int extraspace; int32 global_symbol; - const char *global_name; debug_location_beginning beginning_debug_location = get_token_location_beginning(); @@ -304,15 +310,18 @@ extern void make_global(int array_flag, int name_only) get_next_token(); i = token_value; global_symbol = i; - global_name = token_text; + + name_length = strlen(token_text) + 1; + ensure_memory_list_available(¤t_array_name, name_length); + strncpy(current_array_name.data, token_text, name_length); if (!glulx_mode) { - if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T) - && (svals[i] >= LOWEST_SYSTEM_VAR_NUMBER)) + if ((token_type==SYMBOL_TT) && (symbols[i].type==GLOBAL_VARIABLE_T) + && (symbols[i].value >= LOWEST_SYSTEM_VAR_NUMBER)) goto RedefinitionOfSystemVar; } else { - if ((token_type==SYMBOL_TT) && (stypes[i]==GLOBAL_VARIABLE_T)) + if ((token_type==SYMBOL_TT) && (symbols[i].type==GLOBAL_VARIABLE_T)) goto RedefinitionOfSystemVar; } @@ -324,15 +333,15 @@ extern void make_global(int array_flag, int name_only) panic_mode_error_recovery(); return; } - if (!(sflags[i] & UNKNOWN_SFLAG)) + if (!(symbols[i].flags & UNKNOWN_SFLAG)) { discard_token_location(beginning_debug_location); if (array_flag) - ebf_symbol_error("new array name", token_text, typename(stypes[i]), slines[i]); - else ebf_symbol_error("new global variable name", token_text, typename(stypes[i]), slines[i]); + ebf_symbol_error("new array name", token_text, typename(symbols[i].type), symbols[i].line); + else ebf_symbol_error("new global variable name", token_text, typename(symbols[i].type), symbols[i].line); panic_mode_error_recovery(); return; } - if ((!array_flag) && (sflags[i] & USED_SFLAG)) + if ((!array_flag) && (symbols[i].flags & USED_SFLAG)) error_named("Variable must be defined before use:", token_text); directive_keywords.enabled = TRUE; @@ -352,18 +361,13 @@ extern void make_global(int array_flag, int name_only) if (array_flag) { if (!is_static) { - if (!glulx_mode) - assign_symbol(i, dynamic_array_area_size, ARRAY_T); - else - assign_symbol(i, - dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES, ARRAY_T); + assign_symbol(i, dynamic_array_area_size, ARRAY_T); } else { assign_symbol(i, static_array_area_size, STATIC_ARRAY_T); } - if (no_arrays == MAX_ARRAYS) - memoryerror("MAX_ARRAYS", MAX_ARRAYS); - array_symbols[no_arrays] = i; + ensure_memory_list_available(&arrays_memlist, no_arrays+1); + arrays[no_arrays].symbol = i; } else { if (!glulx_mode && no_globals==233) @@ -372,19 +376,19 @@ extern void make_global(int array_flag, int name_only) panic_mode_error_recovery(); return; } - if (glulx_mode && no_globals==MAX_GLOBAL_VARIABLES) - { discard_token_location(beginning_debug_location); - memoryerror("MAX_GLOBAL_VARIABLES", MAX_GLOBAL_VARIABLES); - panic_mode_error_recovery(); - return; - } - - variable_tokens[MAX_LOCAL_VARIABLES+no_globals] = i; + + ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES+no_globals+1); + variables[MAX_LOCAL_VARIABLES+no_globals].token = i; + variables[MAX_LOCAL_VARIABLES+no_globals].usage = FALSE; assign_symbol(i, MAX_LOCAL_VARIABLES+no_globals, GLOBAL_VARIABLE_T); - variable_tokens[svals[i]] = i; - if (name_only) import_symbol(i); - else global_initial_value[no_globals++]=0; + if (name_only) { + import_symbol(i); + } + else { + ensure_memory_list_available(&global_initial_value_memlist, no_globals+1); + global_initial_value[no_globals++]=0; + } } directive_keywords.enabled = TRUE; @@ -405,10 +409,12 @@ extern void make_global(int array_flag, int name_only) } put_token_back(); if (debugfile_switch && !array_flag) - { debug_file_printf(""); + { + char *global_name = current_array_name.data; + debug_file_printf(""); debug_file_printf("%s", global_name); debug_file_printf("
"); - write_debug_global_backpatch(svals[global_symbol]); + write_debug_global_backpatch(symbols[global_symbol].value); debug_file_printf("
"); write_debug_locations (get_token_location_end(beginning_debug_location)); @@ -434,10 +440,12 @@ extern void make_global(int array_flag, int name_only) } global_initial_value[no_globals-1] = AO.value; if (debugfile_switch) - { debug_file_printf(""); + { + char *global_name = current_array_name.data; + debug_file_printf(""); debug_file_printf("%s", global_name); debug_file_printf("
"); - write_debug_global_backpatch(svals[global_symbol]); + write_debug_global_backpatch(symbols[global_symbol].value); debug_file_printf("
"); write_debug_locations (get_token_location_end(beginning_debug_location)); @@ -456,7 +464,7 @@ extern void make_global(int array_flag, int name_only) else { backpatch_zmachine(ARRAY_MV, GLOBALVAR_ZA, 4*(no_globals-1)); global_initial_value[no_globals-1] - = dynamic_array_area_size - 4*MAX_GLOBAL_VARIABLES; + = dynamic_array_area_size; } } @@ -542,9 +550,12 @@ extern void make_global(int array_flag, int name_only) static_array_area_size += extraspace; } - array_types[no_arrays] = array_type; - array_locs[no_arrays] = is_static; + arrays[no_arrays].type = array_type; + arrays[no_arrays].loc = is_static; + /* Note that, from this point, we must continue through finish_array(). + Exiting this routine on error causes problems. */ + switch(data_type) { case NULLS_AI: @@ -582,7 +593,12 @@ extern void make_global(int array_flag, int name_only) i=0; do - { get_next_token(); + { + /* This isn't the start of a statement, but it's safe to + release token texts anyway. Expressions in an array + list are independent of each other. */ + release_token_texts(); + get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break; @@ -592,7 +608,6 @@ extern void make_global(int array_flag, int name_only) { discard_token_location(beginning_debug_location); error("Missing ';' to end the initial array values " "before \"[\" or \"]\""); - return; } put_token_back(); @@ -669,7 +684,12 @@ advance as part of 'Zcharacter table':", unicode); i = 0; while (TRUE) - { get_next_token(); + { + /* This isn't the start of a statement, but it's safe to + release token texts anyway. Expressions in an array + list are independent of each other. */ + release_token_texts(); + get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) continue; if ((token_type == SEP_TT) && (token_value == CLOSE_SQUARE_SEP)) @@ -693,10 +713,11 @@ advance as part of 'Zcharacter table':", unicode); if (debugfile_switch) { int32 new_area_size; + char *global_name = current_array_name.data; debug_file_printf(""); debug_file_printf("%s", global_name); debug_file_printf(""); - write_debug_array_backpatch(svals[global_symbol]); + write_debug_array_backpatch(symbols[global_symbol].value); debug_file_printf(""); new_area_size = (!is_static ? dynamic_array_area_size : static_array_area_size); debug_file_printf @@ -717,7 +738,7 @@ advance as part of 'Zcharacter table':", unicode); if ((array_type==BYTE_ARRAY) || (array_type==WORD_ARRAY)) i--; if (array_type==BUFFER_ARRAY) i+=WORDSIZE-1; - array_sizes[no_arrays++] = i; + arrays[no_arrays++].size = i; } extern int32 begin_table_array(void) @@ -733,10 +754,7 @@ extern int32 begin_table_array(void) dynamic_array_area_size += array_entry_size; - if (!glulx_mode) - return array_base; - else - return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES; + return array_base; } extern int32 begin_word_array(void) @@ -748,10 +766,7 @@ extern int32 begin_word_array(void) array_base = dynamic_array_area_size; array_entry_size = WORDSIZE; - if (!glulx_mode) - return array_base; - else - return array_base - WORDSIZE * MAX_GLOBAL_VARIABLES; + return array_base; } /* ========================================================================= */ @@ -761,41 +776,85 @@ extern int32 begin_word_array(void) extern void init_arrays_vars(void) { dynamic_array_area = NULL; static_array_area = NULL; + arrays = NULL; global_initial_value = NULL; - array_sizes = NULL; array_symbols = NULL; array_types = NULL; + variables = NULL; } extern void arrays_begin_pass(void) -{ no_arrays = 0; - if (!glulx_mode) - no_globals=0; - else - no_globals=11; - dynamic_array_area_size = WORDSIZE * MAX_GLOBAL_VARIABLES; +{ + int ix, totalvar; + + no_arrays = 0; + if (!glulx_mode) { + no_globals = 0; + /* The compiler-defined globals start at 239 and go down, so + we need to initialize the entire list from the start. */ + totalvar = MAX_ZCODE_GLOBAL_VARS; + } + else { + /* The compiler-defined globals run from 0 to 10. */ + no_globals = 11; + totalvar = no_globals; + } + + ensure_memory_list_available(&global_initial_value_memlist, totalvar); + for (ix=0; ix= MAX_LABELS) memoryerror("MAX_LABELS", MAX_LABELS); - - label_offsets[label] = offset; + ensure_memory_list_available(&labels_memlist, label+1); + + labels[label].offset = offset; + labels[label].symbol = -1; + if (offset < 0) { + /* Mark this label as invalid and don't put it in the linked list. */ + labels[label].prev = -1; + labels[label].next = -1; + return; + } + if (last_label == -1) - { label_prev[label] = -1; + { labels[label].prev = -1; first_label = label; } else - { label_prev[label] = last_label; - label_next[last_label] = label; + { labels[label].prev = last_label; + labels[last_label].next = label; } last_label = label; - label_next[label] = -1; - label_symbols[label] = -1; + labels[label].next = -1; +} + +/* Set a flag indicating that the given label has been jumped to. */ +static void mark_label_used(int label) +{ + if (label < 0) + return; + + /* Entries from 0 to labeluse_size have meaningful values. + If we have to increase labeluse_size, initialize the new + entries to FALSE. */ + ensure_memory_list_available(&labeluse_memlist, label+1); + for (; labeluse_size < label+1; labeluse_size++) { + labeluse[labeluse_size] = FALSE; + } + labeluse[label] = TRUE; } /* ------------------------------------------------------------------------- */ @@ -187,7 +318,7 @@ extern int is_variable_ot(int otval) extern char *variable_name(int32 i) { if (i==0) return("sp"); - if (imarker) { + printf((!any) ? " (" : ": "); + any = TRUE; + printf("%s", describe_mv(o->marker)); + switch (o->marker) { + case VROUTINE_MV: + printf(": %s", veneer_routine_name(o->value)); + break; + case INCON_MV: + printf(": %s", name_of_system_constant(o->value)); + break; + case DWORD_MV: + printf(": '"); + print_dict_word(o->value); + printf("'"); + break; + } + } + if (o->symindex >= 0 && o->symindex < no_symbols) { + printf((!any) ? " (" : ": "); + any = TRUE; + printf("%s", symbols[o->symindex].name); + } + if (any) printf(")"); +} + +static void print_operand_z(const assembly_operand *o, int annotate) +{ switch(o->type) { case EXPRESSION_OT: printf("expr_"); break; case LONG_CONSTANT_OT: printf("long_"); break; case SHORT_CONSTANT_OT: printf("short_"); break; case VARIABLE_OT: - if (o.value==0) { printf("sp"); return; } - printf("%s", variable_name(o.value)); return; + if (o->value==0) { printf("sp"); return; } + printf("%s", variable_name(o->value)); return; case OMITTED_OT: printf(""); return; } - printf("%d", o.value); + printf("%d", o->value); + if (annotate) + print_operand_annotation(o); } -static void print_operand_g(assembly_operand o) +static void print_operand_g(const assembly_operand *o, int annotate) { - switch (o.type) { + switch (o->type) { case EXPRESSION_OT: printf("expr_"); break; case CONSTANT_OT: printf("long_"); break; case HALFCONSTANT_OT: printf("short_"); break; @@ -244,32 +407,34 @@ static void print_operand_g(assembly_operand o) case ZEROCONSTANT_OT: printf("zero_"); return; case DEREFERENCE_OT: printf("*"); break; case GLOBALVAR_OT: - printf("%s (global_%d)", variable_name(o.value), o.value); + printf("global_%d (%s)", o->value, variable_name(o->value)); return; case LOCALVAR_OT: - if (o.value == 0) + if (o->value == 0) printf("stackptr"); else - printf("%s (local_%d)", variable_name(o.value), o.value-1); + printf("local_%d (%s)", o->value-1, variable_name(o->value)); return; case SYSFUN_OT: - if (o.value >= 0 && o.value < NUMBER_SYSTEM_FUNCTIONS) - printf("%s", system_functions.keywords[o.value]); + if (o->value >= 0 && o->value < NUMBER_SYSTEM_FUNCTIONS) + printf("%s", system_functions.keywords[o->value]); else printf(""); return; case OMITTED_OT: printf(""); return; default: printf("???_"); break; } - printf("%d", o.value); + printf("%d", o->value); + if (annotate) + print_operand_annotation(o); } -extern void print_operand(assembly_operand o) +extern void print_operand(const assembly_operand *o, int annotate) { if (!glulx_mode) - print_operand_z(o); + print_operand_z(o, annotate); else - print_operand_g(o); + print_operand_g(o, annotate); } /* ------------------------------------------------------------------------- */ @@ -277,8 +442,9 @@ extern void print_operand(assembly_operand o) /* ------------------------------------------------------------------------- */ static void byteout(int32 i, int mv) -{ if (zcode_ha_size >= MAX_ZCODE_SIZE) - memoryerror("MAX_ZCODE_SIZE",MAX_ZCODE_SIZE); +{ + ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+1); + ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+1); zcode_markers[zcode_ha_size] = (uchar) mv; zcode_holding_area[zcode_ha_size++] = (uchar) i; zmachine_pc++; @@ -286,7 +452,7 @@ static void byteout(int32 i, int mv) /* ------------------------------------------------------------------------- */ /* A database of the 115 canonical Infocom opcodes in Versions 3 to 6 */ -/* And of the however-many-there-are Glulx opcode */ +/* And of the however-many-there-are Glulx opcodes */ /* ------------------------------------------------------------------------- */ typedef struct opcodez @@ -337,6 +503,7 @@ typedef struct opcodeg #define GOP_MemHeap 2 /* uses_memheap_features */ #define GOP_Acceleration 4 /* uses_acceleration_features */ #define GOP_Float 8 /* uses_float_features */ +#define GOP_ExtUndo 16 /* uses_extundo_features */ /* Codes for the number of operands */ @@ -634,6 +801,8 @@ static opcodeg opcodes_table_g[] = { { (uchar *) "jfge", 0x1C5, Br, GOP_Float, 3 }, { (uchar *) "jisnan", 0x1C8, Br, GOP_Float, 2 }, { (uchar *) "jisinf", 0x1C9, Br, GOP_Float, 2 }, + { (uchar *) "hasundo", 0x128, St, GOP_ExtUndo, 1 }, + { (uchar *) "discardundo",0x129, 0, GOP_ExtUndo, 0 }, }; /* The opmacros table is used for fake opcodes. The opcode numbers are @@ -792,9 +961,10 @@ static void write_operand(assembly_operand op) } } -extern void assemblez_instruction(assembly_instruction *AI) +extern void assemblez_instruction(const assembly_instruction *AI) { - uchar *start_pc, *operands_pc; + int32 operands_pc; + int32 start_pc; int32 offset, j, topbits=0, types_byte1, types_byte2; int operand_rules, min=0, max=0, no_operands_given, at_seq_point = FALSE; assembly_operand o1, o2; @@ -802,6 +972,15 @@ extern void assemblez_instruction(assembly_instruction *AI) ASSERT_ZCODE(); + if (execution_never_reaches_here) { + if (!(execution_never_reaches_here & EXECSTATE_NOWARN)) { + warning("This statement can never be reached"); + /* only show the warning once */ + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + return; + } + offset = zmachine_pc; no_instructions++; @@ -810,8 +989,10 @@ extern void assemblez_instruction(assembly_instruction *AI) if (sequence_point_follows) { sequence_point_follows = FALSE; at_seq_point = TRUE; if (debugfile_switch) - { sequence_point_labels[next_sequence_point] = next_label; - sequence_point_locations[next_sequence_point] = + { + ensure_memory_list_available(&sequence_points_memlist, next_sequence_point+1); + sequence_points[next_sequence_point].label = next_label; + sequence_points[next_sequence_point].location = statement_debug_location; set_label_offset(next_label++, zmachine_pc); } @@ -825,11 +1006,8 @@ extern void assemblez_instruction(assembly_instruction *AI) return; } - if (execution_never_reaches_here) - warning("This statement can never be reached"); - operand_rules = opco.op_rules; - execution_never_reaches_here = ((opco.flags & Rf) != 0); + execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE); if (opco.flags2_set != 0) flags2_requirements[opco.flags2_set] = 1; @@ -840,7 +1018,7 @@ extern void assemblez_instruction(assembly_instruction *AI) /* 1. Write the opcode byte(s) */ - start_pc = zcode_holding_area + zcode_ha_size; + start_pc = zcode_ha_size; switch(opco.no) { case VAR_LONG: topbits=0xc0; min=0; max=8; break; @@ -855,23 +1033,31 @@ extern void assemblez_instruction(assembly_instruction *AI) } byteout(opco.code + topbits, 0); - operands_pc = zcode_holding_area + zcode_ha_size; + operands_pc = zcode_ha_size; /* 2. Dispose of the special rules LABEL and TEXT */ if (operand_rules==LABEL) { j = (AI->operand[0]).value; + mark_label_used(j); byteout(j/256, LABEL_MV); byteout(j%256, 0); goto Instruction_Done; } if (operand_rules==TEXT) { int32 i; - uchar *tmp = translate_text(zcode_holding_area + zcode_ha_size, zcode_holding_area+MAX_ZCODE_SIZE, AI->text, STRCTX_GAMEOPC); - if (!tmp) - memoryerror("MAX_ZCODE_SIZE", MAX_ZCODE_SIZE); - j = subtract_pointers(tmp, (zcode_holding_area + zcode_ha_size)); - for (i=0; itext, STRCTX_GAMEOPC); + if (j < 0) { + error("text translation failed"); + j = 0; + } + ensure_memory_list_available(&zcode_markers_memlist, zcode_ha_size+j); + ensure_memory_list_available(&zcode_holding_area_memlist, zcode_ha_size+j); + for (i=0; ioperand[0]; - *start_pc=(*start_pc) + o1.type*0x10; + zcode_holding_area[start_pc] += o1.type*0x10; write_operand(o1); break; @@ -919,12 +1105,12 @@ extern void assemblez_instruction(assembly_instruction *AI) /* Transfer to VAR form if either operand is a long constant */ if ((o1.type==LONG_CONSTANT_OT)||(o2.type==LONG_CONSTANT_OT)) - { *start_pc=(*start_pc) + 0xc0; + { zcode_holding_area[start_pc] += 0xc0; byteout(o1.type*0x40 + o2.type*0x10 + 0x0f, 0); } else - { if (o1.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x40; - if (o2.type==VARIABLE_OT) *start_pc=(*start_pc) + 0x20; + { if (o1.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x40; + if (o2.type==VARIABLE_OT) zcode_holding_area[start_pc] += 0x20; } write_operand(o1); write_operand(o2); @@ -934,12 +1120,12 @@ extern void assemblez_instruction(assembly_instruction *AI) /* 4. Assemble a Store destination, if needed */ if ((AI->store_variable_number) != -1) - { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES) { + { if (AI->store_variable_number >= MAX_LOCAL_VARIABLES+MAX_ZCODE_GLOBAL_VARS) { goto OpcodeSyntaxError; } o1.type = VARIABLE_OT; o1.value = AI->store_variable_number; - variable_usage[o1.value] = TRUE; + variables[o1.value].usage = TRUE; o1.marker = 0; /* Note that variable numbers 249 to 255 (i.e. globals 233 to 239) @@ -956,7 +1142,7 @@ extern void assemblez_instruction(assembly_instruction *AI) if (AI->branch_label_number != -1) { int32 addr, long_form; int branch_on_true = (AI->branch_flag)?1:0; - + mark_label_used(AI->branch_label_number); switch (AI->branch_label_number) { case -2: addr = 2; branch_on_true = 0; long_form = 0; break; /* branch nowhere, carry on */ @@ -994,7 +1180,7 @@ extern void assemblez_instruction(assembly_instruction *AI) for (i=0; ioperand_count; i++) { if ((i==0) && (opco.op_rules == VARIAB)) { if ((AI->operand[0]).type == VARIABLE_OT) - { printf("["); print_operand_z(AI->operand[i]); } + { printf("["); print_operand_z(&AI->operand[i], TRUE); } else printf("%s", variable_name((AI->operand[0]).value)); } @@ -1002,14 +1188,14 @@ extern void assemblez_instruction(assembly_instruction *AI) if ((i==0) && (opco.op_rules == LABEL)) { printf("L%d", AI->operand[0].value); } - else print_operand_z(AI->operand[i]); + else print_operand_z(&AI->operand[i], TRUE); printf(" "); } if (AI->store_variable_number != -1) { assembly_operand AO; printf("-> "); AO.type = VARIABLE_OT; AO.value = AI->store_variable_number; - print_operand_z(AO); printf(" "); + print_operand_z(&AO, TRUE); printf(" "); } switch(AI->branch_label_number) @@ -1025,10 +1211,10 @@ extern void assemblez_instruction(assembly_instruction *AI) } if (asm_trace_level>=2) - { for (j=0;start_pcinternal_number); - if (execution_never_reaches_here) - warning("This statement can never be reached"); - - execution_never_reaches_here = ((opco.flags & Rf) != 0); + execution_never_reaches_here = ((opco.flags & Rf) ? EXECSTATE_UNREACHABLE : EXECSTATE_REACHABLE); if (opco.op_rules & GOP_Unicode) { uses_unicode_features = TRUE; @@ -1143,12 +1338,15 @@ extern void assembleg_instruction(assembly_instruction *AI) if (opco.op_rules & GOP_Float) { uses_float_features = TRUE; } + if (opco.op_rules & GOP_ExtUndo) { + uses_extundo_features = TRUE; + } no_operands_given = AI->operand_count; /* 1. Write the opcode byte(s) */ - start_pc = zcode_holding_area + zcode_ha_size; + start_pc = zcode_ha_size; if (opco.code < 0x80) { byteout(opco.code, 0); @@ -1168,7 +1366,7 @@ extern void assembleg_instruction(assembly_instruction *AI) every two operands (rounded up). We write zeroes for now; when the operands are written, we'll go back and fix them. */ - opmodes_pc = zcode_holding_area + zcode_ha_size; + opmodes_pc = zcode_ha_size; for (ix=0; ix= BRANCH_MV && marker < BRANCHMAX_MV)) { @@ -1340,7 +1538,7 @@ extern void assembleg_instruction(assembly_instruction *AI) if (ix & 1) j = (j << 4); - opmodes_pc[ix/2] |= j; + zcode_holding_area[opmodes_pc+ix/2] |= j; } /* Print assembly trace. */ @@ -1359,23 +1557,23 @@ extern void assembleg_instruction(assembly_instruction *AI) printf("to L%d", AI->operand[i].value); } else { - print_operand_g(AI->operand[i]); + print_operand_g(&AI->operand[i], TRUE); } printf(" "); } if (asm_trace_level>=2) { for (j=0; - start_pc 0) printf("%5d +%05lx .L%d\n", ErrorReport.line_number, ((long int) zmachine_pc), n); set_label_offset(n, zmachine_pc); - execution_never_reaches_here = FALSE; + execution_never_reaches_here = EXECSTATE_REACHABLE; +} + +/* This is the same as assemble_label_no, except we only set up the label + if there has been a forward branch to it. + Returns whether the label is created. + Only use this for labels which never have backwards branches! +*/ +extern int assemble_forward_label_no(int n) +{ + if (n >= 0 && n < labeluse_size && labeluse[n]) { + assemble_label_no(n); + return TRUE; + } + else { + /* There were no forward branches to this label and we promise + there will be no backwards branches to it. Set a negative + offset, which will trip an error if we break our promise. */ + set_label_offset(n, -1); + return FALSE; + } } extern void define_symbol_label(int symbol) -{ label_symbols[svals[symbol]] = symbol; +{ + int label = symbols[symbol].value; + /* We may be creating a new label (label = next_label) or filling in + the value of an old one. So we call ensure. */ + ensure_memory_list_available(&labels_memlist, label+1); + labels[label].symbol = symbol; } extern int32 assemble_routine_header(int no_locals, @@ -1412,13 +1650,15 @@ extern int32 assemble_routine_header(int no_locals, int stackargs = FALSE; int name_length; - execution_never_reaches_here = FALSE; + execution_never_reaches_here = EXECSTATE_REACHABLE; routine_locals = no_locals; - for (i=0; i= 1 - && !strcmp(local_variables.keywords[0], "_vararg_count")) { + if (no_locals >= 1 + && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) { stackargs = TRUE; } @@ -1448,9 +1688,8 @@ extern int32 assemble_routine_header(int no_locals, routine_symbol = the_symbol; name_length = strlen(name) + 1; - routine_name = - my_malloc(name_length * sizeof(char), "temporary copy of routine name"); - strncpy(routine_name, name, name_length); + ensure_memory_list_available(¤t_routine_name, name_length); + strncpy(current_routine_name.data, name, name_length); /* Update the routine counter */ @@ -1476,6 +1715,7 @@ extern int32 assemble_routine_header(int no_locals, for (i=0; i"); if (embedded_flag) { debug_file_printf ("%s", routine_name); } - else if (sflags[routine_symbol] & REPLACE_SFLAG) + else if (symbols[routine_symbol].flags & REPLACE_SFLAG) { /* The symbol type will be set to ROUTINE_T once the replaced version has been given; if it is already set, we must be dealing with a replacement, and we can use the routine name as-is. Otherwise we look for a rename. And if that doesn't work, we fall back to an artificial identifier. */ - if (stypes[routine_symbol] == ROUTINE_T) + if (symbols[routine_symbol].type == ROUTINE_T) { /* Optional because there may be further replacements. */ write_debug_optional_identifier(routine_symbol); } else if (find_symbol_replacement(&routine_symbol)) { debug_file_printf - ("%s", symbs[routine_symbol]); + ("%s", symbols[routine_symbol].name); } else { debug_file_printf @@ -1737,67 +1982,61 @@ void assemble_routine_end(int embedded_flag, debug_locations locations) { debug_file_printf(""); debug_file_printf("
"); write_debug_code_backpatch - (label_offsets[sequence_point_labels[i]]); + (labels[sequence_points[i].label].offset); debug_file_printf("
"); - write_debug_location(sequence_point_locations[i]); + write_debug_location(sequence_points[i].location); debug_file_printf("
"); } debug_file_printf(""); } - my_free(&routine_name, "temporary copy of routine name"); - /* Issue warnings about any local variables not used in the routine. */ for (i=1; i<=routine_locals; i++) - if (!(variable_usage[i])) + if (!(variables[i].usage)) dbnu_warning("Local variable", variable_name(i), routine_starts_line); for (i=0; i= 4) - printf("To label %d, which is %d from here\n", - j, label_offsets[j]-pc); - if ((label_offsets[j] >= pc+2) && (label_offsets[j] < pc+64)) - { if (asm_trace_level >= 4) printf("Short form\n"); + printf("...To label %d, which is %d from here\n", + j, labels[j].offset-pc); + if ((labels[j].offset >= pc+2) && (labels[j].offset < pc+64)) + { if (asm_trace_level >= 4) printf("...Using short form\n"); + zcode_markers[i+1] = DELETED_MV; + } + } + else if (zcode_markers[i] == LABEL_MV) + { + if (asm_trace_level >= 4) + printf("Jump detected at offset %04x\n", pc); + j = (256*zcode_holding_area[i] + zcode_holding_area[i+1]) & 0x7fff; + if (asm_trace_level >= 4) + printf("...To label %d, which is %d from here\n", + j, labels[j].offset-pc); + if (labels[j].offset-pc == 2 && i >= 1 && zcode_holding_area[i-1] == opcodes_table_z[jump_zc].code+128) { + if (asm_trace_level >= 4) printf("...Deleting jump\n"); + zcode_markers[i-1] = DELETED_MV; + zcode_markers[i] = DELETED_MV; zcode_markers[i+1] = DELETED_MV; } } @@ -1841,17 +2095,18 @@ static void transfer_routine_z(void) { printf("Opening label: %d\n", first_label); for (i=0;i %d previous -> %d\n", - i, label_offsets[i], label_next[i], label_prev[i]); + i, labels[i].offset, labels[i].next, labels[i].prev); } + /* label will advance through the linked list as pc increases. */ for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label; i= 4) printf("Position of L%d corrected from %04x to %04x\n", - label, label_offsets[label], new_pc); - label_offsets[label] = new_pc; - label = label_next[label]; + label, labels[label].offset, new_pc); + labels[label].offset = new_pc; + label = labels[label].next; } if (zcode_markers[i] != DELETED_MV) new_pc++; } @@ -1861,6 +2116,8 @@ static void transfer_routine_z(void) operands with offsets to those labels. Also issue markers, now that we know where they occur in the final Z-code area. */ + ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size); + for (i=0, new_pc=adjusted_pc; i0x1fff) fatalerror("Branch out of range: divide the routine up?"); if (addr<0) addr+=(int32) 0x10000L; @@ -1887,18 +2150,24 @@ static void transfer_routine_z(void) } zcode_holding_area[i] = branch_on_true + 0x40 + (addr&0x3f); } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; break; case LABEL_MV: j = 256*zcode_holding_area[i] + zcode_holding_area[i+1]; - addr = label_offsets[j] - new_pc; + if (labels[j].offset < 0) { + error("Attempt to jump to an unreachable label"); + addr = 0; + } + else { + addr = labels[j].offset - new_pc; + } if (addr<-0x8000 || addr>0x7fff) fatalerror("Jump out of range: divide the routine up?"); if (addr<0) addr += (int32) 0x10000L; zcode_holding_area[i] = addr/256; zcode_holding_area[i+1] = addr%256; - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; break; case DELETED_MV: @@ -1920,20 +2189,26 @@ static void transfer_routine_z(void) break; } - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, - zcode_markers[i] + 32*(new_pc/65536)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, (new_pc/256)%256); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, new_pc%256); + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d PC %04x\n", zcode_markers[i], new_pc); + + ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+3); + zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i] + 32*(new_pc/65536); + zcode_backpatch_table[zcode_backpatch_size++] = (new_pc/256)%256; + zcode_backpatch_table[zcode_backpatch_size++] = new_pc%256; break; } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; break; } } + /* Consistency check */ + if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc) + { + fatalerror("Optimisation increased routine length or failed to match; should not happen"); + } + if (asm_trace_level >= 3) { printf("After branch optimisation, routine length is %d bytes\n", new_pc - rstart_pc); @@ -1942,13 +2217,12 @@ static void transfer_routine_z(void) /* Insert null bytes if necessary to ensure the next routine address is */ /* expressible as a packed address */ - { uchar zero[1]; - zero[0] = 0; - if (oddeven_packing_switch) - while ((adjusted_pc%(scale_factor*2))!=0) transfer_byte(zero); - else - while ((adjusted_pc%scale_factor)!=0) transfer_byte(zero); - } + ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+2*scale_factor); + + if (oddeven_packing_switch) + while ((adjusted_pc%(scale_factor*2))!=0) zcode_area[adjusted_pc++] = 0; + else + while ((adjusted_pc%scale_factor)!=0) zcode_area[adjusted_pc++] = 0; zmachine_pc = adjusted_pc; zcode_ha_size = 0; @@ -1957,7 +2231,7 @@ static void transfer_routine_z(void) static void transfer_routine_g(void) { int32 i, j, pc, new_pc, label, form_len, offset_of_next, addr, rstart_pc; - void (* transfer_byte)(uchar *); + int32 adjusted_pc; adjusted_pc = zmachine_pc - zcode_ha_size; rstart_pc = adjusted_pc; @@ -1966,12 +2240,12 @@ static void transfer_routine_g(void) (long int) adjusted_pc, zcode_ha_size, next_label); } - transfer_byte = - (temporary_files_switch)?transfer_to_temp_file:transfer_to_zcode_area; - /* (1) Scan through for branches and make short/long decisions in each case. Mark omitted bytes (bytes 2-4 in branches converted to - short form) with DELETED_MV. */ + short form) with DELETED_MV. + We also look for branches that can be entirely eliminated (because + they are jumping to the very next instruction). The opcode and + all label bytes get DELETED_MV. */ for (i=0, pc=adjusted_pc; i= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) { @@ -1984,16 +2258,25 @@ static void transfer_routine_g(void) | (zcode_holding_area[i+2] << 8) | (zcode_holding_area[i+3])); offset_of_next = pc + 4; - addr = (label_offsets[j] - offset_of_next) + 2; + addr = (labels[j].offset - offset_of_next) + 2; + opmodebyte = i - ((opmodeoffset+1)/2); if (asm_trace_level >= 4) - printf("To label %d, which is (%d-2) = %d from here\n", - j, addr, label_offsets[j] - offset_of_next); - if (addr >= -0x80 && addr < 0x80) { + printf("...To label %d, which is (%d-2) = %d from here\n", + j, addr, labels[j].offset - offset_of_next); + if (addr == 2 && i >= 2 && opmodeoffset == 2 && zcode_holding_area[opmodebyte-1] == opcodes_table_g[jump_gc].code) { + if (asm_trace_level >= 4) printf("...Deleting branch\n"); + zcode_markers[i-2] = DELETED_MV; + zcode_markers[i-1] = DELETED_MV; + zcode_markers[i] = DELETED_MV; + zcode_markers[i+1] = DELETED_MV; + zcode_markers[i+2] = DELETED_MV; + zcode_markers[i+3] = DELETED_MV; + } + else if (addr >= -0x80 && addr < 0x80) { if (asm_trace_level >= 4) printf("...Byte form\n"); zcode_markers[i+1] = DELETED_MV; zcode_markers[i+2] = DELETED_MV; zcode_markers[i+3] = DELETED_MV; - opmodebyte = i - ((opmodeoffset+1)/2); if ((opmodeoffset & 1) == 0) zcode_holding_area[opmodebyte] = (zcode_holding_area[opmodebyte] & 0xF0) | 0x01; @@ -2005,7 +2288,6 @@ static void transfer_routine_g(void) if (asm_trace_level >= 4) printf("...Short form\n"); zcode_markers[i+2] = DELETED_MV; zcode_markers[i+3] = DELETED_MV; - opmodebyte = i - ((opmodeoffset+1)/2); if ((opmodeoffset & 1) == 0) zcode_holding_area[opmodebyte] = (zcode_holding_area[opmodebyte] & 0xF0) | 0x02; @@ -2028,18 +2310,19 @@ static void transfer_routine_g(void) printf("Opening label: %d\n", first_label); for (i=0;i %d previous -> %d\n", - i, label_offsets[i], label_next[i], label_prev[i]); + i, labels[i].offset, labels[i].next, labels[i].prev); } + /* label will advance through the linked list as pc increases. */ for (i=0, pc=adjusted_pc, new_pc=adjusted_pc, label = first_label; i= 4) printf("Position of L%d corrected from %04x to %04x\n", - label, label_offsets[label], new_pc); - label_offsets[label] = new_pc; - label = label_next[label]; + label, labels[label].offset, new_pc); + labels[label].offset = new_pc; + label = labels[label].next; } if (zcode_markers[i] != DELETED_MV) new_pc++; } @@ -2049,6 +2332,8 @@ static void transfer_routine_g(void) operands with offsets to those labels. Also issue markers, now that we know where they occur in the final Z-code area. */ + ensure_memory_list_available(&zcode_area_memlist, adjusted_pc+zcode_ha_size); + for (i=0, new_pc=adjusted_pc; i= BRANCH_MV && zcode_markers[i] < BRANCHMAX_MV) { @@ -2070,7 +2355,13 @@ static void transfer_routine_g(void) after it. */ offset_of_next = new_pc + form_len; - addr = (label_offsets[j] - offset_of_next) + 2; + if (labels[j].offset < 0) { + error("Attempt to jump to an unreachable label"); + addr = 0; + } + else { + addr = (labels[j].offset - offset_of_next) + 2; + } if (asm_trace_level >= 4) { printf("Branch at offset %04x: %04x (%s)\n", new_pc, addr, ((form_len == 1) ? "byte" : @@ -2095,7 +2386,7 @@ static void transfer_routine_g(void) zcode_holding_area[i+2] = (addr >> 8) & 0xFF; zcode_holding_area[i+3] = (addr) & 0xFF; } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; } else if (zcode_markers[i] == LABEL_MV) { error("*** No LABEL opcodes in Glulx ***"); @@ -2124,26 +2415,27 @@ static void transfer_routine_g(void) Then a byte indicating the data size to be patched (1, 2, 4). Then the four-byte address (new_pc). */ - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, - zcode_markers[i]); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, - 4); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, ((new_pc >> 24) & 0xFF)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, ((new_pc >> 16) & 0xFF)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, ((new_pc >> 8) & 0xFF)); - write_byte_to_memory_block(&zcode_backpatch_table, - zcode_backpatch_size++, (new_pc & 0xFF)); + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d size %d PC %04x\n", zcode_markers[i], 4, new_pc); + ensure_memory_list_available(&zcode_backpatch_table_memlist, zcode_backpatch_size+6); + zcode_backpatch_table[zcode_backpatch_size++] = zcode_markers[i]; + zcode_backpatch_table[zcode_backpatch_size++] = 4; + zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 24) & 0xFF); + zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 16) & 0xFF); + zcode_backpatch_table[zcode_backpatch_size++] = ((new_pc >> 8) & 0xFF); + zcode_backpatch_table[zcode_backpatch_size++] = (new_pc & 0xFF); break; } - transfer_byte(zcode_holding_area + i); new_pc++; + zcode_area[adjusted_pc++] = zcode_holding_area[i]; new_pc++; } } + /* Consistency check */ + if (new_pc - rstart_pc > zcode_ha_size || adjusted_pc != new_pc) + { + fatalerror("Optimisation increased routine length or failed to match; should not happen"); + } + if (asm_trace_level >= 3) { printf("After branch optimisation, routine length is %d bytes\n", new_pc - rstart_pc); @@ -2213,7 +2505,22 @@ void assemblez_1_to(int internal_number, void assemblez_1_branch(int internal_number, assembly_operand o1, int label, int flag) -{ AI.internal_number = internal_number; +{ + /* Some clever optimizations first. A constant is always or never equal + to zero. */ + if (o1.marker == 0 && is_constant_ot(o1.type)) { + if (internal_number == jz_zc) { + if ((flag && o1.value == 0) || (!flag && o1.value != 0)) { + assemblez_jump(label); + return; + } + else { + /* assemble nothing at all! */ + return; + } + } + } + AI.internal_number = internal_number; AI.operand_count = 1; AI.operand[0] = o1; AI.branch_label_number = label; @@ -2563,9 +2870,6 @@ void assembleg_1_branch(int internal_number, if ((internal_number == jz_gc && o1.value == 0) || (internal_number == jnz_gc && o1.value != 0)) { assembleg_0_branch(jump_gc, label); - /* We clear the "can't reach statement" flag here, - so that "if (1)" doesn't produce that warning. */ - execution_never_reaches_here = 0; return; } if ((internal_number == jz_gc && o1.value != 0) @@ -2778,9 +3082,11 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)"); get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) { assemblez_instruction(&AI); + AI.text = NULL; return; } ebf_error("semicolon ';' after print string", token_text); + AI.text = NULL; put_token_back(); return; } @@ -2803,11 +3109,11 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)"); else { if (strcmp(token_text, "sp") == 0) n = 0; else - { if (stypes[token_value] != GLOBAL_VARIABLE_T) + { if (symbols[token_value].type != GLOBAL_VARIABLE_T) error_named( "Store '->' destination not 'sp' or a variable:", token_text); - else n = svals[token_value]; + else n = symbols[token_value].value; } } AI.store_variable_number = n; @@ -2961,6 +3267,7 @@ static void parse_assembly_g(void) int error_flag = FALSE, is_macro = FALSE; AI.operand_count = 0; + AI.text = NULL; opcode_names.enabled = TRUE; opcode_macros.enabled = TRUE; @@ -3114,15 +3421,6 @@ extern void parse_assembly(void) /* Data structure management routines */ /* ------------------------------------------------------------------------- */ -extern void asm_begin_pass(void) -{ no_instructions = 0; - zmachine_pc = 0; - no_sequence_points = 0; - next_label = 0; - next_sequence_point = 0; - zcode_ha_size = 0; -} - extern void init_asm_vars(void) { int i; @@ -3132,56 +3430,76 @@ extern void init_asm_vars(void) uses_memheap_features = FALSE; uses_acceleration_features = FALSE; uses_float_features = FALSE; + uses_extundo_features = FALSE; + labels = NULL; + sequence_points = NULL; sequence_point_follows = TRUE; label_moved_error_already_given = FALSE; - initialise_memory_block(&zcode_area); + zcode_area = NULL; } -extern void asm_allocate_arrays(void) -{ if ((debugfile_switch) && (MAX_LABELS < 2000)) MAX_LABELS = 2000; - - variable_tokens = my_calloc(sizeof(int32), - MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable tokens"); - variable_usage = my_calloc(sizeof(int), - MAX_LOCAL_VARIABLES+MAX_GLOBAL_VARIABLES, "variable usage"); - - label_offsets = my_calloc(sizeof(int32), MAX_LABELS, "label offsets"); - label_symbols = my_calloc(sizeof(int32), MAX_LABELS, "label symbols"); - label_next = my_calloc(sizeof(int), MAX_LABELS, "label dll 1"); - label_prev = my_calloc(sizeof(int), MAX_LABELS, "label dll 1"); - sequence_point_labels - = my_calloc(sizeof(int), MAX_LABELS, "sequence point labels"); - sequence_point_locations - = my_calloc(sizeof(debug_location), - MAX_LABELS, - "sequence point locations"); - - zcode_holding_area = my_malloc(MAX_ZCODE_SIZE,"compiled routine code area"); - zcode_markers = my_malloc(MAX_ZCODE_SIZE, "compiled routine code area"); +extern void asm_begin_pass(void) +{ no_instructions = 0; + zmachine_pc = 0; + no_sequence_points = 0; + next_label = 0; + labeluse_size = 0; + next_sequence_point = 0; + zcode_ha_size = 0; + execution_never_reaches_here = EXECSTATE_REACHABLE; +} - named_routine_symbols - = my_calloc(sizeof(int32), MAX_SYMBOLS, "named routine symbols"); +extern void asm_allocate_arrays(void) +{ + initialise_memory_list(&variables_memlist, + sizeof(variableinfo), 200, (void**)&variables, + "variables"); + + initialise_memory_list(&labels_memlist, + sizeof(labelinfo), 1000, (void**)&labels, + "labels"); + initialise_memory_list(&labeluse_memlist, + sizeof(int), 1000, (void**)&labeluse, + "labeluse"); + initialise_memory_list(&sequence_points_memlist, + sizeof(sequencepointinfo), 1000, (void**)&sequence_points, + "sequence points"); + + initialise_memory_list(&zcode_holding_area_memlist, + sizeof(uchar), 2000, (void**)&zcode_holding_area, + "compiled routine code area"); + initialise_memory_list(&zcode_markers_memlist, + sizeof(uchar), 2000, (void**)&zcode_markers, + "compiled routine markers area"); + + initialise_memory_list(&named_routine_symbols_memlist, + sizeof(int32), 1000, (void**)&named_routine_symbols, + "named routine symbols"); + + initialise_memory_list(&zcode_area_memlist, + sizeof(uchar), 8192, (void**)&zcode_area, + "code area"); + + initialise_memory_list(¤t_routine_name, + sizeof(char), 3*MAX_IDENTIFIER_LENGTH, NULL, + "routine name currently being defined"); } extern void asm_free_arrays(void) { - my_free(&variable_tokens, "variable tokens"); - my_free(&variable_usage, "variable usage"); + deallocate_memory_list(&variables_memlist); - my_free(&label_offsets, "label offsets"); - my_free(&label_symbols, "label symbols"); - my_free(&label_next, "label dll 1"); - my_free(&label_prev, "label dll 2"); - my_free(&sequence_point_labels, "sequence point labels"); - my_free(&sequence_point_locations, "sequence point locations"); + deallocate_memory_list(&labels_memlist); + deallocate_memory_list(&sequence_points_memlist); - my_free(&zcode_holding_area, "compiled routine code area"); - my_free(&zcode_markers, "compiled routine code markers"); + deallocate_memory_list(&zcode_holding_area_memlist); + deallocate_memory_list(&zcode_markers_memlist); - my_free(&named_routine_symbols, "named routine symbols"); - deallocate_memory_block(&zcode_area); + deallocate_memory_list(&named_routine_symbols_memlist); + deallocate_memory_list(&zcode_area_memlist); + deallocate_memory_list(¤t_routine_name); } /* ========================================================================= */ diff --git a/src/bpatch.c b/src/bpatch.c index daca6fb..1fd984b 100644 --- a/src/bpatch.c +++ b/src/bpatch.c @@ -2,8 +2,8 @@ /* "bpatch" : Keeps track of, and finally acts on, backpatch markers, */ /* correcting symbol values not known at compilation time */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -22,8 +22,12 @@ #include "header.h" -memory_block zcode_backpatch_table, staticarray_backpatch_table, - zmachine_backpatch_table; +uchar *staticarray_backpatch_table; /* Allocated to staticarray_backpatch_size */ +memory_list staticarray_backpatch_table_memlist; +uchar *zmachine_backpatch_table; /* Allocated to zmachine_backpatch_size */ +memory_list zmachine_backpatch_table_memlist; +uchar *zcode_backpatch_table; /* Allocated to zcode_backpatch_size */ +memory_list zcode_backpatch_table_memlist; int32 zcode_backpatch_size, staticarray_backpatch_size, zmachine_backpatch_size; @@ -38,7 +42,7 @@ static int32 backpatch_value_z(int32 value) ASSERT_ZCODE(); - if (asm_trace_level >= 4) + if (bpatch_trace_setting) printf("BP %s applied to %04x giving ", describe_mv(backpatch_marker), value); @@ -86,7 +90,7 @@ static int32 backpatch_value_z(int32 value) value = value_of_system_constant(value); break; case DWORD_MV: value = dictionary_offset + 7 + - final_dict_order[value]*((version_number==3)?7:9); + final_dict_order[value]*(DICT_ENTRY_BYTE_LENGTH); break; case ACTION_MV: break; @@ -105,10 +109,10 @@ static int32 backpatch_value_z(int32 value) break; case MAIN_MV: value = symbol_index("Main", -1); - if (stypes[value] != ROUTINE_T) + if (symbols[value].type != ROUTINE_T) error("No 'Main' routine has been defined"); - sflags[value] |= USED_SFLAG; - value = svals[value]; + symbols[value].flags |= USED_SFLAG; + value = symbols[value].value; if (OMIT_UNUSED_ROUTINES) value = df_stripped_address_for_address(value); value += code_offset/scale_factor; @@ -123,34 +127,34 @@ static int32 backpatch_value_z(int32 value) value = 0; break; } - if (sflags[value] & UNKNOWN_SFLAG) - { if (!(sflags[value] & UERROR_SFLAG)) - { sflags[value] |= UERROR_SFLAG; + if (symbols[value].flags & UNKNOWN_SFLAG) + { if (!(symbols[value].flags & UERROR_SFLAG)) + { symbols[value].flags |= UERROR_SFLAG; error_named_at("No such constant as", - (char *) symbs[value], slines[value]); + symbols[value].name, symbols[value].line); } } else - if (sflags[value] & CHANGE_SFLAG) - { sflags[value] &= (~(CHANGE_SFLAG)); - backpatch_marker = (svals[value]/0x10000); + if (symbols[value].flags & CHANGE_SFLAG) + { symbols[value].flags &= (~(CHANGE_SFLAG)); + backpatch_marker = (symbols[value].marker); if ((backpatch_marker < 0) || (backpatch_marker > LARGEST_BPATCH_MV)) { if (no_link_errors == 0) { compiler_error_named( "Illegal backpatch marker attached to symbol", - (char *) symbs[value]); + symbols[value].name); backpatch_error_flag = TRUE; } } else - svals[value] = backpatch_value_z((svals[value]) % 0x10000); + symbols[value].value = backpatch_value_z((symbols[value].value) % 0x10000); } - sflags[value] |= USED_SFLAG; - { int t = stypes[value]; - value = svals[value]; + symbols[value].flags |= USED_SFLAG; + { int t = symbols[value].type; + value = symbols[value].value; switch(t) { case ROUTINE_T: if (OMIT_UNUSED_ROUTINES) @@ -172,7 +176,7 @@ static int32 backpatch_value_z(int32 value) break; } - if (asm_trace_level >= 4) printf(" %04x\n", value); + if (bpatch_trace_setting) printf(" %04x\n", value); return(value); } @@ -183,7 +187,7 @@ static int32 backpatch_value_g(int32 value) ASSERT_GLULX(); - if (asm_trace_level >= 4) + if (bpatch_trace_setting) printf("BP %s applied to %04x giving ", describe_mv(backpatch_marker), value); @@ -255,10 +259,10 @@ static int32 backpatch_value_g(int32 value) break; case MAIN_MV: value = symbol_index("Main", -1); - if (stypes[value] != ROUTINE_T) + if (symbols[value].type != ROUTINE_T) error("No 'Main' routine has been defined"); - sflags[value] |= USED_SFLAG; - value = svals[value]; + symbols[value].flags |= USED_SFLAG; + value = symbols[value].value; if (OMIT_UNUSED_ROUTINES) value = df_stripped_address_for_address(value); value += code_offset; @@ -273,34 +277,34 @@ static int32 backpatch_value_g(int32 value) value = 0; break; } - if (sflags[value] & UNKNOWN_SFLAG) - { if (!(sflags[value] & UERROR_SFLAG)) - { sflags[value] |= UERROR_SFLAG; + if (symbols[value].flags & UNKNOWN_SFLAG) + { if (!(symbols[value].flags & UERROR_SFLAG)) + { symbols[value].flags |= UERROR_SFLAG; error_named_at("No such constant as", - (char *) symbs[value], slines[value]); + symbols[value].name, symbols[value].line); } } else - if (sflags[value] & CHANGE_SFLAG) - { sflags[value] &= (~(CHANGE_SFLAG)); - backpatch_marker = smarks[value]; + if (symbols[value].flags & CHANGE_SFLAG) + { symbols[value].flags &= (~(CHANGE_SFLAG)); + backpatch_marker = symbols[value].marker; if ((backpatch_marker < 0) || (backpatch_marker > LARGEST_BPATCH_MV)) { if (no_link_errors == 0) { compiler_error_named( "Illegal backpatch marker attached to symbol", - (char *) symbs[value]); + symbols[value].name); backpatch_error_flag = TRUE; } } else - svals[value] = backpatch_value_g(svals[value]); + symbols[value].value = backpatch_value_g(symbols[value].value); } - sflags[value] |= USED_SFLAG; - { int t = stypes[value]; - value = svals[value]; + symbols[value].flags |= USED_SFLAG; + { int t = symbols[value].type; + value = symbols[value].value; switch(t) { case ROUTINE_T: @@ -340,7 +344,7 @@ symbol"); break; } - if (asm_trace_level >= 4) printf(" %04x\n", value); + if (bpatch_trace_setting) printf(" %04x\n", value); return(value); } @@ -363,16 +367,14 @@ static void backpatch_zmachine_z(int mv, int zmachine_area, int32 offset) if (mv == ACTION_MV) return; } - /* printf("MV %d ZA %d Off %04x\n", mv, zmachine_area, offset); */ + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d ZA %d Off %04x\n", mv, zmachine_area, offset); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, mv); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, zmachine_area); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, offset/256); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, offset%256); + ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+4); + zmachine_backpatch_table[zmachine_backpatch_size++] = mv; + zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area; + zmachine_backpatch_table[zmachine_backpatch_size++] = offset/256; + zmachine_backpatch_table[zmachine_backpatch_size++] = offset%256; } static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset) @@ -390,20 +392,16 @@ static void backpatch_zmachine_g(int mv, int zmachine_area, int32 offset) Then the four-byte address. */ -/* printf("+MV %d ZA %d Off %06x\n", mv, zmachine_area, offset); */ - - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, mv); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, zmachine_area); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, (offset >> 24) & 0xFF); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, (offset >> 16) & 0xFF); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, (offset >> 8) & 0xFF); - write_byte_to_memory_block(&zmachine_backpatch_table, - zmachine_backpatch_size++, (offset) & 0xFF); + if (bpatch_trace_setting >= 2) + printf("BP added: MV %d ZA %d Off %06x\n", mv, zmachine_area, offset); + + ensure_memory_list_available(&zmachine_backpatch_table_memlist, zmachine_backpatch_size+6); + zmachine_backpatch_table[zmachine_backpatch_size++] = mv; + zmachine_backpatch_table[zmachine_backpatch_size++] = zmachine_area; + zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 24) & 0xFF; + zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 16) & 0xFF; + zmachine_backpatch_table[zmachine_backpatch_size++] = (offset >> 8) & 0xFF; + zmachine_backpatch_table[zmachine_backpatch_size++] = (offset) & 0xFF; } extern void backpatch_zmachine(int mv, int zmachine_area, int32 offset) @@ -420,12 +418,12 @@ extern void backpatch_zmachine_image_z(void) backpatch_error_flag = FALSE; while (bm < zmachine_backpatch_size) { backpatch_marker - = read_byte_from_memory_block(&zmachine_backpatch_table, bm); + = zmachine_backpatch_table[bm]; zmachine_area - = read_byte_from_memory_block(&zmachine_backpatch_table, bm+1); + = zmachine_backpatch_table[bm+1]; offset - = 256*read_byte_from_memory_block(&zmachine_backpatch_table,bm+2) - + read_byte_from_memory_block(&zmachine_backpatch_table, bm+3); + = 256*zmachine_backpatch_table[bm+2] + + zmachine_backpatch_table[bm+3]; bm += 4; switch(zmachine_area) @@ -462,19 +460,17 @@ extern void backpatch_zmachine_image_g(void) backpatch_error_flag = FALSE; while (bm < zmachine_backpatch_size) { backpatch_marker - = read_byte_from_memory_block(&zmachine_backpatch_table, bm); + = zmachine_backpatch_table[bm]; zmachine_area - = read_byte_from_memory_block(&zmachine_backpatch_table, bm+1); - offset = read_byte_from_memory_block(&zmachine_backpatch_table, bm+2); + = zmachine_backpatch_table[bm+1]; + offset = zmachine_backpatch_table[bm+2]; offset = (offset << 8) | - read_byte_from_memory_block(&zmachine_backpatch_table, bm+3); + zmachine_backpatch_table[bm+3]; offset = (offset << 8) | - read_byte_from_memory_block(&zmachine_backpatch_table, bm+4); + zmachine_backpatch_table[bm+4]; offset = (offset << 8) | - read_byte_from_memory_block(&zmachine_backpatch_table, bm+5); - bm += 6; - - /* printf("-MV %d ZA %d Off %06x\n", backpatch_marker, zmachine_area, offset); */ + zmachine_backpatch_table[bm+5]; + bm += 6; switch(zmachine_area) { case PROP_DEFAULTS_ZA: addr = prop_defaults_offset+4; break; @@ -514,9 +510,9 @@ extern void backpatch_zmachine_image_g(void) /* ------------------------------------------------------------------------- */ extern void init_bpatch_vars(void) -{ initialise_memory_block(&zcode_backpatch_table); - initialise_memory_block(&staticarray_backpatch_table); - initialise_memory_block(&zmachine_backpatch_table); +{ zcode_backpatch_table = NULL; + staticarray_backpatch_table = NULL; + zmachine_backpatch_table = NULL; } extern void bpatch_begin_pass(void) @@ -527,12 +523,21 @@ extern void bpatch_begin_pass(void) extern void bpatch_allocate_arrays(void) { + initialise_memory_list(&zcode_backpatch_table_memlist, + sizeof(uchar), 128, (void**)&zcode_backpatch_table, + "code backpatch table"); + initialise_memory_list(&staticarray_backpatch_table_memlist, + sizeof(uchar), 128, (void**)&staticarray_backpatch_table, + "static array backpatch table"); + initialise_memory_list(&zmachine_backpatch_table_memlist, + sizeof(uchar), 128, (void**)&zmachine_backpatch_table, + "machine backpatch table"); } extern void bpatch_free_arrays(void) -{ deallocate_memory_block(&zcode_backpatch_table); - deallocate_memory_block(&staticarray_backpatch_table); - deallocate_memory_block(&zmachine_backpatch_table); +{ deallocate_memory_list(&zcode_backpatch_table_memlist); + deallocate_memory_list(&staticarray_backpatch_table_memlist); + deallocate_memory_list(&zmachine_backpatch_table_memlist); } /* ========================================================================= */ diff --git a/src/chars.c b/src/chars.c index 239d576..5201211 100644 --- a/src/chars.c +++ b/src/chars.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "chars" : Character set mappings and the Z-machine alphabet table */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ diff --git a/src/directs.c b/src/directs.c index 1583172..388ac86 100644 --- a/src/directs.c +++ b/src/directs.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "directs" : Directives (# commands) */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -108,21 +108,21 @@ extern int parse_given_directive(int internal_flag) return FALSE; if (!glulx_mode && no_abbreviations==96) - { error("All 96 Z-machine abbreviations already declared"); + { error_max_abbreviations(no_abbreviations); + panic_mode_error_recovery(); return FALSE; + } + if (!glulx_mode && no_abbreviations==MAX_ABBREVS) + { error_max_abbreviations(no_abbreviations); + /* This is no longer a memoryerror(); MAX_ABBREVS is an authoring decision for Z-code games. */ panic_mode_error_recovery(); return FALSE; } - if (no_abbreviations==MAX_ABBREVS) - memoryerror("MAX_ABBREVS", MAX_ABBREVS); if (abbrevs_lookup_table_made) { error("All abbreviations must be declared together"); panic_mode_error_recovery(); return FALSE; } if (token_type != DQ_TT) - return ebf_error_recover("abbreviation string", token_text); - if (strlen(token_text)<2) - { error_named("It's not worth abbreviating", token_text); - continue; + { return ebf_error_recover("abbreviation string", token_text); } /* Abbreviation string with null must fit in a MAX_ABBREV_LENGTH array. */ @@ -170,9 +170,9 @@ extern int parse_given_directive(int internal_flag) return ebf_error_recover("new constant name", token_text); } - if (!(sflags[i] & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))) + if (!(symbols[i].flags & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))) { discard_token_location(beginning_debug_location); - return ebf_symbol_error_recover("new constant name", token_text, typename(stypes[i]), slines[i]); + return ebf_symbol_error_recover("new constant name", token_text, typename(symbols[i].type), symbols[i].line); } assign_symbol(i, 0, CONSTANT_T); @@ -181,7 +181,7 @@ extern int parse_given_directive(int internal_flag) get_next_token(); if ((token_type == SEP_TT) && (token_value == COMMA_SEP)) - { if (debugfile_switch && !(sflags[i] & REDEFINABLE_SFLAG)) + { if (debugfile_switch && !(symbols[i].flags & REDEFINABLE_SFLAG)) { debug_file_printf(""); debug_file_printf("%s", constant_name); write_debug_symbol_optional_backpatch(i); @@ -192,7 +192,7 @@ extern int parse_given_directive(int internal_flag) } if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) - { if (debugfile_switch && !(sflags[i] & REDEFINABLE_SFLAG)) + { if (debugfile_switch && !(symbols[i].flags & REDEFINABLE_SFLAG)) { debug_file_printf(""); debug_file_printf("%s", constant_name); write_debug_symbol_optional_backpatch(i); @@ -209,7 +209,7 @@ extern int parse_given_directive(int internal_flag) if (AO.marker != 0) { assign_marked_symbol(i, AO.marker, AO.value, CONSTANT_T); - sflags[i] |= CHANGE_SFLAG; + symbols[i].flags |= CHANGE_SFLAG; if (i == grammar_version_symbol) error( "Grammar__Version must be given an explicit constant value"); @@ -228,7 +228,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); } } - if (debugfile_switch && !(sflags[i] & REDEFINABLE_SFLAG)) + if (debugfile_switch && !(symbols[i].flags & REDEFINABLE_SFLAG)) { debug_file_printf(""); debug_file_printf("%s", constant_name); write_debug_symbol_optional_backpatch(i); @@ -258,9 +258,9 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); return ebf_error_recover("name", token_text); i = -1; - if (sflags[token_value] & UNKNOWN_SFLAG) + if (symbols[token_value].flags & UNKNOWN_SFLAG) { i = token_value; - sflags[i] |= DEFCON_SFLAG; + symbols[i].flags |= DEFCON_SFLAG; } get_next_token(); @@ -273,7 +273,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); { if (AO.marker != 0) { assign_marked_symbol(i, AO.marker, AO.value, CONSTANT_T); - sflags[i] |= CHANGE_SFLAG; + symbols[i].flags |= CHANGE_SFLAG; } else assign_symbol(i, AO.value, CONSTANT_T); } @@ -328,6 +328,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); else { assembly_operand AO; put_token_back(); + if (ZCODE_LESS_DICT_DATA && !glulx_mode) + warning("The third dictionary field will be ignored because ZCODE_LESS_DICT_DATA is set"); AO = parse_expression(CONSTANT_CONTEXT); if (AO.marker != 0) error("A definite value must be given as a Dictionary flag"); @@ -405,17 +407,28 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); if (token_type != SYMBOL_TT) return ebf_error_recover("symbol name", token_text); + /* Special case: a symbol of the form "VN_nnnn" is considered + defined if the compiler version number is at least nnnn. + Compiler version numbers look like "1640" for Inform 6.40; + see RELEASE_NUMBER. + ("VN_nnnn" isn't a real symbol and can't be used in other + contexts.) */ if ((token_text[0] == 'V') && (token_text[1] == 'N') && (token_text[2] == '_') && (strlen(token_text)==7)) - { i = atoi(token_text+3); - if (VNUMBER < i) flag = (flag)?FALSE:TRUE; - goto HashIfCondition; + { + char *endstr; + i = strtol(token_text+3, &endstr, 10); + if (*endstr == '\0') { + /* All characters after the underscore were digits */ + if (VNUMBER < i) flag = (flag)?FALSE:TRUE; + goto HashIfCondition; + } } - if (sflags[token_value] & UNKNOWN_SFLAG) flag = (flag)?FALSE:TRUE; - else sflags[token_value] |= USED_SFLAG; + if (symbols[token_value].flags & UNKNOWN_SFLAG) flag = (flag)?FALSE:TRUE; + else symbols[token_value].flags |= USED_SFLAG; goto HashIfCondition; case IFNOT_CODE: @@ -428,14 +441,17 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); { dont_enter_into_symbol_table = -2; n = 1; directives.enabled = TRUE; do - { get_next_token(); + { + release_token_texts(); + get_next_token(); if (token_type == EOF_TT) { error("End of file reached in code 'If...'d out"); directives.enabled = FALSE; return TRUE; } if (token_type == DIRECTIVE_TT) - { switch(token_value) + { + switch(token_value) { case ENDIF_CODE: n--; break; case IFV3_CODE: @@ -508,7 +524,9 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); { dont_enter_into_symbol_table = -2; n = 1; directives.enabled = TRUE; do - { get_next_token(); + { + release_token_texts(); + get_next_token(); if (token_type == EOF_TT) { error("End of file reached in code 'If...'d out"); directives.enabled = FALSE; @@ -597,6 +615,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); get_next_token(); if (token_type != DQ_TT) return ebf_error_recover("filename in double-quotes", token_text); + if (strlen(token_text) >= PATHLEN-1) { + error_numbered("'Link' filename is too long; max length is", PATHLEN-1); + break; + } link_module(token_text); /* See "linker.c" */ break; @@ -620,8 +642,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); get_next_token(); i = token_value; if (token_type != SYMBOL_TT) return ebf_error_recover("new low string name", token_text); - if (!(sflags[i] & UNKNOWN_SFLAG)) - return ebf_symbol_error_recover("new low string name", token_text, typename(stypes[i]), slines[i]); + if (!(symbols[i].flags & UNKNOWN_SFLAG)) + return ebf_symbol_error_recover("new low string name", token_text, typename(symbols[i].type), symbols[i].line); get_next_token(); if (token_type != DQ_TT) @@ -761,7 +783,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); break; /* --------------------------------------------------------------------- */ - /* Property [long] [additive] name [alias oldname] */ + /* Property [long] [additive] name */ + /* Property [long] [additive] name alias oldname */ + /* Property [long] [additive] name defaultvalue */ + /* Property [long] individual name */ /* --------------------------------------------------------------------- */ case PROPERTY_CODE: make_property(); break; /* See "objects.c" */ @@ -812,10 +837,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); if (token_type != SYMBOL_TT) return ebf_error_recover("name of routine to replace", token_text); - if (!(sflags[token_value] & UNKNOWN_SFLAG)) + if (!(symbols[token_value].flags & UNKNOWN_SFLAG)) return ebf_error_recover("name of routine not yet defined", token_text); - sflags[token_value] |= REPLACE_SFLAG; + symbols[token_value].flags |= REPLACE_SFLAG; /* If a second symbol is provided, it will refer to the original (replaced) definition of the routine. */ @@ -829,7 +854,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); { return FALSE; } - if (token_type != SYMBOL_TT || !(sflags[token_value] & UNKNOWN_SFLAG)) + if (token_type != SYMBOL_TT || !(symbols[token_value].flags & UNKNOWN_SFLAG)) return ebf_error_recover("semicolon ';' or new routine name", token_text); /* Define the original-form symbol as a zero constant. Its @@ -891,8 +916,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); i = token_value; flag = FALSE; - if (sflags[i] & UNKNOWN_SFLAG) - { sflags[i] |= STUB_SFLAG; + if (symbols[i].flags & UNKNOWN_SFLAG) + { symbols[i].flags |= STUB_SFLAG; flag = TRUE; } @@ -908,15 +933,17 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); { /* Give these parameter-receiving local variables names for the benefit of the debugging information file, - and for assembly tracing to look sensible. */ + and for assembly tracing to look sensible. + (We don't set local_variable.keywords because we're not + going to be parsing any code.) */ - local_variable_texts[0] = "dummy1"; - local_variable_texts[1] = "dummy2"; - local_variable_texts[2] = "dummy3"; - local_variable_texts[3] = "dummy4"; + strcpy(local_variable_names[0].text, "dummy1"); + strcpy(local_variable_names[1].text, "dummy2"); + strcpy(local_variable_names[2].text, "dummy3"); + strcpy(local_variable_names[3].text, "dummy4"); assign_symbol(i, - assemble_routine_header(k, FALSE, (char *) symbs[i], FALSE, i), + assemble_routine_header(k, FALSE, symbols[i].name, FALSE, i), ROUTINE_T); /* Ensure the return value of a stubbed routine is false, @@ -929,7 +956,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); /* Inhibit "local variable unused" warnings */ - for (i=1; i<=k; i++) variable_usage[i] = 1; + for (i=1; i<=k; i++) variables[i].usage = 1; sequence_point_follows = FALSE; assemble_routine_end(FALSE, get_token_locations()); } @@ -966,14 +993,19 @@ the first constant definition"); declare_systemfile(); break; /* see "files.c" */ /* --------------------------------------------------------------------- */ - /* Trace dictionary */ - /* objects */ - /* symbols */ - /* verbs */ - /* [on/off] */ - /* assembly [on/off] */ - /* expressions [on/off] */ - /* lines [on/off] */ + /* Trace dictionary [on/NUM] */ + /* objects [on/NUM] */ + /* symbols [on/NUM] */ + /* verbs [on/NUM] */ + /* [on/off/NUM] {same as "assembly"} */ + /* assembly [on/off/NUM] */ + /* expressions [on/off/NUM] */ + /* lines [on/off/NUM] */ + /* tokens [on/off/NUM] */ + /* linker [on/off/NUM] */ + /* */ + /* The first four trace commands immediately display a compiler table. */ + /* The rest set or clear an ongoing trace. */ /* --------------------------------------------------------------------- */ case TRACE_CODE: @@ -982,62 +1014,105 @@ the first constant definition"); get_next_token(); trace_keywords.enabled = FALSE; directives.enabled = TRUE; - if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) - { asm_trace_level = 1; return FALSE; } + + if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) { + /* "Trace;" */ + put_token_back(); + i = ASSEMBLY_TK; + trace_level = &asm_trace_level; + j = 1; + goto HandleTraceKeyword; + } + if (token_type == NUMBER_TT) { + /* "Trace NUM;" */ + i = ASSEMBLY_TK; + trace_level = &asm_trace_level; + j = token_value; + goto HandleTraceKeyword; + } + /* Anything else must be "Trace KEYWORD..." Remember that + 'on' and 'off' are trace keywords. */ + if (token_type != TRACE_KEYWORD_TT) return ebf_error_recover("debugging keyword", token_text); trace_keywords.enabled = TRUE; - i = token_value; j = 0; + /* Note that "Trace verbs" doesn't affect list_verbs_setting. + It shows the grammar at this point in the code. Setting + list_verbs_setting shows the grammar at the end of + compilation. + Same goes for "Trace dictionary" and list_dict_setting, etc. */ + + i = token_value; + switch(i) - { case DICTIONARY_TK: break; - case OBJECTS_TK: break; - case VERBS_TK: break; - default: - switch(token_value) - { case ASSEMBLY_TK: - trace_level = &asm_trace_level; break; - case EXPRESSIONS_TK: - trace_level = &expr_trace_level; break; - case LINES_TK: - trace_level = &line_trace_level; break; - case TOKENS_TK: - trace_level = &tokens_trace_level; break; - case LINKER_TK: - trace_level = &linker_trace_level; break; - case SYMBOLS_TK: - trace_level = NULL; break; - default: - put_token_back(); - trace_level = &asm_trace_level; break; - } - j = 1; - get_next_token(); - if ((token_type == SEP_TT) && - (token_value == SEMICOLON_SEP)) - { put_token_back(); break; - } - if (token_type == NUMBER_TT) - { j = token_value; break; } - if ((token_type == TRACE_KEYWORD_TT) && (token_value == ON_TK)) - { j = 1; break; } - if ((token_type == TRACE_KEYWORD_TT) && (token_value == OFF_TK)) - { j = 0; break; } - put_token_back(); break; + { + case ASSEMBLY_TK: + trace_level = &asm_trace_level; break; + case EXPRESSIONS_TK: + trace_level = &expr_trace_level; break; + case TOKENS_TK: + trace_level = &tokens_trace_level; break; + case LINKER_TK: + trace_level = &linker_trace_level; break; + case DICTIONARY_TK: + case SYMBOLS_TK: + case OBJECTS_TK: + case VERBS_TK: + trace_level = NULL; break; + case LINES_TK: + /* never implememented */ + trace_level = NULL; break; + default: + put_token_back(); + trace_level = &asm_trace_level; break; } + + j = 1; + get_next_token(); + if ((token_type == SEP_TT) && + (token_value == SEMICOLON_SEP)) + { put_token_back(); + } + else if (token_type == NUMBER_TT) + { j = token_value; + } + else if ((token_type == TRACE_KEYWORD_TT) && (token_value == ON_TK)) + { j = 1; + } + else if ((token_type == TRACE_KEYWORD_TT) && (token_value == OFF_TK)) + { j = 0; + } + else + { put_token_back(); + } + + trace_keywords.enabled = FALSE; + + HandleTraceKeyword: + if (i == LINES_TK) { + warning_named("Trace option is not supported:", trace_keywords.keywords[i]); + break; + } + + if (trace_level == NULL && j == 0) { + warning_named("Trace directive to display table at 'off' level has no effect: table", trace_keywords.keywords[i]); + break; + } + switch(i) - { case DICTIONARY_TK: show_dictionary(); break; - case OBJECTS_TK: list_object_tree(); break; + { case DICTIONARY_TK: show_dictionary(j); break; + case OBJECTS_TK: list_object_tree(); break; case SYMBOLS_TK: list_symbols(j); break; - case VERBS_TK: list_verb_table(); break; + case VERBS_TK: list_verb_table(); break; default: - *trace_level = j; + if (trace_level) + *trace_level = j; break; } - trace_keywords.enabled = FALSE; break; /* --------------------------------------------------------------------- */ @@ -1049,12 +1124,12 @@ the first constant definition"); if (token_type != SYMBOL_TT) return ebf_error_recover("symbol name", token_text); - if (sflags[token_value] & UNKNOWN_SFLAG) + if (symbols[token_value].flags & UNKNOWN_SFLAG) { break; /* undef'ing an undefined constant is okay */ } - if (stypes[token_value] != CONSTANT_T) - { error_named("Cannot Undef a symbol which is not a defined constant:", (char *)symbs[token_value]); + if (symbols[token_value].type != CONSTANT_T) + { error_named("Cannot Undef a symbol which is not a defined constant:", symbols[token_value].name); break; } @@ -1062,7 +1137,7 @@ the first constant definition"); { write_debug_undef(token_value); } end_symbol_scope(token_value); - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; break; /* --------------------------------------------------------------------- */ @@ -1088,21 +1163,46 @@ the first constant definition"); } if (AO.marker != 0) - error("A definite value must be given as version number"); - else - if (glulx_mode) + { + error("A definite value must be given as version number."); + break; + } + else if (no_routines > 1) + { + /* The built-in Main__ routine is number zero. */ + error("A 'Version' directive must come before the first routine definition."); + break; + } + else if (glulx_mode) { warning("The Version directive does not work in Glulx. Use \ -vX.Y.Z instead, as either a command-line argument or a header comment."); break; } else - { i = AO.value; + { + int debtok; + i = AO.value; if ((i<3) || (i>8)) { error("The version number must be in the range 3 to 8"); break; } select_version(i); + /* We must now do a small dance to reset the DICT_ENTRY_BYTES + constant, which was defined at startup based on the Z-code + version. + The calculation here is repeated from select_target(). */ + DICT_ENTRY_BYTE_LENGTH = ((version_number==3)?7:9) - (ZCODE_LESS_DICT_DATA?1:0); + debtok = symbol_index("DICT_ENTRY_BYTES", -1); + if (!(symbols[debtok].flags & UNKNOWN_SFLAG)) + { + if (!(symbols[debtok].flags & REDEFINABLE_SFLAG)) + { + warning("The DICT_ENTRY_BYTES symbol is not marked redefinable"); + } + /* Redefine the symbol... */ + assign_symbol(debtok, DICT_ENTRY_BYTE_LENGTH, CONSTANT_T); + } } } break; /* see "inform.c" */ diff --git a/src/errors.c b/src/errors.c index 195bfe9..09efcd2 100644 --- a/src/errors.c +++ b/src/errors.c @@ -2,8 +2,8 @@ /* "errors" : Warnings, errors and fatal errors */ /* (with error throwback code for RISC OS machines) */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -197,11 +197,8 @@ extern void fatalerror(char *s) #endif #ifdef MAC_FACE close_all_source(); - if (temporary_files_switch) remove_temp_files(); abort_transcript_file(); free_arrays(); - if (store_the_text) - my_free(&all_text,"transcription text"); longjmp(g_fallback, 1); #endif exit(1); @@ -225,16 +222,6 @@ extern void memory_out_error(int32 size, int32 howmany, char *name) fatalerror(error_message_buff); } -extern void memoryerror(char *s, int32 size) -{ - snprintf(error_message_buff, ERROR_BUFLEN, - "The memory setting %s (which is %ld at present) has been \ -exceeded. Try running Inform again with $%s= on the \ -command line.",s,(long int) size,s); - ellipsize_error_message_buff(); - fatalerror(error_message_buff); -} - /* ------------------------------------------------------------------------- */ /* Survivable diagnostics: */ /* compilation errors style 1 */ @@ -244,8 +231,6 @@ command line.",s,(long int) size,s); /* indicate a bug in Inform) */ /* ------------------------------------------------------------------------- */ -static int errors[MAX_ERRORS]; - int no_errors, no_warnings, no_suppressed_warnings, no_link_errors, no_compiler_errors; @@ -253,7 +238,7 @@ char *forerrors_buff; int forerrors_pointer; static void message(int style, char *s) -{ int throw_style = style; +{ if (hash_printed_since_newline) printf("\n"); hash_printed_since_newline = FALSE; print_preamble(); @@ -261,22 +246,19 @@ static void message(int style, char *s) { case 1: printf("Error: "); no_errors++; break; case 2: printf("Warning: "); no_warnings++; break; case 3: printf("Error: [linking '%s'] ", current_module_filename); - no_link_errors++; no_errors++; throw_style=1; break; + no_link_errors++; no_errors++; break; case 4: printf("*** Compiler error: "); - no_compiler_errors++; throw_style=1; break; + no_compiler_errors++; break; } printf(" %s\n", s); #ifdef ARC_THROWBACK - throwback(throw_style, s); + throwback(((style <= 2) ? style : 1), s); #endif #ifdef MAC_FACE ProcessEvents (&g_proc); if (g_proc != true) { free_arrays(); - if (store_the_text) - my_free(&all_text,"transcription text"); close_all_source (); - if (temporary_files_switch) remove_temp_files(); abort_transcript_file(); longjmp (g_fallback, 1); } @@ -295,7 +277,6 @@ static void message(int style, char *s) extern void error(char *s) { if (no_errors == MAX_ERRORS) fatalerror("Too many errors: giving up"); - errors[no_errors] = no_syntax_lines; message(1,s); } @@ -395,6 +376,30 @@ extern void unicode_char_error(char *s, int32 uni) error(error_message_buff); } +extern void error_max_dynamic_strings(int index) +{ + if (index >= 96 && !glulx_mode) + snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(95) may be used in Z-code"); + else if (MAX_DYNAMIC_STRINGS == 0) + snprintf(error_message_buff, ERROR_BUFLEN, "Dynamic strings may not be used, because $MAX_DYNAMIC_STRINGS has been set to 0. Increase MAX_DYNAMIC_STRINGS."); + else if (MAX_DYNAMIC_STRINGS == 32 && !glulx_mode) + snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(%02d) may be used, because $MAX_DYNAMIC_STRINGS has its default value of %d. Increase MAX_DYNAMIC_STRINGS.", MAX_DYNAMIC_STRINGS-1, MAX_DYNAMIC_STRINGS); + else + snprintf(error_message_buff, ERROR_BUFLEN, "Only dynamic strings @(00) to @(%02d) may be used, because $MAX_DYNAMIC_STRINGS has been set to %d. Increase MAX_DYNAMIC_STRINGS.", MAX_DYNAMIC_STRINGS-1, MAX_DYNAMIC_STRINGS); + + ellipsize_error_message_buff(); + error(error_message_buff); +} + +extern void error_max_abbreviations(int index) +{ + /* This is only called for Z-code. */ + if (index >= 96) + error("The number of abbreviations has exceeded 96, the limit in Z-code"); + else + error("The number of abbreviations has exceeded MAX_ABBREVS. Increase MAX_ABBREVS."); +} + /* ------------------------------------------------------------------------- */ /* Style 2: Warning message routines */ /* ------------------------------------------------------------------------- */ @@ -419,6 +424,17 @@ extern void warning_named(char *s1, char *s2) message(2,error_message_buff); } +extern void symtype_warning(char *context, char *name, char *type, char *wanttype) +{ + if (nowarnings_switch) { no_suppressed_warnings++; return; } + if (name) + snprintf(error_message_buff, ERROR_BUFLEN, "In %s, expected %s but found %s \"%s\"", context, wanttype, type, name); + else + snprintf(error_message_buff, ERROR_BUFLEN, "In %s, expected %s but found %s", context, wanttype, type); + ellipsize_error_message_buff(); + message(2,error_message_buff); +} + extern void dbnu_warning(char *type, char *name, brief_location report_line) { int i; ErrorPosition E = ErrorReport; @@ -467,7 +483,6 @@ extern void obsolete_warning(char *s1) extern void link_error(char *s) { if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up"); - errors[no_errors] = no_syntax_lines; message(3,s); } @@ -484,11 +499,11 @@ extern void link_error_named(char *s1, char *s2) extern void print_sorry_message(void) { printf( "***********************************************************************\n\ -Compiler errors should never occur if Inform is working properly.\n\ -Check to see if there is a more recent version available, from which\n\ -the problem may have been removed. If not, please report this fault\n\ -and if at all possible, please include your source code, as faults\n\ -such as these are rare and often difficult to reproduce. Sorry.\n\ +* 'Compiler errors' should never occur if Inform is working properly. *\n\ +* Check to see if there is a more recent version available, from which\n\ +* the problem may have been removed. If not, please report this fault\n\ +* and if at all possible, please include your source code, as faults\n\ +* such as these are rare and often difficult to reproduce. Sorry.\n\ ***********************************************************************\n"); } @@ -580,7 +595,7 @@ extern void errors_begin_pass(void) } extern void errors_allocate_arrays(void) -{ forerrors_buff = my_malloc(512, "errors buffer"); +{ forerrors_buff = my_malloc(FORERRORS_SIZE, "errors buffer"); } extern void errors_free_arrays(void) diff --git a/src/expressc.c b/src/expressc.c index 6651547..f8c7553 100644 --- a/src/expressc.c +++ b/src/expressc.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "expressc" : The expression code generator */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -403,7 +403,7 @@ static void value_in_void_context_z(assembly_operand AO) case SHORT_CONSTANT_OT: t = ""; if (AO.marker == SYMBOL_MV) - t = (char *) (symbs[AO.value]); + t = (symbols[AO.value].name); break; case VARIABLE_OT: t = variable_name(AO.value); @@ -466,13 +466,13 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2, size_ao = zero_ao; size_ao.value = -1; for (x=0; x' to access a --> or table array"); } else { - if ((array_types[y] == BYTE_ARRAY) - || (array_types[y] == STRING_ARRAY)) + if ((arrays[y].type == BYTE_ARRAY) + || (arrays[y].type == STRING_ARRAY)) warning("Using '-->' to access a -> or string array"); } } @@ -520,15 +520,15 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2, max_ao = size_ao; if (byte_flag - && ((array_types[y] == WORD_ARRAY) - || (array_types[y] == TABLE_ARRAY))) + && ((arrays[y].type == WORD_ARRAY) + || (arrays[y].type == TABLE_ARRAY))) { max_ao.value = size_ao.value*2 + 1; type_ao.value += 8; } if ((!byte_flag) - && ((array_types[y] == BYTE_ARRAY) - || (array_types[y] == STRING_ARRAY) - || (array_types[y] == BUFFER_ARRAY))) + && ((arrays[y].type == BYTE_ARRAY) + || (arrays[y].type == STRING_ARRAY) + || (arrays[y].type == BUFFER_ARRAY))) { if ((size_ao.value % 2) == 0) max_ao.value = size_ao.value/2 - 1; else max_ao.value = (size_ao.value-1)/2; @@ -540,10 +540,10 @@ static void access_memory_z(int oc, assembly_operand AO1, assembly_operand AO2, if (max_ao.value >= 256) max_ao.type = LONG_CONSTANT_OT; /* Can't write to the size entry in a string or table */ - if (((array_types[y] == STRING_ARRAY) - || (array_types[y] == TABLE_ARRAY)) + if (((arrays[y].type == STRING_ARRAY) + || (arrays[y].type == TABLE_ARRAY)) && (!read_flag)) - { if ((array_types[y] == TABLE_ARRAY) && byte_flag) + { if ((arrays[y].type == TABLE_ARRAY) && byte_flag) zero_ao.value = 2; else zero_ao.value = 1; } @@ -716,6 +716,25 @@ static void compile_conditional_z(int oc, ASSERT_ZCODE(); + switch (oc) { + case test_attr_zc: + check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"has/hasnt\" expression"); + check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"has/hasnt\" expression"); + break; + case jin_zc: + check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"in/notin\" expression"); + check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"in/notin\" expression"); + break; + case 200: + /* first argument can be anything */ + check_warn_symbol_type(&AO2, CLASS_T, 0, "\"ofclass\" expression"); + break; + case 201: + /* first argument can be anything */ + check_warn_symbol_type(&AO2, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"provides\" expression"); + break; + } + if (oc<200) { if ((runtime_error_checking_switch) && (oc == jin_zc)) { if (flag) error_label = next_label++; @@ -785,7 +804,7 @@ static void value_in_void_context_g(assembly_operand AO) case ZEROCONSTANT_OT: t = ""; if (AO.marker == SYMBOL_MV) - t = (char *) (symbs[AO.value]); + t = (symbols[AO.value].name); break; case GLOBALVAR_OT: case LOCALVAR_OT: @@ -831,30 +850,30 @@ static void access_memory_g(int oc, assembly_operand AO1, assembly_operand AO2, { size_ao = zero_ao; size_ao.value = -1; for (x=0; x' to access a --> or table array"); } else { - if ((array_types[y] == BYTE_ARRAY) - || (array_types[y] == STRING_ARRAY)) + if ((arrays[y].type == BYTE_ARRAY) + || (arrays[y].type == STRING_ARRAY)) warning("Using '-->' to access a -> or string array"); } } @@ -876,25 +895,25 @@ static void access_memory_g(int oc, assembly_operand AO1, assembly_operand AO2, Here "size_ao.value" = largest permitted entry of its own kind */ max_ao = size_ao; if (data_len == 1 - && ((array_types[y] == WORD_ARRAY) - || (array_types[y] == TABLE_ARRAY))) + && ((arrays[y].type == WORD_ARRAY) + || (arrays[y].type == TABLE_ARRAY))) { max_ao.value = size_ao.value*4 + 3; type_ao.value += 8; } if (data_len == 4 - && ((array_types[y] == BYTE_ARRAY) - || (array_types[y] == STRING_ARRAY) - || (array_types[y] == BUFFER_ARRAY))) + && ((arrays[y].type == BYTE_ARRAY) + || (arrays[y].type == STRING_ARRAY) + || (arrays[y].type == BUFFER_ARRAY))) { max_ao.value = (size_ao.value-3)/4; type_ao.value += 16; } max_ao.value++; /* Can't write to the size entry in a string or table */ - if (((array_types[y] == STRING_ARRAY) - || (array_types[y] == TABLE_ARRAY)) + if (((arrays[y].type == STRING_ARRAY) + || (arrays[y].type == TABLE_ARRAY)) && (!read_flag)) - { if ((array_types[y] == TABLE_ARRAY) && data_len == 1) + { if ((arrays[y].type == TABLE_ARRAY) && data_len == 1) zero_ao.value = 4; else zero_ao.value = 1; } @@ -995,7 +1014,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, assembly_operand AO, AO2, AO3; int ln; int check_sp = FALSE, passed_label, failed_label, last_label; - + int pre_unreach; + if (veneer_mode) return AO1; @@ -1017,6 +1037,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, return AO1; } + pre_unreach = execution_never_reaches_here; + passed_label = next_label++; failed_label = next_label++; @@ -1037,6 +1059,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, /* Allow classes */ /* Test if zero... */ assembleg_1_branch(jz_gc, AO, failed_label); + if (!pre_unreach && execution_never_reaches_here) + execution_never_reaches_here |= EXECSTATE_NOWARN; /* Test if first byte is 0x70... */ assembleg_3(aloadb_gc, AO, zero_operand, stack_pointer); INITAO(&AO3); @@ -1047,6 +1071,8 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, else { /* Test if zero... */ assembleg_1_branch(jz_gc, AO, failed_label); + if (!pre_unreach && execution_never_reaches_here) + execution_never_reaches_here |= EXECSTATE_NOWARN; /* Test if first byte is 0x70... */ assembleg_3(aloadb_gc, AO, zero_operand, stack_pointer); INITAO(&AO3); @@ -1057,7 +1083,7 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, INITAOTV(&AO3, BYTECONSTANT_OT, GOBJFIELD_PARENT()); assembleg_3(aload_gc, AO, AO3, stack_pointer); ln = symbol_index("Class", -1); - AO3.value = svals[ln]; + AO3.value = symbols[ln].value; AO3.marker = OBJECT_MV; AO3.type = CONSTANT_OT; assembleg_2_branch(jne_gc, stack_pointer, AO3, passed_label); @@ -1078,7 +1104,7 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, else { /* Build the symbol for "Object" */ ln = symbol_index("Object", -1); - AO2.value = svals[ln]; + AO2.value = symbols[ln].value; AO2.marker = OBJECT_MV; AO2.type = CONSTANT_OT; if (check_sp) { @@ -1116,6 +1142,8 @@ static void compile_conditional_g(condclass *cc, switch ((cc-condclasses)*2 + 500) { case HAS_CC: + check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"has/hasnt\" expression"); + check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"has/hasnt\" expression"); if (runtime_error_checking_switch) { if (flag) error_label = next_label++; @@ -1194,6 +1222,8 @@ static void compile_conditional_g(condclass *cc, break; case IN_CC: + check_warn_symbol_type(&AO1, OBJECT_T, 0, "\"in/notin\" expression"); + check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"in/notin\" expression"); if (runtime_error_checking_switch) { if (flag) error_label = next_label++; @@ -1208,12 +1238,16 @@ static void compile_conditional_g(condclass *cc, break; case OFCLASS_CC: + /* first argument can be anything */ + check_warn_symbol_type(&AO2, CLASS_T, 0, "\"ofclass\" expression"); assembleg_call_2(veneer_routine(OC__Cl_VR), AO1, AO2, stack_pointer); the_zc = (flag ? jnz_gc : jz_gc); AO1 = stack_pointer; break; case PROVIDES_CC: + /* first argument can be anything */ + check_warn_symbol_type(&AO2, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"provides\" expression"); assembleg_call_2(veneer_routine(OP__Pr_VR), AO1, AO2, stack_pointer); the_zc = (flag ? jnz_gc : jz_gc); AO1 = stack_pointer; @@ -1275,6 +1309,12 @@ static void generate_code_from(int n, int void_flag) if ((opnum == LOGAND_OP) || (opnum == LOGOR_OP)) { generate_code_from(below, FALSE); + if (execution_never_reaches_here) { + /* If the condition never falls through to here, then it + was an "... && 0 && ..." test. Our convention is to skip + the "not reached" warnings for this case. */ + execution_never_reaches_here |= EXECSTATE_NOWARN; + } generate_code_from(ET[below].right, FALSE); goto OperatorGenerated; } @@ -1685,6 +1725,8 @@ static void generate_code_from(int n, int void_flag) case PROP_ADD_OP: { assembly_operand AO = ET[below].value; + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".&\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".&\" expression"); if (runtime_error_checking_switch && (!veneer_mode)) AO = check_nonzero_at_runtime(AO, -1, PROP_ADD_RTE); assemblez_2_to(get_prop_addr_zc, AO, @@ -1695,6 +1737,8 @@ static void generate_code_from(int n, int void_flag) case PROP_NUM_OP: { assembly_operand AO = ET[below].value; + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".#\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".#\" expression"); if (runtime_error_checking_switch && (!veneer_mode)) AO = check_nonzero_at_runtime(AO, -1, PROP_NUM_RTE); assemblez_2_to(get_prop_addr_zc, AO, @@ -1707,49 +1751,70 @@ static void generate_code_from(int n, int void_flag) break; case PROPERTY_OP: - { assembly_operand AO = ET[below].value; - + { + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression"); if (runtime_error_checking_switch && (!veneer_mode)) assemblez_3_to(call_vs_zc, veneer_routine(RT__ChPR_VR), - AO, ET[ET[below].right].value, temp_var1); + ET[below].value, ET[ET[below].right].value, temp_var1); else - assemblez_2_to(get_prop_zc, AO, + assemblez_2_to(get_prop_zc, ET[below].value, ET[ET[below].right].value, temp_var1); if (!void_flag) write_result_z(Result, temp_var1); } break; case MESSAGE_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression"); j=1; AI.operand[0] = veneer_routine(RV__Pr_VR); goto GenFunctionCallZ; case MPROP_ADD_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".&\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".&\" expression"); j=1; AI.operand[0] = veneer_routine(RA__Pr_VR); goto GenFunctionCallZ; case MPROP_NUM_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".#\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".#\" expression"); j=1; AI.operand[0] = veneer_routine(RL__Pr_VR); goto GenFunctionCallZ; case MESSAGE_SETEQUALS_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression"); j=1; AI.operand[0] = veneer_routine(WV__Pr_VR); goto GenFunctionCallZ; case MESSAGE_INC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"++.\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"++.\" expression"); j=1; AI.operand[0] = veneer_routine(IB__Pr_VR); goto GenFunctionCallZ; case MESSAGE_DEC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"--.\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"--.\" expression"); j=1; AI.operand[0] = veneer_routine(DB__Pr_VR); goto GenFunctionCallZ; case MESSAGE_POST_INC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".++\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".++\" expression"); j=1; AI.operand[0] = veneer_routine(IA__Pr_VR); goto GenFunctionCallZ; case MESSAGE_POST_DEC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".--\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".--\" expression"); j=1; AI.operand[0] = veneer_routine(DA__Pr_VR); goto GenFunctionCallZ; case SUPERCLASS_OP: j=1; AI.operand[0] = veneer_routine(RA__Sc_VR); goto GenFunctionCallZ; case PROP_CALL_OP: + check_warn_symbol_has_metaclass(&ET[below].value, "\".()\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".()\" expression"); j=1; AI.operand[0] = veneer_routine(CA__Pr_VR); goto GenFunctionCallZ; case MESSAGE_CALL_OP: + check_warn_symbol_has_metaclass(&ET[below].value, "\".()\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".()\" expression"); j=1; AI.operand[0] = veneer_routine(CA__Pr_VR); goto GenFunctionCallZ; @@ -1853,6 +1918,7 @@ static void generate_code_from(int n, int void_flag) case INDIRECT_SYSF: j=0; i = ET[below].right; + check_warn_symbol_type(&ET[i].value, ROUTINE_T, 0, "indirect function call"); goto IndirectFunctionCallZ; case CHILDREN_SYSF: @@ -1928,6 +1994,7 @@ static void generate_code_from(int n, int void_flag) } break; } + check_warn_symbol_type(&ET[below].value, ROUTINE_T, 0, "function call"); GenFunctionCallZ: @@ -1985,6 +2052,8 @@ static void generate_code_from(int n, int void_flag) break; case PROPERTY_SETEQUALS_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression"); if (!void_flag) { if (runtime_error_checking_switch) assemblez_4_to(call_zc, veneer_routine(RT__ChPS_VR), @@ -2128,6 +2197,8 @@ static void generate_code_from(int n, int void_flag) break; case PROPERTY_INC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"++.\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"++.\" expression"); assemblez_store(temp_var1, ET[below].value); assemblez_store(temp_var2, ET[ET[below].right].value); assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3); @@ -2140,6 +2211,8 @@ static void generate_code_from(int n, int void_flag) break; case PROPERTY_DEC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"--.\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"--.\" expression"); assemblez_store(temp_var1, ET[below].value); assemblez_store(temp_var2, ET[ET[below].right].value); assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3); @@ -2152,6 +2225,8 @@ static void generate_code_from(int n, int void_flag) break; case PROPERTY_POST_INC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".++\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".++\" expression"); assemblez_store(temp_var1, ET[below].value); assemblez_store(temp_var2, ET[ET[below].right].value); assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3); @@ -2164,6 +2239,8 @@ static void generate_code_from(int n, int void_flag) break; case PROPERTY_POST_DEC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".--\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".--\" expression"); assemblez_store(temp_var1, ET[below].value); assemblez_store(temp_var2, ET[ET[below].right].value); assemblez_2_to(get_prop_zc, temp_var1, temp_var2, temp_var3); @@ -2181,7 +2258,7 @@ static void generate_code_from(int n, int void_flag) compiler_error("Expr code gen: Can't generate yet"); } } - else { + else { /* Glulx */ assembly_operand AO, AO2; if (operators[opnum].opcode_number_g != -1) { @@ -2375,37 +2452,53 @@ static void generate_code_from(int n, int void_flag) case PROPERTY_OP: case MESSAGE_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression"); AO = veneer_routine(RV__Pr_VR); goto TwoArgFunctionCall; case MPROP_ADD_OP: case PROP_ADD_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".&\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".&\" expression"); AO = veneer_routine(RA__Pr_VR); goto TwoArgFunctionCall; case MPROP_NUM_OP: case PROP_NUM_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".#\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".#\" expression"); AO = veneer_routine(RL__Pr_VR); goto TwoArgFunctionCall; case PROP_CALL_OP: case MESSAGE_CALL_OP: + check_warn_symbol_has_metaclass(&ET[below].value, "\".()\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".()\" expression"); AO2 = veneer_routine(CA__Pr_VR); i = below; goto DoFunctionCall; case MESSAGE_INC_OP: case PROPERTY_INC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"++.\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"++.\" expression"); AO = veneer_routine(IB__Pr_VR); goto TwoArgFunctionCall; case MESSAGE_DEC_OP: case PROPERTY_DEC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\"--.\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\"--.\" expression"); AO = veneer_routine(DB__Pr_VR); goto TwoArgFunctionCall; case MESSAGE_POST_INC_OP: case PROPERTY_POST_INC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".++\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".++\" expression"); AO = veneer_routine(IA__Pr_VR); goto TwoArgFunctionCall; case MESSAGE_POST_DEC_OP: case PROPERTY_POST_DEC_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".--\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".--\" expression"); AO = veneer_routine(DA__Pr_VR); goto TwoArgFunctionCall; case SUPERCLASS_OP: @@ -2425,6 +2518,8 @@ static void generate_code_from(int n, int void_flag) case PROPERTY_SETEQUALS_OP: case MESSAGE_SETEQUALS_OP: + check_warn_symbol_type(&ET[below].value, OBJECT_T, CLASS_T, "\".\" expression"); + check_warn_symbol_type(&ET[ET[below].right].value, PROPERTY_T, INDIVIDUAL_PROPERTY_T, "\".\" expression"); if (runtime_error_checking_switch && (!veneer_mode)) AO = veneer_routine(RT__ChPS_VR); else @@ -2615,6 +2710,7 @@ static void generate_code_from(int n, int void_flag) case INDIRECT_SYSF: i = ET[below].right; + check_warn_symbol_type(&ET[i].value, ROUTINE_T, 0, "indirect function call"); goto IndirectFunctionCallG; case GLK_SYSF: @@ -2684,6 +2780,7 @@ static void generate_code_from(int n, int void_flag) break; } + check_warn_symbol_type(&ET[below].value, ROUTINE_T, 0, "function call"); i = below; IndirectFunctionCallG: @@ -2776,6 +2873,7 @@ static void generate_code_from(int n, int void_flag) if (ET[n].to_expression) { + int32 donelabel; if (void_flag) { warning("Logical expression has no side-effects"); if (ET[n].true_label != -1) @@ -2784,18 +2882,26 @@ static void generate_code_from(int n, int void_flag) assemble_label_no(ET[n].false_label); } else if (ET[n].true_label != -1) - { assemblez_1(push_zc, zero_operand); - assemblez_jump(next_label++); + { + donelabel = next_label++; + if (!execution_never_reaches_here) { + assemblez_1(push_zc, zero_operand); + assemblez_jump(donelabel); + } assemble_label_no(ET[n].true_label); assemblez_1(push_zc, one_operand); - assemble_label_no(next_label-1); + assemble_forward_label_no(donelabel); } else - { assemblez_1(push_zc, one_operand); - assemblez_jump(next_label++); + { + donelabel = next_label++; + if (!execution_never_reaches_here) { + assemblez_1(push_zc, one_operand); + assemblez_jump(donelabel); + } assemble_label_no(ET[n].false_label); assemblez_1(push_zc, zero_operand); - assemble_label_no(next_label-1); + assemble_forward_label_no(donelabel); } ET[n].value = stack_pointer; } @@ -2808,6 +2914,7 @@ static void generate_code_from(int n, int void_flag) if (ET[n].to_expression) { + int32 donelabel; if (void_flag) { warning("Logical expression has no side-effects"); if (ET[n].true_label != -1) @@ -2816,18 +2923,26 @@ static void generate_code_from(int n, int void_flag) assemble_label_no(ET[n].false_label); } else if (ET[n].true_label != -1) - { assembleg_store(stack_pointer, zero_operand); - assembleg_jump(next_label++); + { + donelabel = next_label++; + if (!execution_never_reaches_here) { + assembleg_store(stack_pointer, zero_operand); + assembleg_jump(donelabel); + } assemble_label_no(ET[n].true_label); assembleg_store(stack_pointer, one_operand); - assemble_label_no(next_label-1); + assemble_forward_label_no(donelabel); } else - { assembleg_store(stack_pointer, one_operand); - assembleg_jump(next_label++); + { + donelabel = next_label++; + if (!execution_never_reaches_here) { + assembleg_store(stack_pointer, one_operand); + assembleg_jump(donelabel); + } assemble_label_no(ET[n].false_label); assembleg_store(stack_pointer, zero_operand); - assemble_label_no(next_label-1); + assemble_forward_label_no(donelabel); } ET[n].value = stack_pointer; } diff --git a/src/expressp.c b/src/expressp.c index 5b3c0a7..0e0cf6f 100644 --- a/src/expressp.c +++ b/src/expressp.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "expressp" : The expression parser */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -65,10 +65,7 @@ static int comma_allowed, arrow_allowed, superclass_allowed, array_init_ambiguity, action_ambiguity, etoken_count, inserting_token, bracket_level; -extern int *variable_usage; - -/* Must be at least as many as keyword_group system_functions (currently 12) */ -int system_function_usage[32]; +int system_function_usage[NUMBER_SYSTEM_FUNCTIONS]; static int get_next_etoken(void) { int v, symbol = 0, mark_symbol_as_used = FALSE, @@ -86,6 +83,7 @@ static int get_next_etoken(void) current_token.value = token_value; current_token.type = token_type; current_token.marker = 0; + current_token.symindex = -1; current_token.symtype = 0; current_token.symflags = -1; } @@ -93,7 +91,7 @@ static int get_next_etoken(void) switch(current_token.type) { case LOCAL_VARIABLE_TT: current_token.type = VARIABLE_TT; - variable_usage[current_token.value] = TRUE; + variables[current_token.value].usage = TRUE; break; case DQ_TT: @@ -136,15 +134,16 @@ but not used as a value:", unicode); mark_symbol_as_used = TRUE; - v = svals[symbol]; + v = symbols[symbol].value; - current_token.symtype = stypes[symbol]; - current_token.symflags = sflags[symbol]; - switch(stypes[symbol]) + current_token.symindex = symbol; + current_token.symtype = symbols[symbol].type; + current_token.symflags = symbols[symbol].flags; + switch(symbols[symbol].type) { case ROUTINE_T: /* Replaced functions must always be backpatched because there could be another definition coming. */ - if (sflags[symbol] & REPLACE_SFLAG) + if (symbols[symbol].flags & REPLACE_SFLAG) { current_token.marker = SYMBOL_MV; if (module_switch) import_symbol(symbol); v = symbol; @@ -171,7 +170,7 @@ but not used as a value:", unicode); if (module_switch) current_token.marker = IDENT_MV; break; case CONSTANT_T: - if (sflags[symbol] & (UNKNOWN_SFLAG + CHANGE_SFLAG)) + if (symbols[symbol].flags & (UNKNOWN_SFLAG + CHANGE_SFLAG)) { current_token.marker = SYMBOL_MV; if (module_switch) import_symbol(symbol); v = symbol; @@ -185,7 +184,7 @@ but not used as a value:", unicode); current_token.marker = 0; break; } - if (sflags[symbol] & SYSTEM_SFLAG) + if (symbols[symbol].flags & SYSTEM_SFLAG) current_token.marker = 0; current_token.value = v; @@ -205,9 +204,9 @@ but not used as a value:", unicode); else current_token.type = SMALL_NUMBER_TT; } - if (stypes[symbol] == GLOBAL_VARIABLE_T) + if (symbols[symbol].type == GLOBAL_VARIABLE_T) { current_token.type = VARIABLE_TT; - variable_usage[current_token.value] = TRUE; + variables[current_token.value].usage = TRUE; } break; @@ -329,7 +328,7 @@ but not used as a value:", unicode); current_token.text += 3; current_token.type = SYMBOL_TT; symbol = symbol_index(current_token.text, -1); - if (stypes[symbol] != GLOBAL_VARIABLE_T) { + if (symbols[symbol].type != GLOBAL_VARIABLE_T) { ebf_error( "global variable name after '#g$'", current_token.text); @@ -339,7 +338,7 @@ but not used as a value:", unicode); break; } mark_symbol_as_used = TRUE; - current_token.value = svals[symbol] - MAX_LOCAL_VARIABLES; + current_token.value = symbols[symbol].value - MAX_LOCAL_VARIABLES; current_token.marker = 0; if (!glulx_mode) { if (current_token.value >= 0x100) @@ -439,7 +438,7 @@ but not used as a value:", unicode); if (token_type_allowable[current_token.type]==0) { if (expr_trace_level >= 3) { printf("Discarding as not allowable: '%s' ", current_token.text); - describe_token(current_token); + describe_token(¤t_token); printf("\n"); } current_token.type = ENDEXP_TT; @@ -454,16 +453,16 @@ but not used as a value:", unicode); || (operators[current_token.value].usage == PRE_U))) { if (expr_trace_level >= 3) { printf("Discarding as no longer part: '%s' ", current_token.text); - describe_token(current_token); + describe_token(¤t_token); printf("\n"); } current_token.type = ENDEXP_TT; } else - { if (mark_symbol_as_used) sflags[symbol] |= USED_SFLAG; + { if (mark_symbol_as_used) symbols[symbol].flags |= USED_SFLAG; if (expr_trace_level >= 3) { printf("Expr token = '%s' ", current_token.text); - describe_token(current_token); + describe_token(¤t_token); printf("\n"); } } @@ -500,7 +499,7 @@ const int prec_table[] = { }; -static int find_prec(token_data a, token_data b) +static int find_prec(const token_data *a, const token_data *b) { /* We are comparing the precedence of tokens a and b (where a occurs to the left of b). If the expression is correct, @@ -518,14 +517,14 @@ static int find_prec(token_data a, token_data b) int i, j, l1, l2; - switch(a.type) + switch(a->type) { case SUBOPEN_TT: i=0; break; case SUBCLOSE_TT: i=1; break; case ENDEXP_TT: i=2; break; case OP_TT: i=3; break; default: i=4; break; } - switch(b.type) + switch(b->type) { case SUBOPEN_TT: i+=0; break; case SUBCLOSE_TT: i+=5; break; case ENDEXP_TT: i+=10; break; @@ -535,10 +534,10 @@ static int find_prec(token_data a, token_data b) j = prec_table[i]; if (j != -1) return j; - l1 = operators[a.value].precedence; - l2 = operators[b.value].precedence; - if (operators[b.value].usage == PRE_U) return LOWER_P; - if (operators[a.value].usage == POST_U) return GREATER_P; + l1 = operators[a->value].precedence; + l2 = operators[b->value].precedence; + if (operators[b->value].usage == PRE_U) return LOWER_P; + if (operators[a->value].usage == POST_U) return GREATER_P; /* Anomalous rule to resolve the function call precedence, which is different on the right from on the left, e.g., in: @@ -551,7 +550,7 @@ static int find_prec(token_data a, token_data b) if (l1 < l2) return LOWER_P; if (l1 > l2) return GREATER_P; - switch(operators[a.value].associativity) + switch(operators[a->value].associativity) { case L_A: return GREATER_P; case R_A: return LOWER_P; case 0: return e5; @@ -561,7 +560,8 @@ static int find_prec(token_data a, token_data b) /* --- Converting token to operand ----------------------------------------- */ -/* Must match the switch statement below */ +/* List used to generate gameinfo.dbg. + Must match the switch statement below. */ int z_system_constant_list[] = { adjectives_table_SC, actions_table_SC, @@ -605,6 +605,8 @@ int z_system_constant_list[] = highest_class_number_SC, class_objects_array_SC, highest_object_number_SC, + dictionary_table_SC, + grammar_table_SC, -1 }; static int32 value_of_system_constant_z(int t) @@ -642,9 +644,13 @@ static int32 value_of_system_constant_z(int t) case ipv__end_SC: return variables_offset; case array__start_SC: - return variables_offset + (MAX_GLOBAL_VARIABLES*WORDSIZE); + return variables_offset + (MAX_ZCODE_GLOBAL_VARS*WORDSIZE); case array__end_SC: return static_memory_offset; + case dictionary_table_SC: + return dictionary_offset; + case grammar_table_SC: + return static_memory_offset; case highest_attribute_number_SC: return no_attributes-1; @@ -706,7 +712,8 @@ static int32 value_of_system_constant_z(int t) return(0); } -/* Must match the switch statement below */ +/* List used to generate gameinfo.dbg. + Must match the switch statement below. */ int glulx_system_constant_list[] = { classes_table_SC, identifiers_table_SC, @@ -765,7 +772,15 @@ extern int32 value_of_system_constant(int t) return value_of_system_constant_g(t); } -static int evaluate_term(token_data t, assembly_operand *o) +extern char *name_of_system_constant(int t) +{ + if (t < 0 || t >= NO_SYSTEM_CONSTANTS) { + return "???"; + } + return system_constants.keywords[t]; +} + +static int evaluate_term(const token_data *t, assembly_operand *o) { /* If the given token is a constant, evaluate it into the operand. For now, the identifiers are considered variables. @@ -774,13 +789,12 @@ static int evaluate_term(token_data t, assembly_operand *o) int32 v; - o->marker = t.marker; - o->symtype = t.symtype; - o->symflags = t.symflags; + o->marker = t->marker; + o->symindex = t->symindex; - switch(t.type) + switch(t->type) { case LARGE_NUMBER_TT: - v = t.value; + v = t->value; if (!glulx_mode) { if (v < 0) v = v + 0x10000; o->type = LONG_CONSTANT_OT; @@ -792,7 +806,7 @@ static int evaluate_term(token_data t, assembly_operand *o) } return(TRUE); case SMALL_NUMBER_TT: - v = t.value; + v = t->value; if (!glulx_mode) { if (v < 0) v = v + 0x10000; o->type = SHORT_CONSTANT_OT; @@ -809,7 +823,7 @@ static int evaluate_term(token_data t, assembly_operand *o) o->type = LONG_CONSTANT_OT; else o->type = CONSTANT_OT; - o->value = dictionary_add(t.text, 0x80, 0, 0); + o->value = dictionary_add(t->text, 0x80, 0, 0); return(TRUE); case DQ_TT: /* Create as a static string */ @@ -817,14 +831,14 @@ static int evaluate_term(token_data t, assembly_operand *o) o->type = LONG_CONSTANT_OT; else o->type = CONSTANT_OT; - o->value = compile_string(t.text, STRCTX_GAME); + o->value = compile_string(t->text, STRCTX_GAME); return(TRUE); case VARIABLE_TT: if (!glulx_mode) { o->type = VARIABLE_OT; } else { - if (t.value >= MAX_LOCAL_VARIABLES) { + if (t->value >= MAX_LOCAL_VARIABLES) { o->type = GLOBALVAR_OT; } else { @@ -833,21 +847,21 @@ static int evaluate_term(token_data t, assembly_operand *o) o->type = LOCALVAR_OT; } } - o->value = t.value; + o->value = t->value; return(TRUE); case SYSFUN_TT: if (!glulx_mode) { o->type = VARIABLE_OT; - o->value = t.value + 256; + o->value = t->value + 256; } else { o->type = SYSFUN_OT; - o->value = t.value; + o->value = t->value; } - system_function_usage[t.value] = 1; + system_function_usage[t->value] = 1; return(TRUE); case ACTION_TT: - *o = action_of_name(t.text); + *o = action_of_name(t->text); return(TRUE); case SYSTEM_CONSTANT_TT: /* Certain system constants depend only on the @@ -856,7 +870,7 @@ static int evaluate_term(token_data t, assembly_operand *o) them immediately. */ if (!glulx_mode) { o->type = LONG_CONSTANT_OT; - switch(t.value) + switch(t->value) { case version_number_SC: o->type = SHORT_CONSTANT_OT; @@ -873,6 +887,8 @@ static int evaluate_term(token_data t, assembly_operand *o) case dict_par3_SC: o->type = SHORT_CONSTANT_OT; o->marker = 0; + if (ZCODE_LESS_DICT_DATA) + error("#dict_par3 is unavailable when ZCODE_LESS_DICT_DATA is set"); v = (version_number==3)?6:8; break; case lowest_attribute_number_SC: case lowest_action_number_SC: @@ -893,7 +909,7 @@ static int evaluate_term(token_data t, assembly_operand *o) o->type = SHORT_CONSTANT_OT; o->marker = 0; v = oddeven_packing_switch; break; default: - v = t.value; + v = t->value; o->marker = INCON_MV; break; } @@ -901,7 +917,7 @@ static int evaluate_term(token_data t, assembly_operand *o) } else { o->type = CONSTANT_OT; - switch(t.value) + switch(t->value) { /* The three dict_par flags point at the lower byte of the flag field, because the library is written @@ -943,7 +959,7 @@ static int evaluate_term(token_data t, assembly_operand *o) /* ###fix: need to fill more of these in! */ default: - v = t.value; + v = t->value; o->marker = INCON_MV; break; } @@ -957,35 +973,40 @@ static int evaluate_term(token_data t, assembly_operand *o) /* --- Emitter ------------------------------------------------------------- */ -expression_tree_node *ET; +expression_tree_node *ET; /* Allocated to ET_used */ +static memory_list ET_memlist; static int ET_used; extern void clear_expression_space(void) { ET_used = 0; } -static assembly_operand *emitter_stack; -static int *emitter_markers; -static int *emitter_bracket_counts; +typedef struct emitterstackinfo_s { + assembly_operand op; + int marker; + int bracket_count; +} emitterstackinfo; #define FUNCTION_VALUE_MARKER 1 #define ARGUMENT_VALUE_MARKER 2 #define OR_VALUE_MARKER 3 +static emitterstackinfo *emitter_stack; /* Allocated to emitter_sp */ +static memory_list emitter_stack_memlist; static int emitter_sp; static int is_property_t(int symbol_type) { return ((symbol_type == PROPERTY_T) || (symbol_type == INDIVIDUAL_PROPERTY_T)); } -static void mark_top_of_emitter_stack(int marker, token_data t) +static void mark_top_of_emitter_stack(int marker, const token_data *t) { if (emitter_sp < 1) { compiler_error("SR error: Attempt to add a marker to the top of an empty emitter stack"); return; } if (expr_trace_level >= 2) { printf("Marking top of emitter stack (which is "); - print_operand(emitter_stack[emitter_sp-1]); + print_operand(&emitter_stack[emitter_sp-1].op, FALSE); printf(") as "); switch(marker) { @@ -1004,21 +1025,20 @@ static void mark_top_of_emitter_stack(int marker, token_data t) } printf("\n"); } - if (emitter_markers[emitter_sp-1]) + if (emitter_stack[emitter_sp-1].marker) { if (marker == ARGUMENT_VALUE_MARKER) { warning("Ignoring spurious leading comma"); return; } - error_named("Missing operand for", t.text); - if (emitter_sp == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); - emitter_markers[emitter_sp] = 0; - emitter_bracket_counts[emitter_sp] = 0; - emitter_stack[emitter_sp] = zero_operand; + error_named("Missing operand for", t->text); + ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1); + emitter_stack[emitter_sp].marker = 0; + emitter_stack[emitter_sp].bracket_count = 0; + emitter_stack[emitter_sp].op = zero_operand; emitter_sp++; } - emitter_markers[emitter_sp-1] = marker; + emitter_stack[emitter_sp-1].marker = marker; } static void add_bracket_layer_to_emitter_stack(int depth) @@ -1026,7 +1046,7 @@ static void add_bracket_layer_to_emitter_stack(int depth) if (emitter_sp < depth + 1) return; if (expr_trace_level >= 2) printf("Adding bracket layer\n"); - ++emitter_bracket_counts[emitter_sp-depth-1]; + ++emitter_stack[emitter_sp-depth-1].bracket_count; } static void remove_bracket_layer_from_emitter_stack() @@ -1034,46 +1054,47 @@ static void remove_bracket_layer_from_emitter_stack() if (emitter_sp < 2) return; if (expr_trace_level >= 2) printf("Removing bracket layer\n"); - if (emitter_bracket_counts[emitter_sp-2] <= 0) + if (emitter_stack[emitter_sp-2].bracket_count <= 0) { compiler_error("SR error: Attempt to remove a nonexistent bracket layer from the emitter stack"); return; } - --emitter_bracket_counts[emitter_sp-2]; + --emitter_stack[emitter_sp-2].bracket_count; } -static void emit_token(token_data t) +static void emit_token(const token_data *t) { assembly_operand o1, o2; int arity, stack_size, i; int op_node_number, operand_node_number, previous_node_number; int32 x = 0; if (expr_trace_level >= 2) - { printf("Output: %-19s%21s ", t.text, ""); + { printf("Output: %-19s%21s ", t->text, ""); for (i=0; itype == SUBOPEN_TT) return; stack_size = 0; while ((stack_size < emitter_sp) && - !emitter_markers[emitter_sp-stack_size-1] && - !emitter_bracket_counts[emitter_sp-stack_size-1]) + !emitter_stack[emitter_sp-stack_size-1].marker && + !emitter_stack[emitter_sp-stack_size-1].bracket_count) stack_size++; - if (t.type == SUBCLOSE_TT) - { if (stack_size < emitter_sp && emitter_bracket_counts[emitter_sp-stack_size-1]) + if (t->type == SUBCLOSE_TT) + { if (stack_size < emitter_sp && emitter_stack[emitter_sp-stack_size-1].bracket_count) { if (stack_size == 0) { error("No expression between brackets '(' and ')'"); - emitter_stack[emitter_sp] = zero_operand; - emitter_markers[emitter_sp] = 0; - emitter_bracket_counts[emitter_sp] = 0; - ++emitter_sp; + ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1); + emitter_stack[emitter_sp].op = zero_operand; + emitter_stack[emitter_sp].marker = 0; + emitter_stack[emitter_sp].bracket_count = 0; + emitter_sp++; } else if (stack_size < 1) compiler_error("SR error: emitter stack empty in subexpression"); @@ -1084,14 +1105,14 @@ static void emit_token(token_data t) return; } - if (t.type != OP_TT) - { emitter_markers[emitter_sp] = 0; - emitter_bracket_counts[emitter_sp] = 0; + if (t->type != OP_TT) + { + ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1); + emitter_stack[emitter_sp].marker = 0; + emitter_stack[emitter_sp].bracket_count = 0; - if (emitter_sp == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); - if (!evaluate_term(t, &(emitter_stack[emitter_sp++]))) - compiler_error_named("Emit token error:", t.text); + if (!evaluate_term(t, &(emitter_stack[emitter_sp++].op))) + compiler_error_named("Emit token error:", t->text); return; } @@ -1099,85 +1120,85 @@ static void emit_token(token_data t) call, since we ignore spurious leading commas in function argument lists) with no intervening brackets. Function calls are variadic, so we don't apply argument-separating commas. */ - if (t.value == COMMA_OP && + if (t->value == COMMA_OP && stack_size < emitter_sp && - (emitter_markers[emitter_sp-stack_size-1] == ARGUMENT_VALUE_MARKER || - emitter_markers[emitter_sp-stack_size-1] == FUNCTION_VALUE_MARKER) && - !emitter_bracket_counts[emitter_sp-stack_size-1]) + (emitter_stack[emitter_sp-stack_size-1].marker == ARGUMENT_VALUE_MARKER || + emitter_stack[emitter_sp-stack_size-1].marker == FUNCTION_VALUE_MARKER) && + !emitter_stack[emitter_sp-stack_size-1].bracket_count) { if (expr_trace_level >= 2) printf("Treating comma as argument-separating\n"); return; } - if (t.value == OR_OP) + if (t->value == OR_OP) return; arity = 1; - if (t.value == FCALL_OP) + if (t->value == FCALL_OP) { if (expr_trace_level >= 3) { printf("FCALL_OP finds marker stack: "); - for (x=0; x= 256 && - emitter_stack[emitter_sp-arity].value < 288)) - { int index = emitter_stack[emitter_sp-arity].value; + emitter_stack[emitter_sp-arity].op.type == VARIABLE_OT && + emitter_stack[emitter_sp-arity].op.value >= 256 && + emitter_stack[emitter_sp-arity].op.value < 288)) + { int index = emitter_stack[emitter_sp-arity].op.value; if(!glulx_mode) index -= 256; if(index >= 0 && index < NUMBER_SYSTEM_FUNCTIONS) error_named("System function name used as a value:", system_functions.keywords[index]); else compiler_error("Found unnamed system function used as a value"); - emitter_stack[emitter_sp-arity] = zero_operand; + emitter_stack[emitter_sp-arity].op = zero_operand; } ++arity; } } else { arity = 1; - if (operators[t.value].usage == IN_U) arity = 2; + if (operators[t->value].usage == IN_U) arity = 2; - if (operators[t.value].precedence == 3) + if (operators[t->value].precedence == 3) { arity = 2; x = emitter_sp-1; - if(!emitter_markers[x] && !emitter_bracket_counts[x]) - { for (--x; emitter_markers[x] == OR_VALUE_MARKER && !emitter_bracket_counts[x]; --x) + if(!emitter_stack[x].marker && !emitter_stack[x].bracket_count) + { for (--x; emitter_stack[x].marker == OR_VALUE_MARKER && !emitter_stack[x].bracket_count; --x) { ++arity; ++stack_size; } - for (;x >= 0 && !emitter_markers[x] && !emitter_bracket_counts[x]; --x) + for (;x >= 0 && !emitter_stack[x].marker && !emitter_stack[x].bracket_count; --x) ++stack_size; } } if (arity > stack_size) - { error_named("Missing operand for", t.text); + { error_named("Missing operand for", t->text); while (arity > stack_size) - { if (emitter_sp == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); - emitter_markers[emitter_sp] = 0; - emitter_bracket_counts[emitter_sp] = 0; - emitter_stack[emitter_sp] = zero_operand; + { ensure_memory_list_available(&emitter_stack_memlist, emitter_sp+1); + emitter_stack[emitter_sp].marker = 0; + emitter_stack[emitter_sp].bracket_count = 0; + emitter_stack[emitter_sp].op = zero_operand; emitter_sp++; stack_size++; } } } - /* pseudo-typecheck in 6.30 */ + /* pseudo-typecheck in 6.30: catch an unqualified property name */ for (i = 1; i <= arity; i++) { - o1 = emitter_stack[emitter_sp - i]; - if (is_property_t(o1.symtype) ) { - switch(t.value) + o1 = emitter_stack[emitter_sp - i].op; + if ((o1.symindex >= 0) + && is_property_t(symbols[o1.symindex].type)) { + switch(t->value) { case FCALL_OP: case SETEQUALS_OP: case NOTEQUAL_OP: @@ -1189,7 +1210,11 @@ static void emit_token(token_data t) case PROPERTY_OP: if (i < arity) break; case GE_OP: case LE_OP: - if ((i < arity) && (o1.symflags & STAR_SFLAG)) break; + /* Direction properties "n_to", etc *are* compared + in some libraries. They have STAR_SFLAG to tell us + to skip the warning. */ + if ((i < arity) + && (symbols[o1.symindex].flags & STAR_SFLAG)) break; default: warning("Property name in expression is not qualified by object"); } @@ -1198,9 +1223,9 @@ static void emit_token(token_data t) switch(arity) { case 1: - o1 = emitter_stack[emitter_sp - 1]; + o1 = emitter_stack[emitter_sp - 1].op; if ((o1.marker == 0) && is_constant_ot(o1.type)) - { switch(t.value) + { switch(t->value) { case UNARY_MINUS_OP: x = -o1.value; goto FoldConstant; case ARTNOT_OP: if (!glulx_mode) @@ -1216,8 +1241,8 @@ static void emit_token(token_data t) break; case 2: - o1 = emitter_stack[emitter_sp - 2]; - o2 = emitter_stack[emitter_sp - 1]; + o1 = emitter_stack[emitter_sp - 2].op; + o2 = emitter_stack[emitter_sp - 1].op; if ((o1.marker == 0) && (o2.marker == 0) && is_constant_ot(o1.type) && is_constant_ot(o2.type)) @@ -1232,7 +1257,7 @@ static void emit_token(token_data t) ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value; } - switch(t.value) + switch(t->value) { case PLUS_OP: x = ov1 + ov2; goto FoldConstantC; case MINUS_OP: x = ov1 - ov2; goto FoldConstantC; @@ -1242,7 +1267,7 @@ static void emit_token(token_data t) if (ov2 == 0) error("Division of constant by zero"); else - if (t.value == DIVIDE_OP) { + if (t->value == DIVIDE_OP) { if (ov2 < 0) { ov1 = -ov1; ov2 = -ov2; @@ -1291,13 +1316,32 @@ static void emit_token(token_data t) } } + + /* We can also fold logical operations if they are certain + to short-circuit. The right-hand argument is skipped even + if it's non-constant or has side effects. */ + + if ((o1.marker == 0) + && is_constant_ot(o1.type)) { + + if (t->value == LOGAND_OP && o1.value == 0) + { + x = 0; + goto FoldConstant; + } + + if (t->value == LOGOR_OP && o1.value != 0) + { + x = 1; + goto FoldConstant; + } + } } + ensure_memory_list_available(&ET_memlist, ET_used+1); op_node_number = ET_used++; - if (op_node_number == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); - ET[op_node_number].operator_number = t.value; + ET[op_node_number].operator_number = t->value; ET[op_node_number].up = -1; ET[op_node_number].down = -1; ET[op_node_number].right = -1; @@ -1311,14 +1355,14 @@ static void emit_token(token_data t) if (expr_trace_level >= 3) printf("i=%d, emitter_sp=%d, arity=%d, ETU=%d\n", i, emitter_sp, arity, ET_used); - if (emitter_stack[i].type == EXPRESSION_OT) - operand_node_number = emitter_stack[i].value; + if (emitter_stack[i].op.type == EXPRESSION_OT) + operand_node_number = emitter_stack[i].op.value; else - { operand_node_number = ET_used++; - if (operand_node_number == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); + { + ensure_memory_list_available(&ET_memlist, ET_used+1); + operand_node_number = ET_used++; ET[operand_node_number].down = -1; - ET[operand_node_number].value = emitter_stack[i]; + ET[operand_node_number].value = emitter_stack[i].op; } ET[operand_node_number].up = op_node_number; ET[operand_node_number].right = -1; @@ -1333,11 +1377,11 @@ static void emit_token(token_data t) emitter_sp = emitter_sp - arity + 1; - emitter_stack[emitter_sp - 1].type = EXPRESSION_OT; - emitter_stack[emitter_sp - 1].value = op_node_number; + emitter_stack[emitter_sp - 1].op.type = EXPRESSION_OT; + emitter_stack[emitter_sp - 1].op.value = op_node_number; + emitter_stack[emitter_sp - 1].op.marker = 0; emitter_stack[emitter_sp - 1].marker = 0; - emitter_markers[emitter_sp - 1] = 0; - emitter_bracket_counts[emitter_sp - 1] = 0; + emitter_stack[emitter_sp - 1].bracket_count = 0; /* Remove the marker for the brackets implied by operator precedence */ remove_bracket_layer_from_emitter_stack(); @@ -1352,7 +1396,7 @@ static void emit_token(token_data t) { char folding_error[40]; int32 ov1 = (o1.value >= 0x8000) ? (o1.value - 0x10000) : o1.value; int32 ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value; - switch(t.value) + switch(t->value) { case PLUS_OP: sprintf(folding_error, "%d + %d = %d", ov1, ov2, x); @@ -1382,28 +1426,28 @@ the range -32768 to +32767:", folding_error); if (!glulx_mode) { if (x<256) - emitter_stack[emitter_sp - 1].type = SHORT_CONSTANT_OT; - else emitter_stack[emitter_sp - 1].type = LONG_CONSTANT_OT; + emitter_stack[emitter_sp - 1].op.type = SHORT_CONSTANT_OT; + else emitter_stack[emitter_sp - 1].op.type = LONG_CONSTANT_OT; } else { if (x == 0) - emitter_stack[emitter_sp - 1].type = ZEROCONSTANT_OT; + emitter_stack[emitter_sp - 1].op.type = ZEROCONSTANT_OT; else if (x >= -128 && x <= 127) - emitter_stack[emitter_sp - 1].type = BYTECONSTANT_OT; + emitter_stack[emitter_sp - 1].op.type = BYTECONSTANT_OT; else if (x >= -32768 && x <= 32767) - emitter_stack[emitter_sp - 1].type = HALFCONSTANT_OT; + emitter_stack[emitter_sp - 1].op.type = HALFCONSTANT_OT; else - emitter_stack[emitter_sp - 1].type = CONSTANT_OT; + emitter_stack[emitter_sp - 1].op.type = CONSTANT_OT; } - emitter_stack[emitter_sp - 1].value = x; + emitter_stack[emitter_sp - 1].op.value = x; + emitter_stack[emitter_sp - 1].op.marker = 0; emitter_stack[emitter_sp - 1].marker = 0; - emitter_markers[emitter_sp - 1] = 0; - emitter_bracket_counts[emitter_sp - 1] = 0; + emitter_stack[emitter_sp - 1].bracket_count = 0; if (expr_trace_level >= 2) { printf("Folding constant to: "); - print_operand(emitter_stack[emitter_sp - 1]); + print_operand(&emitter_stack[emitter_sp - 1].op, FALSE); printf("\n"); } @@ -1419,9 +1463,7 @@ static void show_node(int n, int depth, int annotate) for (j=0; j<2*depth+2; j++) printf(" "); if (ET[n].down == -1) - { print_operand(ET[n].value); - if (annotate && (ET[n].value.marker != 0)) - printf(" [%s]", describe_mv(ET[n].value.marker)); + { print_operand(&ET[n].value, annotate); printf("\n"); } else @@ -1443,9 +1485,7 @@ static void show_node(int n, int depth, int annotate) extern void show_tree(assembly_operand AO, int annotate) { if (AO.type == EXPRESSION_OT) show_node(AO.value, 0, annotate); else - { printf("Constant: "); print_operand(AO); - if (annotate && (AO.marker != 0)) - printf(" [%s]", describe_mv(AO.marker)); + { printf("Constant: "); print_operand(&AO, annotate); printf("\n"); } } @@ -1624,10 +1664,25 @@ static void delete_negations(int n, int context) (etc) to delete the ~~ operator from the tree. Since this is depth first, the ~~ being deleted has no ~~s beneath it, which - is important to make "negate_condition" work. */ + is important to make "negate_condition" work. + + We also do the check for (x <= y or z) here. This must be done + before negate_condition. + */ int i; + if (ET[n].operator_number == LE_OP || ET[n].operator_number == GE_OP) { + if (ET[n].down != -1 + && ET[ET[n].down].right != -1 + && ET[ET[ET[n].down].right].right != -1) { + if (ET[n].operator_number == LE_OP) + warning("The behavior of (<= or) may be unexpected."); + else + warning("The behavior of (>= or) may be unexpected."); + } + } + if (ET[n].right != -1) delete_negations(ET[n].right, context); if (ET[n].down == -1) return; delete_negations(ET[n].down, context); @@ -1654,9 +1709,9 @@ static void insert_exp_to_cond(int n, int context) if (ET[n].down == -1) { if (context==CONDITION_CONTEXT) - { new = ET_used++; - if (new == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); + { + ensure_memory_list_available(&ET_memlist, ET_used+1); + new = ET_used++; ET[new] = ET[n]; ET[n].down = new; ET[n].operator_number = NONZERO_OP; ET[new].up = n; ET[new].right = -1; @@ -1677,9 +1732,8 @@ static void insert_exp_to_cond(int n, int context) default: if (context != CONDITION_CONTEXT) break; + ensure_memory_list_available(&ET_memlist, ET_used+1); new = ET_used++; - if (new == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); ET[new] = ET[n]; ET[n].down = new; ET[n].operator_number = NONZERO_OP; ET[new].up = n; ET[new].right = -1; @@ -1736,9 +1790,8 @@ static void func_args_on_stack(int n, int context) || ET[fnaddr].value.value == INDIRECT_SYSF || ET[fnaddr].value.value == GLK_SYSF))) { if (etoken_num_children(pn) > (unsigned int)(opnum == FCALL_OP ? 4:3)) { + ensure_memory_list_available(&ET_memlist, ET_used+1); new = ET_used++; - if (new == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); ET[new] = ET[n]; ET[n].down = new; ET[n].operator_number = PUSH_OP; @@ -1759,9 +1812,8 @@ static assembly_operand check_conditions(assembly_operand AO, int context) if (AO.type != EXPRESSION_OT) { if (context != CONDITION_CONTEXT) return AO; + ensure_memory_list_available(&ET_memlist, ET_used+1); n = ET_used++; - if (n == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); ET[n].down = -1; ET[n].up = -1; ET[n].right = -1; @@ -1782,7 +1834,8 @@ static assembly_operand check_conditions(assembly_operand AO, int context) /* --- Shift-reduce parser ------------------------------------------------- */ static int sr_sp; -static token_data *sr_stack; +static token_data *sr_stack; /* Allocated to sr_sp */ +static memory_list sr_stack_memlist; extern assembly_operand parse_expression(int context) { @@ -1866,6 +1919,7 @@ extern assembly_operand parse_expression(int context) previous_token.type = ENDEXP_TT; previous_token.value = 0; + ensure_memory_list_available(&sr_stack_memlist, 1); sr_sp = 1; sr_stack[0] = previous_token; @@ -1905,7 +1959,7 @@ extern assembly_operand parse_expression(int context) return AO; } - AO = emitter_stack[0]; + AO = emitter_stack[0].op; if (AO.type == EXPRESSION_OT) { if (expr_trace_level >= 3) { printf("Tree before lvalue checking:\n"); @@ -1917,7 +1971,9 @@ extern assembly_operand parse_expression(int context) ET[AO.value].up = -1; } else { - if ((context != CONSTANT_CONTEXT) && is_property_t(AO.symtype) + if ((context != CONSTANT_CONTEXT) + && (AO.symindex >= 0) + && is_property_t(symbols[AO.symindex].type) && (arrow_allowed) && (!bare_prop_allowed)) warning("Bare property name found. \"self.prop\" intended?"); } @@ -1934,7 +1990,7 @@ extern assembly_operand parse_expression(int context) return(AO); } - switch(find_prec(a,b)) + switch(find_prec(&a,&b)) { case e5: /* Associativity error */ error_named("Brackets mandatory to clarify order of:", @@ -1942,14 +1998,13 @@ extern assembly_operand parse_expression(int context) case LOWER_P: case EQUAL_P: - if (sr_sp == MAX_EXPRESSION_NODES) - memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES); + ensure_memory_list_available(&sr_stack_memlist, sr_sp+1); sr_stack[sr_sp++] = b; switch(b.type) { case SUBOPEN_TT: if (sr_sp >= 2 && sr_stack[sr_sp-2].type == OP_TT && sr_stack[sr_sp-2].value == FCALL_OP) - mark_top_of_emitter_stack(FUNCTION_VALUE_MARKER, b); + mark_top_of_emitter_stack(FUNCTION_VALUE_MARKER, &b); else add_bracket_layer_to_emitter_stack(0); break; @@ -1958,7 +2013,7 @@ extern assembly_operand parse_expression(int context) case OR_OP: if (sr_stack[sr_sp-2].type == OP_TT && operators[sr_stack[sr_sp-2].value].precedence == 3) - mark_top_of_emitter_stack(OR_VALUE_MARKER, b); + mark_top_of_emitter_stack(OR_VALUE_MARKER, &b); else { error("'or' not between values to the right of a condition"); /* Convert to + for error recovery purposes */ @@ -1974,7 +2029,7 @@ extern assembly_operand parse_expression(int context) if (shallowest_open_bracket_index > 0 && sr_stack[shallowest_open_bracket_index-1].type == OP_TT && sr_stack[shallowest_open_bracket_index-1].value == FCALL_OP) - { mark_top_of_emitter_stack(ARGUMENT_VALUE_MARKER, b); + { mark_top_of_emitter_stack(ARGUMENT_VALUE_MARKER, &b); break; } /* Non-argument-separating commas get treated like any other operator; we fall through to the default case. */ @@ -1992,9 +2047,9 @@ extern assembly_operand parse_expression(int context) case GREATER_P: do { pop = sr_stack[sr_sp - 1]; - emit_token(pop); + emit_token(&pop); sr_sp--; - } while (find_prec(sr_stack[sr_sp-1], pop) != LOWER_P); + } while (find_prec(&sr_stack[sr_sp-1], &pop) != LOWER_P); break; case e1: /* Missing operand error */ @@ -2056,7 +2111,12 @@ extern void init_expressp_vars(void) { int i; /* make_operands(); */ make_lexical_interface_tables(); - for (i=0;i<32;i++) system_function_usage[i] = 0; + for (i=0; i", total_files); @@ -157,8 +142,10 @@ extern void load_sourcefile(char *filename_given, int same_directory_flag) fatalerror_named("Couldn't open source file", name); InputFiles[total_files].is_input = TRUE; + InputFiles[total_files].initial_buffering = TRUE; - if (line_trace_level > 0) printf("\nOpening file \"%s\"\n",name); + if (files_trace_setting > 0) + printf("Opening file \"%s\"\n",name); total_files++; total_input_files++; @@ -169,7 +156,8 @@ static void close_sourcefile(int file_number) { if (InputFiles[file_number-1].handle == NULL) return; - /* Close this file. */ + /* Close this file. But keep the InputFiles entry around, including + its filename. */ if (ferror(InputFiles[file_number-1].handle)) fatalerror_named("I/O failure: couldn't read from source file", @@ -179,7 +167,10 @@ static void close_sourcefile(int file_number) InputFiles[file_number-1].handle = NULL; - if (line_trace_level > 0) printf("\nClosing file\n"); + if (files_trace_setting > 0) { + char *str = (InputFiles[file_number-1].initial_buffering ? " (in initial buffering)" : ""); + printf("Closing file \"%s\"%s\n", InputFiles[file_number-1].filename, str); + } } extern void close_all_source(void) @@ -219,17 +210,10 @@ extern int register_orig_sourcefile(char *filename) name = filename; /* no translation */ - if (total_files == MAX_SOURCE_FILES) - memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES); - - if (filename_storage_left <= (int)strlen(name)) - memoryerror("MAX_SOURCE_FILES", MAX_SOURCE_FILES); - - filename_storage_left -= strlen(name)+1; - strcpy(filename_storage_p, name); - InputFiles[total_files].filename = filename_storage_p; + ensure_memory_list_available(&InputFiles_memlist, total_files+1); - filename_storage_p += strlen(name)+1; + InputFiles[total_files].filename = my_malloc(strlen(name)+1, "filename storage"); + strcpy(InputFiles[total_files].filename, name); if (debugfile_switch) { debug_file_printf("", total_files); @@ -242,6 +226,7 @@ extern int register_orig_sourcefile(char *filename) InputFiles[total_files].handle = NULL; InputFiles[total_files].is_input = FALSE; + InputFiles[total_files].initial_buffering = FALSE; total_files++; current_origsource_file = total_files; @@ -403,7 +388,7 @@ static void output_compression(int entnum, int32 *size, int *count) } static void output_file_z(void) -{ FILE *fin=NULL; char new_name[PATHLEN]; +{ char new_name[PATHLEN]; int32 length, blanks=0, size, i, j, offset; uint32 code_length, size_before_code, next_cons_check; int use_function; @@ -457,14 +442,6 @@ static void output_file_z(void) /* (2) Output the compiled code area. */ - if (temporary_files_switch) - { fclose(Temp2_fp); - Temp2_fp = NULL; - fin=fopen(Temp2_Name,"rb"); - if (fin==NULL) - fatalerror("I/O failure: couldn't reopen temporary file 2"); - } - if (!OMIT_UNUSED_ROUTINES) { /* This is the old-fashioned case, which is easy. All of zcode_area (zmachine_pc bytes) will be output. next_cons_check will be @@ -495,11 +472,11 @@ static void output_file_z(void) for (i=0; i= 0x80) long_flag = FALSE; backpatch_marker &= 0x7f; offset = offset + (backpatch_marker/32)*0x10000; @@ -514,17 +491,13 @@ static void output_file_z(void) while (j static_strings_extent || ch < 0) compiler_error("Read too much not-yet-compressed text."); @@ -1191,13 +1087,13 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver int32 val, ix, jx; for (ix=0, jx=0; ix%s " "(superseded replacement)", - symbs[symbol_index]); + symbols[symbol_index].name); if (fseek(Debug_fp, 0L, SEEK_END)) { fatalerror("I/O failure: can't seek in debugging information file"); } } fgetpos - (Debug_fp, &replacement_debug_backpatch_positions[symbol_index].position); - replacement_debug_backpatch_positions[symbol_index].valid = TRUE; - debug_file_printf("%s", symbs[symbol_index]); + (Debug_fp, &symbol_debug_info[symbol_index].replacement_backpatch_pos.position); + symbol_debug_info[symbol_index].replacement_backpatch_pos.valid = TRUE; + debug_file_printf("%s", symbols[symbol_index].name); /* Space for: artificial="true" (superseded replacement) */ debug_file_printf(" "); } extern void write_debug_symbol_backpatch(int32 symbol_index) -{ if (symbol_debug_backpatch_positions[symbol_index].valid) { +{ if (symbol_debug_info[symbol_index].backpatch_pos.valid) { compiler_error("Symbol entry incorrectly reused in debug information " "file backpatching"); } - fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position); - symbol_debug_backpatch_positions[symbol_index].valid = TRUE; + fgetpos(Debug_fp, &symbol_debug_info[symbol_index].backpatch_pos.position); + symbol_debug_info[symbol_index].backpatch_pos.valid = TRUE; /* Reserve space for up to 10 digits plus a negative sign. */ debug_file_printf("*BACKPATCH*"); } extern void write_debug_symbol_optional_backpatch(int32 symbol_index) -{ if (symbol_debug_backpatch_positions[symbol_index].valid) { +{ if (symbol_debug_info[symbol_index].backpatch_pos.valid) { compiler_error("Symbol entry incorrectly reused in debug information " "file backpatching"); } @@ -1614,8 +1510,8 @@ extern void write_debug_symbol_optional_backpatch(int32 symbol_index) so that we'll be in the same case as above if the symbol is eventually defined. */ debug_file_printf(""); - fgetpos(Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position); - symbol_debug_backpatch_positions[symbol_index].valid = TRUE; + fgetpos(Debug_fp, &symbol_debug_info[symbol_index].backpatch_pos.position); + symbol_debug_info[symbol_index].backpatch_pos.valid = TRUE; debug_file_printf("*BACKPATCH*"); } @@ -1731,18 +1627,18 @@ extern void end_writing_debug_sections(int32 end_address) } extern void write_debug_undef(int32 symbol_index) -{ if (!symbol_debug_backpatch_positions[symbol_index].valid) +{ if (!symbol_debug_info[symbol_index].backpatch_pos.valid) { compiler_error ("Attempt to erase debugging information never written or since " "erased"); } - if (stypes[symbol_index] != CONSTANT_T) + if (symbols[symbol_index].type != CONSTANT_T) { compiler_error ("Attempt to erase debugging information for a non-constant " "because of an #undef"); } if (fsetpos - (Debug_fp, &symbol_debug_backpatch_positions[symbol_index].position)) + (Debug_fp, &symbol_debug_info[symbol_index].backpatch_pos.position)) { fatalerror("I/O failure: can't seek in debugging information file"); } /* There are 7 characters in ``''. */ @@ -1752,7 +1648,7 @@ extern void write_debug_undef(int32 symbol_index) /* Overwrite: *BACKPATCH* */ debug_file_printf(" "); nullify_debug_file_position - (&symbol_debug_backpatch_positions[symbol_index]); + (&symbol_debug_info[symbol_index].backpatch_pos); if (fseek(Debug_fp, 0L, SEEK_END)) { fatalerror("I/O failure: can't seek in debugging information file"); } @@ -1783,14 +1679,13 @@ static void apply_debug_information_backpatches static void apply_debug_information_symbol_backpatches() { int backpatch_symbol; for (backpatch_symbol = no_symbols; backpatch_symbol--;) - { if (symbol_debug_backpatch_positions[backpatch_symbol].valid) + { if (symbol_debug_info[backpatch_symbol].backpatch_pos.valid) { if (fsetpos(Debug_fp, - &symbol_debug_backpatch_positions - [backpatch_symbol].position)) + &symbol_debug_info[backpatch_symbol].backpatch_pos.position)) { fatalerror ("I/O failure: can't seek in debugging information file"); } - debug_file_printf("%11d", svals[backpatch_symbol]); + debug_file_printf("%11d", symbols[backpatch_symbol].value); } } } @@ -1834,57 +1729,6 @@ extern void end_debug_file() close_debug_file(); } -/* ------------------------------------------------------------------------- */ -/* Temporary storage files: */ -/* */ -/* Temp file 1 is used to hold the static strings area, as compiled */ -/* 2 to hold compiled routines of Z-code */ -/* 3 to hold the link data table (but only for modules) */ -/* */ -/* (Though annoying, this procedure typically saves about 200K of memory, */ -/* an important point for Amiga and sub-386 PC users of Inform) */ -/* ------------------------------------------------------------------------- */ - -extern void open_temporary_files(void) -{ translate_temp_filename(1); - Temp1_fp=fopen(Temp1_Name,"wb"); - if (Temp1_fp==NULL) fatalerror_named("Couldn't open temporary file 1", - Temp1_Name); - translate_temp_filename(2); - Temp2_fp=fopen(Temp2_Name,"wb"); - if (Temp2_fp==NULL) fatalerror_named("Couldn't open temporary file 2", - Temp2_Name); - - if (!module_switch) return; - translate_temp_filename(3); - Temp3_fp=fopen(Temp3_Name,"wb"); - if (Temp3_fp==NULL) fatalerror_named("Couldn't open temporary file 3", - Temp3_Name); -} - -extern void check_temp_files(void) -{ - if (ferror(Temp1_fp)) - fatalerror("I/O failure: couldn't write to temporary file 1"); - if (ferror(Temp2_fp)) - fatalerror("I/O failure: couldn't write to temporary file 2"); - if (module_switch && ferror(Temp3_fp)) - fatalerror("I/O failure: couldn't write to temporary file 3"); -} - -extern void remove_temp_files(void) -{ if (Temp1_fp != NULL) fclose(Temp1_fp); - Temp1_fp = NULL; - if (Temp2_fp != NULL) fclose(Temp2_fp); - Temp2_fp = NULL; - remove(Temp1_Name); remove(Temp2_Name); - if (module_switch) - { if (Temp3_fp != NULL) fclose(Temp3_fp); - Temp3_fp = NULL; - remove(Temp3_Name); - } -} - /* ========================================================================= */ /* Data structure management routines */ /* ------------------------------------------------------------------------- */ @@ -1908,8 +1752,6 @@ extern void files_begin_prepass(void) extern void files_begin_pass(void) { total_chars_read=0; - if (temporary_files_switch) - open_temporary_files(); } static void initialise_accumulator @@ -1927,10 +1769,9 @@ static void initialise_accumulator } extern void files_allocate_arrays(void) -{ filename_storage = my_malloc(MAX_SOURCE_FILES*64, "filename storage"); - filename_storage_p = filename_storage; - filename_storage_left = MAX_SOURCE_FILES*64; - InputFiles = my_malloc(MAX_SOURCE_FILES*sizeof(FileId), +{ + initialise_memory_list(&InputFiles_memlist, + sizeof(FileId), 16, (void**)&InputFiles, "input file storage"); if (debugfile_switch) { if (glulx_mode) @@ -1959,8 +1800,14 @@ static void tear_down_accumulator(debug_backpatch_accumulator *accumulator) } extern void files_free_arrays(void) -{ my_free(&filename_storage, "filename storage"); - my_free(&InputFiles, "input file storage"); +{ + int ix; + for (ix=0; ix -#define INCLUDE_TASK_ID -#define Temporary_Directory "T:" -#ifdef MAIN_INFORM_FILE -static int32 unique_task_id(void) -{ return (int32)FindTask(NULL); -} -#endif #endif /* ------------------------------------------------------------------------- */ /* ARCHIMEDES block: Acorn/RISC OS settings */ @@ -229,17 +190,12 @@ static int32 unique_task_id(void) #define MACHINE_STRING "RISC OS" /* 2 */ #define CHAR_IS_UNSIGNED -/* 3 */ -#define DEFAULT_MEMORY_SIZE LARGE_SIZE /* 4 */ #define FN_SEP '.' #define STANDARD_DIRECTORIES #define NO_FILE_EXTENSIONS #define Source_Directory "inform" #define ICL_Directory "ICL" -/* 5 */ -#define ENABLE_TEMPORARY_PATH -#define Temporary_Directory "ram:" /* 6 */ #define ARC_THROWBACK #endif @@ -249,20 +205,8 @@ static int32 unique_task_id(void) #ifdef ATARIST /* 1 */ #define MACHINE_STRING "Atari ST" -/* 3 */ -#define DEFAULT_MEMORY_SIZE LARGE_SIZE /* 4 */ #define FN_SEP '/' -/* 5 */ -#ifndef TOSFS -#define Temporary_Directory "/tmp" -#define INCLUDE_TASK_ID -#ifdef MAIN_INFORM_FILE -static int32 unique_task_id(void) -{ return (int32)getpid(); -} -#endif -#endif #endif /* ------------------------------------------------------------------------- */ /* BEOS block */ @@ -270,13 +214,9 @@ static int32 unique_task_id(void) #ifdef BEOS /* 1 */ #define MACHINE_STRING "BeOS" -/* 3 */ -#define DEFAULT_MEMORY_SIZE LARGE_SIZE /* 4 */ #define FN_SEP '/' #define FILE_EXTENSIONS -/* 5 */ -#define Temporary_Directory "/tmp" #endif /* ------------------------------------------------------------------------- */ /* LINUX block */ @@ -286,14 +226,13 @@ static int32 unique_task_id(void) #define MACHINE_STRING "Linux" /* 2 */ #define HAS_REALPATH -/* 3 */ -#define DEFAULT_MEMORY_SIZE HUGE_SIZE /* 4 */ #define FN_SEP '/' -/* 5 */ -#define Temporary_Directory "/tmp" /* 6 */ #define PATHLEN 8192 +#if defined(__STDC__) && (__STDC_VERSION__ >= 201112L) +#define USE_C11_TIME_API +#endif #endif /* ------------------------------------------------------------------------- */ /* Macintosh block */ @@ -318,8 +257,6 @@ static int32 unique_task_id(void) #define PROMPT_INPUT #endif #endif -/* 3 */ -#define DEFAULT_MEMORY_SIZE LARGE_SIZE /* 4 */ #define FN_SEP ':' #ifdef MAC_MPW @@ -342,8 +279,6 @@ static int32 unique_task_id(void) #define MACHINE_STRING "OS/2" /* 2 */ #define CHAR_IS_UNSIGNED -/* 3 */ -#define DEFAULT_MEMORY_SIZE LARGE_SIZE /* 4 */ #define FN_SEP '/' #endif @@ -355,24 +290,13 @@ static int32 unique_task_id(void) #define MACHINE_STRING "MacOS" /* 2 */ #define HAS_REALPATH -/* 3 */ -#define DEFAULT_MEMORY_SIZE HUGE_SIZE /* 4 */ #define FN_SEP '/' -/* 5 */ -#define Temporary_Directory "/tmp" -#define INCLUDE_TASK_ID -#define _POSIX_C_SOURCE 199506L -#define _XOPEN_SOURCE 500 -#ifdef MAIN_INFORM_FILE -#include -#include -static int32 unique_task_id(void) -{ return (int32)getpid(); -} -#endif /* 6 */ #define PATHLEN 8192 +#if defined(__STDC__) && (__STDC_VERSION__ >= 201112L) +#define USE_C11_TIME_API +#endif #endif /* ------------------------------------------------------------------------- */ /* PC and PC_QUICKC block */ @@ -384,14 +308,6 @@ static int32 unique_task_id(void) #ifdef PC /* 1 */ #define MACHINE_STRING "PC" -/* 2 */ -#define USE_TEMPORARY_FILES -/* 3 */ -#ifdef PC_QUICKC -#define DEFAULT_MEMORY_SIZE SMALL_SIZE -#else -#define DEFAULT_MEMORY_SIZE LARGE_SIZE -#endif /* 4 */ #define FN_SEP '\\' /* 6 */ @@ -405,17 +321,13 @@ static int32 unique_task_id(void) #define MACHINE_STRING "Win32" /* 2 */ #define HAS_REALPATH -/* 3 */ -#define DEFAULT_MEMORY_SIZE HUGE_SIZE /* 4 */ #define FN_SEP '\\' /* 6 */ #define DEFAULT_ERROR_FORMAT 1 #define PATHLEN 512 -#ifdef _MSC_VER /* Microsoft Visual C++ */ -#define snprintf _snprintf -#define isnan _isnan -#define isinf(x) (!_isnan(x) && !_finite(x)) +#if _MSC_VER >= 1920 /* Visual C++ 2019 */ +#define USE_C11_TIME_API #endif #endif /* ------------------------------------------------------------------------- */ @@ -428,20 +340,8 @@ static int32 unique_task_id(void) #endif /* 2 */ #define HAS_REALPATH -/* 3 */ -#define DEFAULT_MEMORY_SIZE HUGE_SIZE /* 4 */ #define FN_SEP '/' -/* 5 */ -#define PATHLEN 512 -#define Temporary_Directory "/tmp" -#define INCLUDE_TASK_ID -#ifdef MAIN_INFORM_FILE -#include -static int32 unique_task_id(void) -{ return (int32)getpid(); -} -#endif #endif /* ------------------------------------------------------------------------- */ /* VMS (Dec VAX and Alpha) block */ @@ -459,8 +359,6 @@ static int32 unique_task_id(void) #endif /* 2 */ #define CHAR_IS_UNSIGNED -/* 3 */ -#define DEFAULT_MEMORY_SIZE LARGE_SIZE /* 4 */ #define FN_SEP '/' #define Code_Extension ".zip" @@ -561,9 +459,6 @@ static int32 unique_task_id(void) #ifndef Module_Directory #define Module_Directory "modules" #endif -#ifndef Temporary_Directory -#define Temporary_Directory "" -#endif #ifndef ICL_Directory #define ICL_Directory "" #endif @@ -582,9 +477,6 @@ static int32 unique_task_id(void) #ifndef Module_Directory #define Module_Directory "" #endif -#ifndef Temporary_Directory -#define Temporary_Directory "" -#endif #ifndef ICL_Directory #define ICL_Directory "" #endif @@ -602,18 +494,10 @@ static int32 unique_task_id(void) #define PATHLEN 128 #endif -#ifndef Temporary_File -#define Temporary_File "Inftemp" -#endif - #ifndef DEFAULT_ERROR_FORMAT #define DEFAULT_ERROR_FORMAT 0 #endif -#ifndef DEFAULT_MEMORY_SIZE -#define DEFAULT_MEMORY_SIZE LARGE_SIZE -#endif - #ifndef CHAR_IS_UNSIGNED typedef unsigned char uchar; #else @@ -629,7 +513,9 @@ static int32 unique_task_id(void) #endif /* ------------------------------------------------------------------------- */ -/* A macro (rather than constant) definition: */ +/* subtract_pointers() measures an address difference in bytes. This is */ +/* a macro. */ +/* We also declare some memory functions for PC_QUICKC. */ /* ------------------------------------------------------------------------- */ #ifdef PC_QUICKC @@ -640,6 +526,36 @@ static int32 unique_task_id(void) #define subtract_pointers(p1,p2) (((char *) p1)-((char *) p2)) #endif + +/* ------------------------------------------------------------------------- */ +/* Definitions for time measurement. TIMEVALUE is a type; TIMEVALUE_NOW() */ +/* sets it; TIMEVALUE_DIFFERENCE() determines a difference in seconds, */ +/* as a float. */ +/* Modern platforms should support timespec_get() or clock_gettime(). To */ +/* use timespec_get(), #define USE_C11_TIME_API. To use clock_gettime(), */ +/* #define USE_POSIX_TIME_API. To use the old implementation using */ +/* time(), #define USE_OLD_TIME_API. This can only measure in integer */ +/* second counts, but it's better than waiting for gnomon. */ +/* ------------------------------------------------------------------------- */ + +#if !defined(USE_C11_TIME_API) && !defined(USE_POSIX_TIME_API) && !defined(USE_OLD_TIME_API) +#define USE_OLD_TIME_API +#endif + +#if defined(USE_OLD_TIME_API) + #define TIMEVALUE time_t + #define TIMEVALUE_NOW(t) (*t) = time(0) + #define TIMEVALUE_DIFFERENCE(begt, endt) (float)(*(endt) - *(begt)) +#elif defined(USE_C11_TIME_API) + #define TIMEVALUE struct timespec + #define TIMEVALUE_NOW(t) timespec_get((t), TIME_UTC) + #define TIMEVALUE_DIFFERENCE(begt, endt) ((float)((endt)->tv_sec - (begt)->tv_sec) + (float)((endt)->tv_nsec - (begt)->tv_nsec) / 1000000000.0F) +#elif defined(USE_POSIX_TIME_API) + #define TIMEVALUE struct timespec + #define TIMEVALUE_NOW(t) clock_gettime(CLOCK_REALTIME, (t)) + #define TIMEVALUE_DIFFERENCE(begt, endt) ((float)((endt)->tv_sec - (begt)->tv_sec) + (float)((endt)->tv_nsec - (begt)->tv_nsec) / 1000000000.0F) +#endif + /* ------------------------------------------------------------------------- */ /* SEEK_SET is a constant which should be defined in the ANSI header files */ /* but which is not present in some implementations: it's used as a */ @@ -726,42 +642,102 @@ static int32 unique_task_id(void) /* Structure definitions (there are a few others local to files) */ /* ------------------------------------------------------------------------- */ +/* A memory list is a sequential array of items. The list grows as + necessary, but it is *not* sparse. + This can optionally maintain an external pointer (of any type) which + also refers to the allocated array. The external pointer will always + have the same value as data. + (Note: the external pointer must itself have a stable location, because + we keep a pointer *to* it. It cannot live in another memory list or + realloced array. Most of our memory lists refer to global or static + variables, so that's fine.) +*/ +typedef struct memory_list_s +{ + char *whatfor; /* must be a static string */ + void *data; /* allocated array of count*itemsize bytes */ + void **extpointer; /* pointer to keep in sync */ + size_t itemsize; /* item size in bytes */ + size_t count; /* number of items allocated */ +} memory_list; + +typedef struct identstruct_s +{ + char text[MAX_IDENTIFIER_LENGTH+1]; +} identstruct; + typedef struct assembly_operand_t -{ int type; +{ int type; /* ?_OT value */ int32 value; - int symtype; /* 6.30 */ - int symflags; /* 6.30 */ - int marker; + int symindex; /* index in symbols array, if derived from a symbol */ + int marker; /* ?_MV value */ } assembly_operand; -#define INITAOTV(aop, typ, val) ((aop)->type=(typ), (aop)->value=(val), (aop)->marker=0, (aop)->symtype=0, (aop)->symflags=0) +#define INITAOTV(aop, typ, val) ((aop)->type=(typ), (aop)->value=(val), (aop)->marker=0, (aop)->symindex=-1) #define INITAOT(aop, typ) INITAOTV(aop, typ, 0) #define INITAO(aop) INITAOTV(aop, 0, 0) -#define MAX_LINES_PER_VERB 32 +typedef struct variableinfo_s { + int32 token; /* Symbol table index for variable name */ + int usage; /* TRUE if referred to */ +} variableinfo; + typedef struct verbt { int lines; - int l[MAX_LINES_PER_VERB]; + int *l; /* alloced array */ + int size; /* allocated size of l */ } verbt; +typedef struct actioninfo_s { + int32 symbol; /* The symbol table index of the action name */ + int32 byte_offset; /* The (byte) offset in the Z-machine code area of + the ...Sub routine */ +} actioninfo; + +/* Information about an object class. */ +typedef struct classinfo_s { + /* The number of the prototype-object for this class */ + int object_number; + /* The offset of properties block for this class (always an offset inside the properties table) */ + int32 begins_at; + /* Class name symbol number */ + int32 symbol; +} classinfo; + +/* Common property information. */ +typedef struct commonpropinfo_s { + int32 default_value; /* Common property default value */ + int is_long; /* "Long" means "never write a 1-byte value to + this property", and is an obsolete feature: + since Inform 5 all properties have been "long" */ + int is_additive; /* "Additive" means that values accumulate rather + than erase each other during class inheritance */ +} commonpropinfo; + +/* Property entry record (Z). */ typedef struct prop { uchar l, num; assembly_operand ao[32]; } prop; +/* Properties and attributes of the object currently being constructed (Z). */ /* Only one of this object. */ typedef struct fpropt { uchar atts[6]; int l; prop pp[64]; + int32 symbol; /* name symbol or 0 */ } fpropt; +/* Constructed object (Z). */ typedef struct objecttz { uchar atts[6]; int parent, next, child; int propsize; + int32 symbol; /* name symbol or 0 */ } objecttz; +/* Property entry record (G). */ typedef struct propg { int num; int continuation; @@ -770,24 +746,38 @@ typedef struct propg { int32 datalen; } propg; +/* Properties and attributes of the object currently being constructed (G). */ /* Only one of this object. */ typedef struct fproptg { uchar atts[MAX_NUM_ATTR_BYTES]; int numprops; - propg *props; + propg *props; /* allocated to numprops */ + memory_list props_memlist; int propdatasize; - assembly_operand *propdata; + assembly_operand *propdata; /* allocated to propdatasize */ + memory_list propdata_memlist; int32 finalpropaddr; + /* It's safe to use memory_lists in this object because there's just + one and it's static. */ + int32 symbol; /* name symbol or 0 */ } fproptg; +/* Constructed object (G). */ typedef struct objecttg { /* attributes are stored in a separate array */ int32 shortname; int32 parent, next, child; int32 propaddr; int32 propsize; + int32 symbol; /* name symbol or 0 */ } objecttg; +typedef struct abbreviation_s { + int value; + int quality; + int freq; +} abbreviation; + typedef struct maybe_file_position_S { int valid; fpos_t position; @@ -830,23 +820,69 @@ typedef struct debug_location_beginning_s int32 orig_beg_char_number; } debug_location_beginning; +#define MAX_KEYWORD_GROUP_SIZE (119) + typedef struct keyword_group_s -{ char *keywords[120]; +{ char *keywords[MAX_KEYWORD_GROUP_SIZE+1]; /* empty-string-terminated */ int change_token_type; int enabled; int case_sensitive; } keyword_group; -typedef struct token_data_s -{ char *text; - int32 value; /* ###-long */ - int type; - int symtype; /* 6.30 */ - int symflags; /* 6.30 */ - int marker; +typedef struct lexeme_data_s { + char *text; /* points at lextexts array */ + int32 value; + int type; /* a *_TT value */ debug_location location; + int lextext; /* index of text string in lextexts */ + int context; /* lexical context used to interpret this token */ +} lexeme_data; + +typedef struct token_data_s { + char *text; + int32 value; + int type; /* a *_TT value */ + int symindex; + int symtype; + int symflags; + int marker; } token_data; +typedef struct symbolinfo_s { + char *name; /* Points into a symbol_name_space_chunk */ + int32 value; + int marker; /* ?_MV value */ + brief_location line; + unsigned int flags; /* ?_SFLAGS bitmask */ + uchar type; /* ?_T value */ + int next_entry; /* Linked list for symbol hash table */ +} symbolinfo; + +typedef struct symboldebuginfo_s { + maybe_file_position backpatch_pos; + maybe_file_position replacement_backpatch_pos; +} symboldebuginfo; + +typedef struct arrayinfo_s { + int32 symbol; /* index in symbols[] */ + int size; /* length of array */ + int type; /* BYTE_ARRAY, WORD_ARRAY, etc */ + int loc; /* true for static, false for dynamic (regular) arrays */ +} arrayinfo; + +typedef struct labelinfo_s { + int32 offset; /* Offset (zmachine_pc) value */ + int32 symbol; /* Symbol numbers if defined in source */ + int next; /* For linked list */ + int prev; /* For linked list */ +} labelinfo; + +typedef struct sequencepointinfo_s { + int label; /* Label number */ + debug_location location; /* Source code reference (used for making + debugging file) */ +} sequencepointinfo; + typedef struct FileId_s /* Source code file identifier: */ { char *filename; /* The filename (after translation) */ FILE *handle; /* Handle of file (when open), or @@ -855,6 +891,8 @@ typedef struct FileId_s /* Source code file identifier: */ parsing? If not, this is an origsource filename (and handle is NULL). */ + int initial_buffering; /* Are we still in the initial + begin_buffering_file() call? */ } FileId; typedef struct ErrorPosition_s @@ -868,17 +906,6 @@ typedef struct ErrorPosition_s int32 orig_char; } ErrorPosition; -/* A memory block can hold at most ALLOC_CHUNK_SIZE * 72: */ - -extern int ALLOC_CHUNK_SIZE; - -typedef struct memory_block_s -{ int chunks; - int extent_of_last; - uchar *chunk[72]; - int write_pos; -} memory_block; - /* This serves for both Z-code and Glulx instructions. Glulx doesn't use the text, store_variable_number, branch_label_number, or branch_flag fields. */ @@ -887,7 +914,7 @@ typedef struct assembly_instruction_t int store_variable_number; int32 branch_label_number; int branch_flag; - char *text; + char *text; /* if set, generally points to token_text */ int operand_count; assembly_operand operand[8]; } assembly_instruction; @@ -1318,6 +1345,11 @@ typedef struct operator_s #define FAKE_ACTION_T 11 #define STATIC_ARRAY_T 12 +/* These types never occur in the symbol table; they exist only as + type-checking requirements. */ +#define STRING_REQ_T 13 +#define DICT_WORD_REQ_T 14 + /* ------------------------------------------------------------------------- */ /* Statusline_flag values */ /* ------------------------------------------------------------------------- */ @@ -1493,6 +1525,7 @@ typedef struct operator_s #define WARNING_DK 34 #define TERMINATING_DK 35 #define STATIC_DK 36 +#define INDIVIDUAL_DK 37 /* Index numbers into the keyword group "trace_keywords" (see "lexer.c") */ @@ -1583,8 +1616,8 @@ typedef struct operator_s #define oddeven_packing_SC 58 -#define grammar_table_SC 59 /* Glulx-only */ -#define dictionary_table_SC 60 /* Glulx-only */ +#define grammar_table_SC 59 +#define dictionary_table_SC 60 #define dynam_string_table_SC 61 /* Glulx-only */ @@ -1962,6 +1995,19 @@ typedef struct operator_s #define STRCTX_SYMBOL 9 /* prop/attr/etc names */ #define STRCTX_INFIX 10 /* text printed in asterisk traces */ +/* ------------------------------------------------------------------------- */ +/* Bit-flags applying to the execution_never_reaches_here variable. */ +/* Note that if any flags are set, UNREACHABLE is set, so we can easily */ +/* test "if (execution_never_reaches_here)..." */ +/* ------------------------------------------------------------------------- */ + +#define EXECSTATE_REACHABLE 0 /* compile normally */ +#define EXECSTATE_UNREACHABLE 1 /* execution cannot reach this line */ +#define EXECSTATE_ENTIRE 2 /* execution cannot reach this entire + statement or code block */ +#define EXECSTATE_NOWARN 4 /* do not print a warning about unreachable + code */ + /* ========================================================================= */ /* Initialisation extern definitions */ /* */ @@ -2088,14 +2134,17 @@ extern void verbs_free_arrays(void); /* Extern definitions for "arrays" */ /* ------------------------------------------------------------------------- */ +#define MAX_ZCODE_GLOBAL_VARS (240) + extern int no_globals, no_arrays; extern int dynamic_array_area_size; -extern int *dynamic_array_area; +extern uchar *dynamic_array_area; +extern memory_list dynamic_array_area_memlist; extern int static_array_area_size; -extern int *static_array_area; +extern uchar *static_array_area; +extern memory_list static_array_area_memlist; extern int32 *global_initial_value; -extern int32 *array_symbols; -extern int *array_sizes, *array_types, *array_locs; +extern arrayinfo *arrays; extern void make_global(int array_flag, int name_only); extern void set_variable_value(int i, int32 v); @@ -2109,29 +2158,32 @@ extern void finish_array(int32 i, int is_static); /* Extern definitions for "asm" */ /* ------------------------------------------------------------------------- */ -extern memory_block zcode_area; +extern uchar *zcode_area; +extern memory_list zcode_area_memlist; extern int32 zmachine_pc; extern int32 no_instructions; extern int sequence_point_follows; extern int uses_unicode_features, uses_memheap_features, - uses_acceleration_features, uses_float_features; + uses_acceleration_features, uses_float_features, + uses_extundo_features; extern debug_location statement_debug_location; extern int execution_never_reaches_here; -extern int *variable_usage; +extern variableinfo *variables; +extern memory_list variables_memlist; extern int next_label, no_sequence_points; -extern int32 *variable_tokens; extern assembly_instruction AI; extern int32 *named_routine_symbols; -extern void print_operand(assembly_operand o); +extern void print_operand(const assembly_operand *o, int annotate); extern char *variable_name(int32 i); extern void set_constant_ot(assembly_operand *AO); extern int is_constant_ot(int otval); extern int is_variable_ot(int otval); -extern void assemblez_instruction(assembly_instruction *a); -extern void assembleg_instruction(assembly_instruction *a); +extern void assemblez_instruction(const assembly_instruction *a); +extern void assembleg_instruction(const assembly_instruction *a); extern void assemble_label_no(int n); +extern int assemble_forward_label_no(int n); extern void assemble_jump(int n); extern void define_symbol_label(int symbol); extern int32 assemble_routine_header(int no_locals, int debug_flag, @@ -2229,8 +2281,12 @@ extern void parse_assembly(void); /* Extern definitions for "bpatch" */ /* ------------------------------------------------------------------------- */ -extern memory_block zcode_backpatch_table, staticarray_backpatch_table, - zmachine_backpatch_table; +extern uchar *staticarray_backpatch_table; +extern memory_list staticarray_backpatch_table_memlist; +extern uchar *zmachine_backpatch_table; +extern memory_list zmachine_backpatch_table_memlist; +extern uchar *zcode_backpatch_table; +extern memory_list zcode_backpatch_table_memlist; extern int32 zcode_backpatch_size, staticarray_backpatch_size, zmachine_backpatch_size; extern int backpatch_marker, backpatch_error_flag; @@ -2285,6 +2341,7 @@ extern int parse_given_directive(int internal_flag); /* Extern definitions for "errors" */ /* ------------------------------------------------------------------------- */ +#define FORERRORS_SIZE (512) extern char *forerrors_buff; extern int forerrors_pointer; extern int no_errors, no_warnings, no_suppressed_warnings, @@ -2295,7 +2352,8 @@ extern ErrorPosition ErrorReport; extern void fatalerror(char *s) NORETURN; extern void fatalerror_named(char *s1, char *s2) NORETURN; extern void memory_out_error(int32 size, int32 howmany, char *name) NORETURN; -extern void memoryerror(char *s, int32 size) NORETURN; +extern void error_max_dynamic_strings(int index); +extern void error_max_abbreviations(int index); extern void error(char *s); extern void error_named(char *s1, char *s2); extern void error_numbered(char *s1, int val); @@ -2308,6 +2366,7 @@ extern void no_such_label(char *lname); extern void warning(char *s); extern void warning_numbered(char *s1, int val); extern void warning_named(char *s1, char *s2); +extern void symtype_warning(char *context, char *name, char *type, char *wanttype); extern void dbnu_warning(char *type, char *name, brief_location report_line); extern void uncalled_routine_warning(char *type, char *name, brief_location report_line); extern void obsolete_warning(char *s1); @@ -2351,6 +2410,7 @@ extern int z_system_constant_list[]; extern int glulx_system_constant_list[]; extern int32 value_of_system_constant(int t); +extern char *name_of_system_constant(int t); extern void clear_expression_space(void); extern void show_tree(assembly_operand AO, int annotate); extern assembly_operand parse_expression(int context); @@ -2365,14 +2425,8 @@ extern int current_input_file; extern int total_input_files; extern FileId *InputFiles; -extern FILE *Temp1_fp, *Temp2_fp, *Temp3_fp; -extern char Temp1_Name[], Temp2_Name[], Temp3_Name[]; extern int32 total_chars_read; -extern void open_temporary_files(void); -extern void check_temp_files(void); -extern void remove_temp_files(void); - extern void open_transcript_file(char *what_of); extern void write_to_transcript_file(char *text, int linetype); extern void close_transcript_file(void); @@ -2432,20 +2486,23 @@ extern int WORDSIZE, INDIV_PROP_START, OBJECT_BYTE_LENGTH, DICT_ENTRY_BYTE_LENGTH, DICT_ENTRY_FLAG_POS; extern int32 MAXINTWORD; -extern int asm_trace_level, line_trace_level, expr_trace_level, +extern int asm_trace_level, expr_trace_level, linker_trace_level, tokens_trace_level; extern int - bothpasses_switch, concise_switch, - economy_switch, frequencies_switch, - ignore_switches_switch, listobjects_switch, debugfile_switch, - listing_switch, memout_switch, printprops_switch, - offsets_switch, percentages_switch, obsolete_switch, + concise_switch, + economy_switch, frequencies_setting, + ignore_switches_switch, debugfile_switch, + files_trace_setting, memout_switch, printprops_switch, + printactions_switch, + obsolete_switch, optabbrevs_trace_setting, transcript_switch, statistics_switch, optimise_switch, version_set_switch, nowarnings_switch, hash_switch, - memory_map_switch, module_switch, temporary_files_switch, + memory_map_setting, module_switch, define_DEBUG_switch, define_USE_MODULES_switch, define_INFIX_switch, - runtime_error_checking_switch; + runtime_error_checking_switch, + list_verbs_setting, list_dict_setting, list_objects_setting, + list_symbols_setting; extern int oddeven_packing_switch; @@ -2453,6 +2510,8 @@ extern int glulx_mode, compression_switch; extern int32 requested_glulx_version; extern int error_format, store_the_text, asm_trace_setting, + expr_trace_setting, linker_trace_setting, tokens_trace_setting, + bpatch_trace_setting, symdef_trace_setting, double_space_setting, trace_fns_setting, character_set_setting, character_set_unicode; @@ -2470,7 +2529,6 @@ extern int translate_in_filename(int last_value, char *new_name, char *old_name, extern void translate_out_filename(char *new_name, char *old_name); extern int translate_link_filename(int last_value, char *new_name, char *old_name); -extern void translate_temp_filename(int i); #ifdef ARCHIMEDES extern char *riscos_file_type(void); @@ -2491,7 +2549,7 @@ extern int total_source_line_count; extern int dont_enter_into_symbol_table; extern int return_sp_as_variable; extern int next_token_begins_syntax_line; -extern char **local_variable_texts; +extern identstruct *local_variable_names; extern int32 token_value; extern int token_type; @@ -2503,7 +2561,9 @@ extern debug_location_beginning get_token_location_beginning(void); extern void discard_token_location(debug_location_beginning beginning); extern debug_locations get_token_location_end(debug_location_beginning beginning); -extern void describe_token(token_data t); +extern void describe_token_triple(const char *text, int32 value, int type); +/* The describe_token() macro works on both token_data and lexeme_data structs. */ +#define describe_token(t) describe_token_triple((t)->text, (t)->value, (t)->type) extern void construct_local_variable_tables(void); extern void declare_systemfile(void); @@ -2519,6 +2579,7 @@ extern brief_location blank_brief_location; extern void put_token_back(void); extern void get_next_token(void); +extern void release_token_texts(void); extern void restart_lexer(char *lexical_source, char *name); extern keyword_group directives, statements, segment_markers, @@ -2530,7 +2591,7 @@ extern keyword_group directives, statements, segment_markers, /* Extern definitions for "linker" */ /* ------------------------------------------------------------------------- */ -extern memory_block link_data_area; +extern uchar *link_data_area; extern int32 link_data_size; extern char current_module_filename[]; @@ -2547,27 +2608,21 @@ extern void link_module(char *filename); /* Extern definitions for "memory" */ /* ------------------------------------------------------------------------- */ -extern int32 malloced_bytes; +extern size_t malloced_bytes; -extern int MAX_QTEXT_SIZE, MAX_SYMBOLS, HASH_TAB_SIZE, MAX_DICT_ENTRIES, - MAX_OBJECTS, MAX_ACTIONS, MAX_ADJECTIVES, MAX_ABBREVS, - MAX_STATIC_DATA, MAX_PROP_TABLE_SIZE, SYMBOLS_CHUNK_SIZE, - MAX_EXPRESSION_NODES, MAX_LABELS, MAX_LINESPACE, - MAX_LOW_STRINGS, MAX_CLASSES, MAX_VERBS, - MAX_VERBSPACE, MAX_ARRAYS, MAX_INCLUSION_DEPTH, - MAX_SOURCE_FILES, MAX_DYNAMIC_STRINGS; +extern int HASH_TAB_SIZE, + MAX_ABBREVS, + MAX_DYNAMIC_STRINGS; -extern int32 MAX_STATIC_STRINGS, MAX_ZCODE_SIZE, MAX_LINK_DATA_SIZE, - MAX_TRANSCRIPT_SIZE, MAX_INDIV_PROP_TABLE_SIZE, - MAX_NUM_STATIC_STRINGS, MAX_UNICODE_CHARS, - MAX_STACK_SIZE, MEMORY_MAP_EXTENSION; +extern int32 MAX_STACK_SIZE, MEMORY_MAP_EXTENSION; -extern int32 MAX_OBJ_PROP_COUNT, MAX_OBJ_PROP_TABLE_SIZE; -extern int MAX_LOCAL_VARIABLES, MAX_GLOBAL_VARIABLES; +extern int MAX_LOCAL_VARIABLES; extern int DICT_WORD_SIZE, DICT_CHAR_SIZE, DICT_WORD_BYTES; extern int ZCODE_HEADER_EXT_WORDS, ZCODE_HEADER_FLAGS_3; +extern int ZCODE_LESS_DICT_DATA; extern int NUM_ATTR_BYTES, GLULX_OBJECT_EXT_BYTES; extern int WARN_UNUSED_ROUTINES, OMIT_UNUSED_ROUTINES; +extern int STRIP_UNREACHABLE_LABELS; extern int TRANSCRIPT_FORMAT; /* These macros define offsets that depend on the value of NUM_ATTR_BYTES. @@ -2580,24 +2635,22 @@ extern int TRANSCRIPT_FORMAT; #define GOBJFIELD_SIBLING() (5+((NUM_ATTR_BYTES)/4)) #define GOBJFIELD_CHILD() (6+((NUM_ATTR_BYTES)/4)) -extern void *my_malloc(int32 size, char *whatfor); -extern void my_realloc(void *pointer, int32 oldsize, int32 size, +extern void *my_malloc(size_t size, char *whatfor); +extern void my_realloc(void *pointer, size_t oldsize, size_t size, char *whatfor); -extern void *my_calloc(int32 size, int32 howmany, char *whatfor); -extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany, - int32 howmany, char *whatfor); +extern void *my_calloc(size_t size, size_t howmany, char *whatfor); +extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, + size_t howmany, char *whatfor); extern void my_free(void *pointer, char *whatitwas); -extern void set_memory_sizes(int size_flag); +extern void set_memory_sizes(void); extern void adjust_memory_sizes(void); extern void memory_command(char *command); extern void print_memory_usage(void); -extern void initialise_memory_block(memory_block *MB); -extern void deallocate_memory_block(memory_block *MB); -extern int read_byte_from_memory_block(memory_block *MB, int32 index); -extern void write_byte_to_memory_block(memory_block *MB, - int32 index, int value); +extern void initialise_memory_list(memory_list *ML, size_t itemsize, size_t initalloc, void **extpointer, char *whatfor); +extern void deallocate_memory_list(memory_list *ML); +extern void ensure_memory_list_available(memory_list *ML, size_t count); /* ------------------------------------------------------------------------- */ /* Extern definitions for "objects" */ @@ -2607,17 +2660,18 @@ extern int no_attributes, no_properties; extern int no_individual_properties; extern int individuals_length; extern uchar *individuals_table; +extern memory_list individuals_table_memlist; extern int no_classes, no_objects; extern objecttz *objectsz; +extern memory_list objectsz_memlist; extern objecttg *objectsg; extern uchar *objectatts; -extern int *class_object_numbers; -extern int32 *class_begins_at; +extern classinfo *class_info; +extern memory_list class_info_memlist; -extern int32 *prop_default_value; -extern int *prop_is_long; -extern int *prop_is_additive; -extern char *properties_table; +extern commonpropinfo *commonprops; +extern uchar *properties_table; +extern memory_list properties_table_memlist; extern int properties_table_size; extern void make_attribute(void); @@ -2636,18 +2690,8 @@ extern void write_the_identifier_names(void); extern int no_named_constants; extern int no_symbols; -extern int32 **symbs; -extern int32 *svals; -extern int *smarks; -extern brief_location *slines; -extern int *sflags; -#ifdef VAX - extern char *stypes; -#else - extern signed char *stypes; -#endif -extern maybe_file_position *symbol_debug_backpatch_positions; -extern maybe_file_position *replacement_debug_backpatch_positions; +extern symbolinfo *symbols; +extern symboldebuginfo *symbol_debug_info; extern int32 *individual_name_strings; extern int32 *attribute_name_strings; extern int32 *action_name_strings; @@ -2660,13 +2704,17 @@ extern uint32 df_total_size_after_stripping; extern char *typename(int type); extern int hash_code_from_string(char *p); extern int strcmpcis(char *p, char *q); +extern int get_symbol_index(char *p); extern int symbol_index(char *lexeme_text, int hashcode); extern void end_symbol_scope(int k); extern void describe_symbol(int k); extern void list_symbols(int level); extern void assign_marked_symbol(int index, int marker, int32 value, int type); extern void assign_symbol(int index, int32 value, int type); +extern void check_warn_symbol_type(const assembly_operand *AO, int wanttype, int wanttype2, char *label); +extern void check_warn_symbol_has_metaclass(const assembly_operand *AO, char *context); extern void issue_unused_warnings(void); +extern void issue_debug_symbol_warnings(void); extern void add_config_symbol_definition(char *symbol, int32 value); extern void add_symbol_replacement_mapping(int original, int renamed); extern int find_symbol_replacement(int *value); @@ -2738,24 +2786,26 @@ extern void write_serial_number(char *buffer); /* Extern definitions for "text" */ /* ------------------------------------------------------------------------- */ -extern uchar *low_strings, *low_strings_top; -extern char *all_text, *all_text_top; +extern uchar *translated_text; + +extern uchar *low_strings; +extern int32 low_strings_top; extern int no_abbreviations; extern int abbrevs_lookup_table_made, is_abbreviation; extern uchar *abbreviations_at; -extern int *abbrev_values; -extern int *abbrev_quality; -extern int *abbrev_freqs; +extern abbreviation *abbreviations; extern int32 total_chars_trans, total_bytes_trans, zchars_trans_in_last_string; extern int put_strings_in_low_memory; extern int dict_entries; -extern uchar *dictionary, *dictionary_top; +extern uchar *dictionary; +extern int32 dictionary_top; extern int *final_dict_order; -extern memory_block static_strings_area; +extern uchar *static_strings_area; +extern memory_list static_strings_area_memlist; extern int32 static_strings_extent; /* And now, a great many declarations for dealing with Glulx string @@ -2767,7 +2817,7 @@ extern int no_unicode_chars; typedef struct unicode_usage_s unicode_usage_t; struct unicode_usage_s { int32 ch; - unicode_usage_t *next; + int next; /* index in unicode_usage_entries of next */ }; extern unicode_usage_t *unicode_usage_entries; @@ -2785,7 +2835,7 @@ typedef struct huffentity_struct { int type; union { int branch[2]; - unsigned char ch; + uchar ch; int val; } u; int depth; @@ -2806,12 +2856,14 @@ extern void compress_game_text(void); /* end of the Glulx string compression stuff */ extern void ao_free_arrays(void); +extern void extract_all_text(void); extern int32 compile_string(char *b, int strctx); -extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx); +extern int32 translate_text(int32 p_limit, char *s_text, int strctx); extern void optimise_abbreviations(void); extern void make_abbreviation(char *text); -extern void show_dictionary(void); +extern void show_dictionary(int level); extern void word_to_ascii(uchar *p, char *result); +extern void print_dict_word(int node); extern void write_dictionary_to_transcript(void); extern void sort_dictionary(void); extern void dictionary_prepare(char *dword, uchar *optresult); @@ -2829,6 +2881,7 @@ extern int32 veneer_routine_address[]; extern void compile_initial_routine(void); extern assembly_operand veneer_routine(int code); +extern char *veneer_routine_name(int code); extern void compile_veneer(void); /* ------------------------------------------------------------------------- */ @@ -2842,8 +2895,9 @@ extern int32 grammar_version_symbol; extern verbt *Inform_verbs; extern uchar *grammar_lines; extern int32 grammar_lines_top; -extern int32 *action_byte_offset, - *grammar_token_routine, +extern actioninfo *actions; +extern memory_list actions_memlist; +extern int32 *grammar_token_routine, *adjectives; extern void find_the_actions(void); diff --git a/src/inform.c b/src/inform.c index a72d81e..5c02b6b 100644 --- a/src/inform.c +++ b/src/inform.c @@ -2,8 +2,8 @@ /* "inform" : The top level of Inform: switches, pathnames, filenaming */ /* conventions, ICL (Inform Command Line) files, main */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -16,7 +16,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -123,6 +123,8 @@ static void select_target(int targ) WORDSIZE = 2; MAXINTWORD = 0x7FFF; + MAX_LOCAL_VARIABLES = 16; /* including "sp" */ + if (INDIV_PROP_START != 64) { INDIV_PROP_START = 64; fatalerror("You cannot change INDIV_PROP_START in Z-code"); @@ -139,18 +141,6 @@ static void select_target(int targ) NUM_ATTR_BYTES = 6; fatalerror("You cannot change NUM_ATTR_BYTES in Z-code"); } - if (MAX_LOCAL_VARIABLES != 16) { - MAX_LOCAL_VARIABLES = 16; - fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code"); - } - if (MAX_GLOBAL_VARIABLES != 240) { - MAX_GLOBAL_VARIABLES = 240; - fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code"); - } - if (MAX_VERBS > 255) { - MAX_VERBS = 255; - fatalerror("MAX_VERBS can only go above 255 when Glulx is used"); - } } else { /* Glulx */ @@ -158,6 +148,10 @@ static void select_target(int targ) MAXINTWORD = 0x7FFFFFFF; scale_factor = 0; /* It should never even get used in Glulx */ + /* This could really be 120, since the practical limit is the size + of local_variables.keywords. But historically it's been 119. */ + MAX_LOCAL_VARIABLES = 119; /* including "sp" */ + if (INDIV_PROP_START < 256) { INDIV_PROP_START = 256; warning_numbered("INDIV_PROP_START should be at least 256 in Glulx. Setting to", INDIV_PROP_START); @@ -174,12 +168,11 @@ static void select_target(int targ) } } - if (MAX_LOCAL_VARIABLES >= 120) { - MAX_LOCAL_VARIABLES = 119; - warning("MAX_LOCAL_VARIABLES cannot exceed 119; resetting to 119"); - /* This is because the keyword table in the lexer only has 120 - entries. */ + if (MAX_LOCAL_VARIABLES > MAX_KEYWORD_GROUP_SIZE) { + compiler_error("MAX_LOCAL_VARIABLES cannot exceed MAX_KEYWORD_GROUP_SIZE"); + MAX_LOCAL_VARIABLES = MAX_KEYWORD_GROUP_SIZE; } + if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) { DICT_WORD_SIZE = MAX_DICT_WORD_SIZE; warning_numbered( @@ -195,21 +188,13 @@ static void select_target(int targ) /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */ } - if (MAX_ADJECTIVES > 255) { - MAX_ADJECTIVES = 255; - warning("MAX_ADJECTIVES cannot exceed 255; resetting to 255"); - /* Only used under Grammar__Version 1, which is obsolete. */ - } - /* Set up a few more variables that depend on the above values */ if (!targ) { /* Z-machine */ DICT_WORD_BYTES = DICT_WORD_SIZE; - /* The Z-code generator doesn't use the following variables, although - it would be a little cleaner if it did. */ OBJECT_BYTE_LENGTH = 0; - DICT_ENTRY_BYTE_LENGTH = (version_number==3)?7:9; + DICT_ENTRY_BYTE_LENGTH = ((version_number==3)?7:9) - (ZCODE_LESS_DICT_DATA?1:0); DICT_ENTRY_FLAG_POS = 0; } else { @@ -245,54 +230,45 @@ static void select_target(int targ) MAX_ABBREVS = 64; } } - else { - if (MAX_DYNAMIC_STRINGS > 100) { - MAX_DYNAMIC_STRINGS = 100; - warning("MAX_DYNAMIC_STRINGS cannot exceed 100; resetting to 100"); - /* This is because they are specified in text literals like "@00", - with two digits. */ - } - } } /* ------------------------------------------------------------------------- */ /* Tracery: output control variables */ +/* (These are initially set to foo_trace_setting, but the Trace directive */ +/* can change them on the fly) */ /* ------------------------------------------------------------------------- */ int asm_trace_level, /* trace assembly: 0 for off, 1 for assembly - only, 2 for full assembly tracing with hex dumps */ - line_trace_level, /* line tracing: 0 off, 1 on */ - expr_trace_level, /* expression tracing: 0 off, 1 full, 2 brief */ - linker_trace_level, /* set by -y: 0 to 4 levels of tracing */ - tokens_trace_level; /* lexer output tracing: 0 off, 1 on */ + only, 2 for full assembly tracing with hex dumps, + 3 for branch shortening info, 4 for verbose + branch info */ + expr_trace_level, /* expression tracing: 0 off, 1 on, 2/3 more */ + linker_trace_level, /* linker tracing: 0 to 4 levels */ + tokens_trace_level; /* lexer output tracing: 0 off, 1 on, 2/3 more */ /* ------------------------------------------------------------------------- */ /* On/off switch variables (by default all FALSE); other switch settings */ +/* (Some of these have become numerical settings now) */ /* ------------------------------------------------------------------------- */ -int bothpasses_switch, /* -b */ - concise_switch, /* -c */ +int concise_switch, /* -c */ economy_switch, /* -e */ - frequencies_switch, /* -f */ + frequencies_setting, /* $!FREQ, -f */ ignore_switches_switch, /* -i */ - listobjects_switch, /* -j */ debugfile_switch, /* -k */ - listing_switch, /* -l */ - memout_switch, /* -m */ - printprops_switch, /* -n */ - offsets_switch, /* -o */ - percentages_switch, /* -p */ + memout_switch, /* $!MEM */ + printprops_switch, /* $!PROPS */ + printactions_switch, /* $!ACTIONS */ obsolete_switch, /* -q */ transcript_switch, /* -r */ - statistics_switch, /* -s */ + statistics_switch, /* $!STATS, -s */ optimise_switch, /* -u */ version_set_switch, /* -v */ nowarnings_switch, /* -w */ hash_switch, /* -x */ - memory_map_switch, /* -z */ + memory_map_setting, /* $!MAP, -z */ oddeven_packing_switch, /* -B */ define_DEBUG_switch, /* -D */ - temporary_files_switch, /* -F */ module_switch, /* -M */ runtime_error_checking_switch, /* -S */ define_USE_MODULES_switch, /* -U */ @@ -307,53 +283,66 @@ int compression_switch; /* set by -H */ int character_set_setting, /* set by -C0 through -C9 */ character_set_unicode, /* set by -Cu */ error_format, /* set by -E */ - asm_trace_setting, /* set by -a and -t: value of - asm_trace_level to use when tracing */ + asm_trace_setting, /* $!ASM, -a: initial value of + asm_trace_level */ + bpatch_trace_setting, /* $!BPATCH */ + symdef_trace_setting, /* $!SYMDEF */ + expr_trace_setting, /* $!EXPR: initial value of + expr_trace_level */ + tokens_trace_setting, /* $!TOKENS: initial value of + tokens_trace_level */ + optabbrevs_trace_setting, /* $!FINDABBREVS */ double_space_setting, /* set by -d: 0, 1 or 2 */ - trace_fns_setting, /* set by -g: 0, 1 or 2 */ - linker_trace_setting, /* set by -y: ditto for linker_... */ + trace_fns_setting, /* $!RUNTIME, -g: 0, 1, 2, or 3 */ + files_trace_setting, /* $!FILES */ + linker_trace_setting, /* $!LINKER: initial value of + linker_trace_level */ + list_verbs_setting, /* $!VERBS */ + list_dict_setting, /* $!DICT */ + list_objects_setting, /* $!OBJECTS */ + list_symbols_setting, /* $!SYMBOLS */ store_the_text; /* when set, record game text to a chunk - of memory (used by both -r & -k) */ + of memory (used by -u) */ static int r_e_c_s_set; /* has -S been explicitly set? */ int glulx_mode; /* -G */ static void reset_switch_settings(void) -{ asm_trace_setting=0; - linker_trace_level=0; - tokens_trace_level=0; +{ asm_trace_setting = 0; + linker_trace_setting = 0; + tokens_trace_setting = 0; + expr_trace_setting = 0; + bpatch_trace_setting = 0; + symdef_trace_setting = 0; + list_verbs_setting = 0; + list_dict_setting = 0; + list_objects_setting = 0; + list_symbols_setting = 0; store_the_text = FALSE; - bothpasses_switch = FALSE; concise_switch = FALSE; double_space_setting = 0; economy_switch = FALSE; - frequencies_switch = FALSE; + files_trace_setting = 0; + frequencies_setting = 0; trace_fns_setting = 0; ignore_switches_switch = FALSE; - listobjects_switch = FALSE; debugfile_switch = FALSE; - listing_switch = FALSE; - memout_switch = FALSE; - printprops_switch = FALSE; - offsets_switch = FALSE; - percentages_switch = FALSE; + memout_switch = 0; + printprops_switch = 0; + printactions_switch = 0; obsolete_switch = FALSE; transcript_switch = FALSE; statistics_switch = FALSE; optimise_switch = FALSE; + optabbrevs_trace_setting = 0; version_set_switch = FALSE; nowarnings_switch = FALSE; hash_switch = FALSE; - memory_map_switch = FALSE; + memory_map_setting = 0; oddeven_packing_switch = FALSE; define_DEBUG_switch = FALSE; -#ifdef USE_TEMPORARY_FILES - temporary_files_switch = TRUE; -#else - temporary_files_switch = FALSE; -#endif define_USE_MODULES_switch = FALSE; module_switch = FALSE; #ifdef ARC_THROWBACK @@ -373,6 +362,12 @@ static void reset_switch_settings(void) compression_switch = TRUE; glulx_mode = FALSE; requested_glulx_version = 0; + + /* These aren't switches, but for clarity we reset them too. */ + asm_trace_level = 0; + expr_trace_level = 0; + linker_trace_level = 0; + tokens_trace_level = 0; } /* ------------------------------------------------------------------------- */ @@ -428,10 +423,10 @@ static void begin_pass(void) files_begin_pass(); endofpass_flag = FALSE; - line_trace_level = 0; expr_trace_level = 0; + expr_trace_level = expr_trace_setting; asm_trace_level = asm_trace_setting; + tokens_trace_level = tokens_trace_setting; linker_trace_level = linker_trace_setting; - if (listing_switch) line_trace_level=1; lexer_begin_pass(); linker_begin_pass(); @@ -527,7 +522,6 @@ static char Source_Path[PATHLEN]; static char Include_Path[PATHLEN]; static char Code_Path[PATHLEN]; static char Module_Path[PATHLEN]; -static char Temporary_Path[PATHLEN]; static char current_source_path[PATHLEN]; char Debugging_Name[PATHLEN]; char Transcript_Name[PATHLEN]; @@ -634,7 +628,6 @@ static void set_default_paths(void) set_path_value(Code_Path, Code_Directory); set_path_value(Module_Path, Module_Directory); set_path_value(ICL_Path, ICL_Directory); - set_path_value(Temporary_Path, Temporary_Directory); set_path_value(Debugging_Name, Debugging_File); set_path_value(Transcript_Name, Transcript_File); set_path_value(Language_Name, Default_Language); @@ -676,7 +669,6 @@ static void set_path_command(char *command) if (strcmp(pathname, "code_path")==0) path_to_set=Code_Path; if (strcmp(pathname, "module_path")==0) path_to_set=Module_Path; if (strcmp(pathname, "icl_path")==0) path_to_set=ICL_Path; - if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path; if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name; if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name; if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name; @@ -962,10 +954,8 @@ Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\ name_or_unset(Code_Path)); printf( -" Temporary file (out) temporary_path %s\n\ - ICL command file (in) icl_path %s\n\ +" ICL command file (in) icl_path %s\n\ Module (in & out) module_path %s\n\n", - name_or_unset(Temporary_Path), name_or_unset(ICL_Path), name_or_unset(Module_Path)); printf( @@ -997,7 +987,6 @@ Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\ Include files: %s\n\ Story files: %s (Version 3), %s (v4), %s (v5, the default),\n\ %s (v6), %s (v7), %s (v8), %s (Glulx)\n\ - Temporary files: .tmp\n\ Modules: %s\n\n", Source_Extension, Include_Extension, Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension, @@ -1072,33 +1061,6 @@ Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\ module_switch = save_mm; } -/* ------------------------------------------------------------------------- */ -/* Naming temporary files */ -/* (Arguably temporary files should be made using "tmpfile" in */ -/* the ANSI C library, but many supposed ANSI libraries lack it.) */ -/* ------------------------------------------------------------------------- */ - -extern void translate_temp_filename(int i) -{ char *p = NULL; - switch(i) - { case 1: p=Temp1_Name; break; - case 2: p=Temp2_Name; break; - case 3: p=Temp3_Name; break; - default: return; - } - if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) { - printf ("Temporary_Path is too long.\n"); - exit(1); - } - sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i); -#ifdef INCLUDE_TASK_ID - sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id()); -#endif -#ifdef FILE_EXTENSIONS - sprintf(p+strlen(p), ".tmp"); -#endif -} - #ifdef ARCHIMEDES static char riscos_ft_buffer[4]; @@ -1137,13 +1099,11 @@ static void run_pass(void) lexer_endpass(); if (module_switch) linker_endpass(); + issue_debug_symbol_warnings(); + close_all_source(); if (hash_switch && hash_printed_since_newline) printf("\n"); - if (temporary_files_switch) - { if (module_switch) flush_link_data(); - check_temp_files(); - } sort_dictionary(); if (track_unused_routines) locate_dead_functions(); @@ -1152,9 +1112,8 @@ static void run_pass(void) int output_has_occurred; -static void rennab(int32 time_taken) +static void rennab(float time_taken) { /* rennab = reverse of banner */ - int t = no_warnings + no_suppressed_warnings; if (memout_switch) print_memory_usage(); @@ -1181,7 +1140,16 @@ static void rennab(int32 time_taken) if (no_compiler_errors > 0) print_sorry_message(); if (statistics_switch) - printf("Completed in %ld seconds\n", (long int) time_taken); + { + /* Print the duration to a sensible number of decimal places. + (We aim for three significant figures.) */ + if (time_taken >= 10.0) + printf("Completed in %.1f seconds\n", time_taken); + else if (time_taken >= 1.0) + printf("Completed in %.2f seconds\n", time_taken); + else + printf("Completed in %.3f seconds\n", time_taken); + } } /* ------------------------------------------------------------------------- */ @@ -1191,7 +1159,9 @@ static void rennab(int32 time_taken) static int execute_icl_header(char *file1); static int compile(int number_of_files_specified, char *file1, char *file2) -{ int32 time_start; +{ + TIMEVALUE time_start, time_end; + float duration; if (execute_icl_header(file1)) return 1; @@ -1221,7 +1191,9 @@ compiling modules: disabling -S switch\n"); runtime_error_checking_switch = FALSE; } - time_start=time(0); no_compilations++; + TIMEVALUE_NOW(&time_start); + + no_compilations++; strcpy(Source_Name, file1); convert_filename_flag = TRUE; strcpy(Code_Name, file1); @@ -1251,15 +1223,22 @@ compiling modules: disabling -S switch\n"); { end_debug_file(); } - if (temporary_files_switch && (no_errors>0)) remove_temp_files(); + if (optimise_switch) { + /* Pull out all_text so that it will not be freed. */ + extract_all_text(); + } free_arrays(); - rennab((int32) (time(0)-time_start)); - - if (optimise_switch) optimise_abbreviations(); + TIMEVALUE_NOW(&time_end); + duration = TIMEVALUE_DIFFERENCE(&time_start, &time_end); + + rennab(duration); - if (store_the_text) my_free(&all_text,"transcription text"); + if (optimise_switch) { + optimise_abbreviations(); + ao_free_arrays(); + } return (no_errors==0)?0:1; } @@ -1273,7 +1252,7 @@ static void cli_print_help(int help_level) printf( "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\ story files, as well as \"Glulx\" story files:\n\ -Copyright (c) Graham Nelson 1993 - 2021.\n\n"); +Copyright (c) Graham Nelson 1993 - 2022.\n\n"); /* For people typing just "inform", a summary only: */ @@ -1303,15 +1282,12 @@ One or more words can be supplied as \"commands\". These may be:\n\n\ printf( " $list list current settings\n\ - $huge make standard \"huge game\" settings %s\n\ - $large make standard \"large game\" settings %s\n\ - $small make standard \"small game\" settings %s\n\ $?SETTING explain briefly what SETTING is for\n\ $SETTING=number change SETTING to given number\n\ - $#SYMBOL=number define SYMBOL as a constant in the story\n\n", - (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"", - (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"", - (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":""); + $!TRACEOPT set trace option TRACEOPT\n\ + (or $!TRACEOPT=2, 3, etc for more tracing;\n\ + $! by itself to list all trace options)\n\ + $#SYMBOL=number define SYMBOL as a constant in the story\n\n"); printf( " (filename) read in a list of commands (in the format above)\n\ @@ -1319,17 +1295,19 @@ One or more words can be supplied as \"commands\". These may be:\n\n\ printf("Alternate command-line formats for the above:\n\ --help (this page)\n\ - --path PATH=dir\n\ - --addpath PATH=dir\n\ - --list\n\ - --size huge, --size large, --size small\n\ - --helpopt SETTING\n\ - --opt SETTING=number\n\ - --define SETTING=number\n\ - --config filename (setup file)\n\n"); + --path PATH=dir (set path)\n\ + --addpath PATH=dir (add to path)\n\ + --list (list current settings)\n\ + --helpopt SETTING (explain setting)\n\ + --opt SETTING=number (change setting)\n\ + --helptrace (list all trace options)\n\ + --trace TRACEOPT (set trace option)\n\ + --trace TRACEOPT=num (more tracing)\n\ + --define SYMBOL=number (define constant)\n\ + --config filename (read setup file)\n\n"); #ifndef PROMPT_INPUT - printf("For example: \"inform -dexs $huge curses\".\n"); + printf("For example: \"inform -dexs curses\".\n"); #endif return; @@ -1342,7 +1320,8 @@ One or more words can be supplied as \"commands\". These may be:\n\n\ /* The -h2 (switches) help information: */ printf("Help on the full list of legal switch commands:\n\n\ - a trace assembly-language (without hex dumps; see -t)\n\ + a trace assembly-language\n\ + a2 trace assembly with hex dumps\n\ c more concise error messages\n\ d contract double spaces after full stops in text\n\ d2 contract double spaces after exclamation and question marks, too\n\ @@ -1359,19 +1338,12 @@ One or more words can be supplied as \"commands\". These may be:\n\n\ printf("\ i ignore default switches set within the file\n\ - j list objects as constructed\n\ - k output debugging information to \"%s\"\n\ - l list every statement run through Inform (not implemented)\n\ - m say how much memory has been allocated\n\ - n print numbers of properties, attributes and actions\n", + k output debugging information to \"%s\"\n", Debugging_Name); printf("\ - o print offset addresses\n\ - p give percentage breakdown of story file\n\ q keep quiet about obsolete usages\n\ r record all the text to \"%s\"\n\ - s give statistics\n\ - t trace assembly-language (with full hex dumps; see -a)\n", + s give statistics\n", Transcript_Name); printf("\ @@ -1384,7 +1356,6 @@ One or more words can be supplied as \"commands\". These may be:\n\n\ v8 compile to version-8 (expanded \"Advanced\") story file\n\ w disable warning messages\n\ x print # for every 100 lines compiled\n\ - y trace linking system\n\ z print memory map of the virtual machine\n\n"); printf("\ @@ -1401,11 +1372,6 @@ printf(" E1 Microsoft-style error messages%s\n", (error_format==1)?" (current setting)":""); printf(" E2 Macintosh MPW-style error messages%s\n", (error_format==2)?" (current setting)":""); -#ifdef USE_TEMPORARY_FILES -printf(" F0 use extra memory rather than temporary files\n"); -#else -printf(" F1 use temporary files to reduce memory consumption\n"); -#endif printf(" G compile a Glulx game file\n"); printf(" H use Huffman encoding to compress Glulx strings\n"); printf(" M compile as a Module for future linking\n"); @@ -1446,8 +1412,14 @@ extern void switches(char *p, int cmode) } switch(p[i]) { - case 'a': asm_trace_setting = 1; break; - case 'b': bothpasses_switch = state; break; + case 'a': switch(p[i+1]) + { case '1': asm_trace_setting=1; s=2; break; + case '2': asm_trace_setting=2; s=2; break; + case '3': asm_trace_setting=3; s=2; break; + case '4': asm_trace_setting=4; s=2; break; + default: asm_trace_setting=1; break; + } + break; case 'c': concise_switch = state; break; case 'd': switch(p[i+1]) { case '1': double_space_setting=1; s=2; break; @@ -1456,7 +1428,7 @@ extern void switches(char *p, int cmode) } break; case 'e': economy_switch = state; break; - case 'f': frequencies_switch = state; break; + case 'f': frequencies_setting = (state?1:0); break; case 'g': switch(p[i+1]) { case '1': trace_fns_setting=1; s=2; break; case '2': trace_fns_setting=2; s=2; break; @@ -1472,24 +1444,17 @@ extern void switches(char *p, int cmode) } break; case 'i': ignore_switches_switch = state; break; - case 'j': listobjects_switch = state; break; case 'k': if (cmode == 0) error("The switch '-k' can't be set with 'Switches'"); else debugfile_switch = state; break; - case 'l': listing_switch = state; break; - case 'm': memout_switch = state; break; - case 'n': printprops_switch = state; break; - case 'o': offsets_switch = state; break; - case 'p': percentages_switch = state; break; case 'q': obsolete_switch = state; break; case 'r': if (cmode == 0) error("The switch '-r' can't be set with 'Switches'"); else transcript_switch = state; break; case 's': statistics_switch = state; break; - case 't': asm_trace_setting=2; break; case 'u': if (cmode == 0) { error("The switch '-u' can't be set with 'Switches'"); break; @@ -1514,8 +1479,7 @@ extern void switches(char *p, int cmode) break; case 'w': nowarnings_switch = state; break; case 'x': hash_switch = state; break; - case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break; - case 'z': memory_map_switch = state; break; + case 'z': memory_map_setting = (state ? 1 : 0); break; case 'B': oddeven_packing_switch = state; break; case 'C': s=2; if (p[i+1] == 'u') { @@ -1543,16 +1507,6 @@ extern void switches(char *p, int cmode) default: error_format=1; break; } break; - case 'F': if (cmode == 0) { - error("The switch '-F' can't be set with 'Switches'"); - break; - } - switch(p[i+1]) - { case '0': s=2; temporary_files_switch = FALSE; break; - case '1': s=2; temporary_files_switch = TRUE; break; - default: temporary_files_switch = state; break; - } - break; case 'M': module_switch = state; if (state && (r_e_c_s_set == FALSE)) runtime_error_checking_switch = FALSE; @@ -1598,22 +1552,15 @@ extern void switches(char *p, int cmode) } } - if (optimise_switch && (!store_the_text)) - { store_the_text=TRUE; -#ifdef PC_QUICKC - if (memout_switch) - printf("Allocation %ld bytes for transcription text\n", - (long) MAX_TRANSCRIPT_SIZE); - all_text = halloc(MAX_TRANSCRIPT_SIZE,1); - malloced_bytes += MAX_TRANSCRIPT_SIZE; - if (all_text==NULL) - fatalerror("Can't hallocate memory for transcription text. Darn."); -#else - all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text"); -#endif + if (optimise_switch) + { + /* store_the_text is equivalent to optimise_switch; -u sets both. + We could simplify this. */ + store_the_text=TRUE; } } +/* Check whether the string looks like an ICL command. */ static int icl_command(char *p) { if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$') || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE; @@ -1844,6 +1791,7 @@ static int execute_dashdash_command(char *p, char *p2) } else if (!strcmp(p, "size")) { consumed2 = TRUE; + /* We accept these arguments even though they've been withdrawn. */ if (!(p2 && (!strcmpcis(p2, "HUGE") || !strcmpcis(p2, "LARGE") || !strcmpcis(p2, "SMALL")))) { printf("--size must be followed by \"huge\", \"large\", or \"small\"\n"); return consumed2; @@ -1902,6 +1850,17 @@ static int execute_dashdash_command(char *p, char *p2) } snprintf(cli_buff, CMD_BUF_SIZE, "(%s)", p2); } + else if (!strcmp(p, "trace")) { + consumed2 = TRUE; + if (!p2) { + printf("--trace must be followed by \"traceopt\" or \"traceopt=N\"\n"); + return consumed2; + } + snprintf(cli_buff, CMD_BUF_SIZE, "$!%s", p2); + } + else if (!strcmp(p, "helptrace")) { + strcpy(cli_buff, "$!"); + } else { printf("Option \"--%s\" unknown (try \"inform -h\")\n", p); return FALSE; @@ -2039,7 +1998,7 @@ static int sub_main(int argc, char **argv) banner(); - set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths(); + set_memory_sizes(); set_default_paths(); reset_switch_settings(); select_version(5); cli_files_specified = 0; no_compilations = 0; diff --git a/src/lexer.c b/src/lexer.c index f79a6e6..5884126 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "lexer" : Lexical analyser */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -15,7 +15,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -45,7 +45,7 @@ int next_token_begins_syntax_line; /* When TRUE, start a new syntax int32 last_mapped_line; /* Last syntax line reported to debugging file */ /* ------------------------------------------------------------------------- */ -/* The lexer's output is a sequence of triples, each called a "token", */ +/* The lexer's output is a sequence of structs, each called a "token", */ /* representing one lexical unit (or "lexeme") each. Instead of providing */ /* "lookahead" (that is, always having available the next token after the */ /* current one, so that syntax analysers higher up in Inform can have */ @@ -59,6 +59,8 @@ int32 last_mapped_line; /* Last syntax line reported to debugging file */ /* ------------------------------------------------------------------------- */ /* These three variables are set to the current token on a call to */ /* get_next_token() (but are not changed by a call to put_token_back()). */ +/* (It would be tidier to use a token_data structure, rather than having */ +/* get_next_token() unpack three values. But this is the way it is.) */ /* ------------------------------------------------------------------------- */ int token_type; @@ -226,6 +228,11 @@ extern debug_locations get_token_location_end /* maximum number of tokens ever put back at once, plus 1 (in effect, the */ /* maximum token lookahead ever needed in syntax analysis, plus 1). */ /* */ +/* Note that the circle struct type is lexeme_data, whereas the expression */ +/* code all works in token_data. They have slightly different needs. The */ +/* data is exported through the token_text, token_value, token_type */ +/* globals, so there's no need to use the same struct at both levels. */ +/* */ /* Unlike some compilers, Inform does not have a context-free lexer: in */ /* fact it has 12288 different possible states. However, the context only */ /* affects the interpretation of "identifiers": lexemes beginning with a */ @@ -245,24 +252,36 @@ extern debug_locations get_token_location_end old-style "objectloop (a in b)" and a new "objectloop (a in b ...)".) */ static int circle_position; -static token_data circle[CIRCLE_SIZE]; - -static int token_contexts[CIRCLE_SIZE]; +static lexeme_data circle[CIRCLE_SIZE]; /* ------------------------------------------------------------------------- */ /* A complication, however, is that the text of some lexemes needs to be */ /* held in Inform's memory for much longer periods: for example, a */ /* dictionary word lexeme (like "'south'") must have its text preserved */ /* until the code generation time for the expression it occurs in, when */ -/* the dictionary reference is actually made. Code generation in general */ -/* occurs as early as possible in Inform: pending some better method of */ -/* garbage collection, we simply use a buffer so large that unless */ -/* expressions spread across 10K of source code are found, there can be */ -/* no problem. */ +/* the dictionary reference is actually made. We handle this by keeping */ +/* all lexeme text until the end of the statement (or, for top-level */ +/* directives, until the end of the directive). Then we call */ +/* release_token_texts() to start over. The lextexts array will therefore */ +/* grow to the largest number of lexemes in a single statement or */ +/* directive. */ /* ------------------------------------------------------------------------- */ -static char *lexeme_memory; -static char *lex_p; /* Current write position */ +typedef struct lextext_s { + char *text; + size_t size; /* Allocated size (including terminal null) + This is always at least MAX_IDENTIFIER_LENGTH+1 */ +} lextext; + +static lextext *lextexts; /* Allocated to no_lextexts */ +static memory_list lextexts_memlist; +static int no_lextexts; + +static int cur_lextexts; /* Number of lextexts in current use + (cur_lextexts <= no_lextexts) */ + +static int lex_index; /* Index of lextext being written to */ +static int lex_pos; /* Current write position in that lextext */ /* ------------------------------------------------------------------------- */ /* The lexer itself needs up to 3 characters of lookahead (it uses an */ @@ -294,7 +313,10 @@ static int tokens_put_back; /* Count of the number of backward moves made from the last-read token */ -extern void describe_token(token_data t) +/* This gets called for both token_data and lexeme_data structs. It prints + a description of the common part (the text, value, type fields). +*/ +extern void describe_token_triple(const char *text, int32 value, int type) { /* Many of the token types are not set in this file, but later on in Inform's higher stages (for example, in the expression evaluator); @@ -302,51 +324,51 @@ extern void describe_token(token_data t) printf("{ "); - switch(t.type) + switch(type) { /* The following token types occur in lexer output: */ case SYMBOL_TT: printf("symbol "); - describe_symbol(t.value); + describe_symbol(value); break; - case NUMBER_TT: printf("literal number %d", t.value); + case NUMBER_TT: printf("literal number %d", value); break; - case DQ_TT: printf("string \"%s\"", t.text); + case DQ_TT: printf("string \"%s\"", text); break; - case SQ_TT: printf("string '%s'", t.text); + case SQ_TT: printf("string '%s'", text); break; - case SEP_TT: printf("separator '%s'", t.text); + case SEP_TT: printf("separator '%s'", text); break; case EOF_TT: printf("end of file"); break; - case STATEMENT_TT: printf("statement name '%s'", t.text); + case STATEMENT_TT: printf("statement name '%s'", text); break; - case SEGMENT_MARKER_TT: printf("object segment marker '%s'", t.text); + case SEGMENT_MARKER_TT: printf("object segment marker '%s'", text); break; - case DIRECTIVE_TT: printf("directive name '%s'", t.text); + case DIRECTIVE_TT: printf("directive name '%s'", text); break; - case CND_TT: printf("textual conditional '%s'", t.text); + case CND_TT: printf("textual conditional '%s'", text); break; - case OPCODE_NAME_TT: printf("opcode name '%s'", t.text); + case OPCODE_NAME_TT: printf("opcode name '%s'", text); break; - case SYSFUN_TT: printf("built-in function name '%s'", t.text); + case SYSFUN_TT: printf("built-in function name '%s'", text); break; - case LOCAL_VARIABLE_TT: printf("local variable name '%s'", t.text); + case LOCAL_VARIABLE_TT: printf("local variable name '%s'", text); break; - case MISC_KEYWORD_TT: printf("statement keyword '%s'", t.text); + case MISC_KEYWORD_TT: printf("statement keyword '%s'", text); break; - case DIR_KEYWORD_TT: printf("directive keyword '%s'", t.text); + case DIR_KEYWORD_TT: printf("directive keyword '%s'", text); break; - case TRACE_KEYWORD_TT: printf("'trace' keyword '%s'", t.text); + case TRACE_KEYWORD_TT: printf("'trace' keyword '%s'", text); break; - case SYSTEM_CONSTANT_TT: printf("system constant name '%s'", t.text); + case SYSTEM_CONSTANT_TT: printf("system constant name '%s'", text); break; /* The remaining are etoken types, not set by the lexer */ case OP_TT: printf("operator '%s'", - operators[t.value].description); + operators[value].description); break; case ENDEXP_TT: printf("end of expression"); break; @@ -354,26 +376,26 @@ extern void describe_token(token_data t) break; case SUBCLOSE_TT: printf("close bracket"); break; - case LARGE_NUMBER_TT: printf("large number: '%s'=%d",t.text,t.value); + case LARGE_NUMBER_TT: printf("large number: '%s'=%d",text,value); break; - case SMALL_NUMBER_TT: printf("small number: '%s'=%d",t.text,t.value); + case SMALL_NUMBER_TT: printf("small number: '%s'=%d",text,value); break; - case VARIABLE_TT: printf("variable '%s'=%d", t.text, t.value); + case VARIABLE_TT: printf("variable '%s'=%d", text, value); break; - case DICTWORD_TT: printf("dictionary word '%s'", t.text); + case DICTWORD_TT: printf("dictionary word '%s'", text); break; - case ACTION_TT: printf("action name '%s'", t.text); + case ACTION_TT: printf("action name '%s'", text); break; default: printf("** unknown token type %d, text='%s', value=%d **", - t.type, t.text, t.value); + type, text, value); } printf(" }"); } /* ------------------------------------------------------------------------- */ -/* All but one of the 280 Inform keywords (118 of them opcode names used */ +/* All but one of the Inform keywords (most of them opcode names used */ /* only by the assembler). (The one left over is "sp", a keyword used in */ /* assembly language only.) */ /* */ @@ -385,7 +407,9 @@ extern void describe_token(token_data t) /* "header.h" but is otherwise not significant. */ /* ------------------------------------------------------------------------- */ -#define MAX_KEYWORDS 350 +/* This must exceed the total number of keywords across all groups, + including opcodes. */ +#define MAX_KEYWORDS (350) /* The values will be filled in at compile time, when we know which opcode set to use. */ @@ -444,6 +468,7 @@ static char *opcode_list_g[] = { "sqrt", "exp", "log", "pow", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "jfeq", "jfne", "jflt", "jfle", "jfgt", "jfge", "jisnan", "jisinf", + "hasundo", "discardundo", "" }; @@ -492,7 +517,7 @@ keyword_group directive_keywords = "string", "table", "buffer", "data", "initial", "initstr", "with", "private", "has", "class", "error", "fatalerror", "warning", - "terminating", "static", + "terminating", "static", "individual", "" }, DIR_KEYWORD_TT, FALSE, TRUE }; @@ -561,8 +586,10 @@ keyword_group *keyword_groups[12] &directive_keywords, &misc_keywords, &statements, &conditions, &system_functions, &system_constants, &opcode_macros}; +/* These keywords are set to point to local_variable_names entries when + a routine header is parsed. See construct_local_variable_tables(). */ keyword_group local_variables = -{ { "" }, /* Filled in when routine declared */ +{ { "" }, LOCAL_VARIABLE_TT, FALSE, FALSE }; @@ -618,8 +645,21 @@ static int *keywords_data_table; static int *local_variable_hash_table; static int *local_variable_hash_codes; -char **local_variable_texts; -static char *local_variable_text_table; + +/* Note that MAX_LOCAL_VARIABLES is the maximum number of local variables + for this VM, *including* "sp" (the stack pointer "local"). + This used to be a memory setting. Now it is a constant: 16 for Z-code, + 119 for Glulx. +*/ + +/* Names of local variables in the current routine. + This is allocated to MAX_LOCAL_VARIABLES-1. (The stack pointer "local" + is not included in this array.) + + (This could be a memlist, growing as needed up to MAX_LOCAL_VARIABLES-1. + But right now we just allocate the max.) + */ +identstruct *local_variable_names; static char one_letter_locals[128]; @@ -637,11 +677,21 @@ static void make_keywords_tables(void) } for (j=0; *(oplist[j]); j++) { + if (j >= MAX_KEYWORD_GROUP_SIZE) { + /* Gotta increase MAX_KEYWORD_GROUP_SIZE */ + compiler_error("opcode_list has overflowed opcode_names.keywords"); + break; + } opcode_names.keywords[j] = oplist[j]; } opcode_names.keywords[j] = ""; for (j=0; *(maclist[j]); j++) { + if (j >= MAX_KEYWORD_GROUP_SIZE) { + /* Gotta increase MAX_KEYWORD_GROUP_SIZE */ + compiler_error("opmacro_list has overflowed opcode_macros.keywords"); + break; + } opcode_macros.keywords[j] = maclist[j]; } opcode_macros.keywords[j] = ""; @@ -654,7 +704,13 @@ static void make_keywords_tables(void) for (i=1; i<=11; i++) { keyword_group *kg = keyword_groups[i]; for (j=0; *(kg->keywords[j]) != 0; j++) - { h = hash_code_from_string(kg->keywords[j]); + { + if (tp >= MAX_KEYWORDS) { + /* Gotta increase MAX_KEYWORDS */ + compiler_error("keywords_data_table has overflowed MAX_KEYWORDS"); + break; + } + h = hash_code_from_string(kg->keywords[j]); if (keywords_hash_table[h] == -1) keywords_hash_table[h] = tp; else @@ -668,32 +724,37 @@ static void make_keywords_tables(void) } } +/* Look at the strings stored in local_variable_names (from 0 to no_locals). + Set local_variables.keywords to point to these, and also prepare the + hash tables. */ extern void construct_local_variable_tables(void) -{ int i, h; char *p = local_variable_text_table; +{ int i, h; for (i=0; i= 0) { for (;index= MAX_INCLUSION_DEPTH) - memoryerror("MAX_INCLUSION_DEPTH",MAX_INCLUSION_DEPTH); + CF = NULL; + CurrentLB = NULL; + + ensure_memory_list_available(&FileStack_memlist, i+1); + while (i >= FileStack_max) { + FileStack[FileStack_max++].buffer = my_malloc(SOURCE_BUFFER_SIZE+4, "source file buffer"); + } p = (uchar *) FileStack[i].buffer; @@ -1280,6 +1349,8 @@ static void begin_buffering_file(int i, int file_no) FileStack[i].file_no = file_no; FileStack[i].size = file_load_chars(file_no, (char *) p, SOURCE_BUFFER_SIZE); + /* If the file is shorter than SOURCE_BUFFER_SIZE, it's now closed already. We still need to set up the file entry though. */ + lookahead = source_to_iso_grid[p[0]]; lookahead2 = source_to_iso_grid[p[1]]; lookahead3 = source_to_iso_grid[p[2]]; @@ -1299,6 +1370,8 @@ static void begin_buffering_file(int i, int file_no) FileStack[i].LB.orig_source = NULL; FileStack[i].LB.orig_file = 0; FileStack[i].LB.orig_line = 0; FileStack[i].LB.orig_char = 0; + InputFiles[file_no-1].initial_buffering = FALSE; + CurrentLB = &(FileStack[i].LB); CF = &(FileStack[i]); @@ -1376,7 +1449,7 @@ static int get_next_char_from_pipeline(void) lookahead3 = source_to_iso_grid[p[CF->read_pos++]]; CurrentLB->chars_read++; - if (forerrors_pointer < 511) + if (forerrors_pointer < FORERRORS_SIZE-1) forerrors_buff[forerrors_pointer++] = current; if (current == '\n') reached_new_line(); return(current); @@ -1400,7 +1473,7 @@ static int get_next_char_from_string(void) else lookahead3 = source_to_iso_grid[p[3]]; CurrentLB->chars_read++; - if (forerrors_pointer < 511) + if (forerrors_pointer < FORERRORS_SIZE-1) forerrors_buff[forerrors_pointer++] = current; if (current == '\n') reached_new_line(); return(current); @@ -1418,11 +1491,54 @@ static int get_next_char_from_string(void) /* and move the read position forward */ /* by one */ /* */ +/* release_token_texts() discard all the tokens that have been */ +/* read in, except for put-back ones */ +/* */ /* restart_lexer(source, name) if source is NULL, initialise the lexer */ /* to read from source files; */ /* otherwise, to read from this string. */ /* ------------------------------------------------------------------------- */ +extern void release_token_texts(void) +{ + /* This is called at the beginning of every (top-level) directive and + every statement. It drops all token usage so that the lextexts + array can be reused. + + Call this immediately before a get_next_token() call. + + This should *not* be called within parse_expression(). Expression + code generation relies on token data being retained across the whole + expression. + */ + int ix; + + token_text = NULL; + + if (tokens_put_back == 0) { + cur_lextexts = 0; + return; + } + + /* If any tokens have been put back, we have to preserve their text. + Move their lextext usage to the head of the lextexts array. */ + + for (ix=0; ix= lextexts[lex_index].size) { + size_t newsize = lextexts[lex_index].size * 2; + my_realloc(&lextexts[lex_index].text, lextexts[lex_index].size, newsize, "one lexeme text"); + lextexts[lex_index].size = newsize; + } + lextexts[lex_index].text[lex_pos++] = ch; +} + +/* Remove the last character and ensure it's null-terminated */ +static void lexdelc(void) +{ + if (lex_pos > 0) { + lex_pos--; + } + lextexts[lex_index].text[lex_pos] = 0; +} + +/* Return the last character */ +static char lexlastc(void) +{ + if (lex_pos == 0) { + return 0; + } + return lextexts[lex_index].text[lex_pos-1]; +} + +/* Add a string of characters (including the null) */ +static void lexadds(char *str) +{ + while (*str) { + lexaddc(*str); + str++; + } + lexaddc(0); +} + extern void get_next_token(void) { int d, i, j, k, quoted_size, e, radix, context; int32 n; char *r; int returning_a_put_back_token = TRUE; - + context = lexical_context(); if (tokens_put_back > 0) { i = circle_position - tokens_put_back + 1; if (i<0) i += CIRCLE_SIZE; tokens_put_back--; - if (context != token_contexts[i]) + if (context != circle[i].context) { j = circle[i].type; if ((j==0) || ((j>=100) && (j<200))) - interpret_identifier(i, FALSE); + interpret_identifier(circle[i].text, i, FALSE); + circle[i].context = context; } goto ReturnBack; } @@ -1463,12 +1627,22 @@ extern void get_next_token(void) if (circle_position == CIRCLE_SIZE-1) circle_position = 0; else circle_position++; - if (lex_p > lexeme_memory + 4*MAX_QTEXT_SIZE) - lex_p = lexeme_memory; - - circle[circle_position].text = lex_p; + lex_index = cur_lextexts++; + if (lex_index >= no_lextexts) { + /* fresh lextext block; must init it */ + no_lextexts = lex_index+1; + ensure_memory_list_available(&lextexts_memlist, no_lextexts); + lextexts[lex_index].size = MAX_IDENTIFIER_LENGTH + 1; + lextexts[lex_index].text = my_malloc(lextexts[lex_index].size, "one lexeme text"); + } + lex_pos = 0; + lextexts[lex_index].text[0] = 0; /* start with an empty string */ + + circle[circle_position].lextext = lex_index; + circle[circle_position].text = NULL; /* will fill in later */ circle[circle_position].value = 0; - *lex_p = 0; + circle[circle_position].type = 0; + circle[circle_position].context = context; StartTokenAgain: d = (*get_next_char)(); @@ -1499,8 +1673,7 @@ extern void get_next_token(void) case EOF_CODE: circle[circle_position].type = EOF_TT; - strcpy(lex_p, ""); - lex_p += strlen(lex_p) + 1; + lexadds(""); break; case DIGIT_CODE: @@ -1509,11 +1682,11 @@ extern void get_next_token(void) n=0; do { n = n*radix + character_digit_value[d]; - *lex_p++ = d; + lexaddc(d); } while ((character_digit_value[lookahead] < radix) && (d = (*get_next_char)(), TRUE)); - *lex_p++ = 0; + lexaddc(0); circle[circle_position].type = NUMBER_TT; circle[circle_position].value = n; break; @@ -1522,38 +1695,38 @@ extern void get_next_token(void) { int expo=0; double intv=0, fracv=0; int expocount=0, intcount=0, fraccount=0; int signbit = (d == '-'); - *lex_p++ = d; + lexaddc(d); while (character_digit_value[lookahead] < 10) { intv = 10.0*intv + character_digit_value[lookahead]; intcount++; - *lex_p++ = lookahead; + lexaddc(lookahead); (*get_next_char)(); } if (lookahead == '.') { double fracpow = 1.0; - *lex_p++ = lookahead; + lexaddc(lookahead); (*get_next_char)(); while (character_digit_value[lookahead] < 10) { fracpow *= 0.1; fracv = fracv + fracpow*character_digit_value[lookahead]; fraccount++; - *lex_p++ = lookahead; + lexaddc(lookahead); (*get_next_char)(); } } if (lookahead == 'e' || lookahead == 'E') { int exposign = 0; - *lex_p++ = lookahead; + lexaddc(lookahead); (*get_next_char)(); if (lookahead == '+' || lookahead == '-') { exposign = (lookahead == '-'); - *lex_p++ = lookahead; + lexaddc(lookahead); (*get_next_char)(); } while (character_digit_value[lookahead] < 10) { expo = 10*expo + character_digit_value[lookahead]; expocount++; - *lex_p++ = lookahead; + lexaddc(lookahead); (*get_next_char)(); } if (expocount == 0) @@ -1564,7 +1737,7 @@ extern void get_next_token(void) error("Floating-point literal must have digits"); n = construct_float(signbit, intv, fracv, expo); } - *lex_p++ = 0; + lexaddc(0); circle[circle_position].type = NUMBER_TT; circle[circle_position].value = n; if (!glulx_mode && dont_enter_into_symbol_table != -2) error("Floating-point literals are not available in Z-code"); @@ -1585,15 +1758,15 @@ extern void get_next_token(void) case QUOTE_CODE: /* Single-quotes: scan a literal string */ quoted_size=0; do - { e = d; d = (*get_next_char)(); *lex_p++ = d; + { e = d; d = (*get_next_char)(); lexaddc(d); if (quoted_size++==64) { error( "Too much text for one pair of quotations '...' to hold"); - *lex_p='\''; break; + lexaddc('\''); break; } if ((d == '\'') && (e != '@')) { if (quoted_size == 1) - { d = (*get_next_char)(); *lex_p++ = d; + { d = (*get_next_char)(); lexaddc(d); if (d != '\'') error("No text between quotation marks ''"); } @@ -1601,29 +1774,25 @@ extern void get_next_token(void) } } while (d != EOF); if (d==EOF) ebf_error("'\''", "end of file"); - *(lex_p-1) = 0; + lexdelc(); circle[circle_position].type = SQ_TT; break; case DQUOTE_CODE: /* Double-quotes: scan a literal string */ quoted_size=0; do - { d = (*get_next_char)(); *lex_p++ = d; - if (quoted_size++==MAX_QTEXT_SIZE) - { memoryerror("MAX_QTEXT_SIZE", MAX_QTEXT_SIZE); - break; - } + { d = (*get_next_char)(); lexaddc(d); if (d == '\n') - { lex_p--; - while (*(lex_p-1) == ' ') lex_p--; - if (*(lex_p-1) != '^') *lex_p++ = ' '; + { lex_pos--; + while (lexlastc() == ' ') lex_pos--; + if (lexlastc() != '^') lexaddc(' '); while ((lookahead != EOF) && (tokeniser_grid[lookahead] == WHITESPACE_CODE)) (*get_next_char)(); } else if (d == '\\') { int newline_passed = FALSE; - lex_p--; + lex_pos--; while ((lookahead != EOF) && (tokeniser_grid[lookahead] == WHITESPACE_CODE)) if ((d = (*get_next_char)()) == '\n') @@ -1638,40 +1807,44 @@ extern void get_next_token(void) } } while ((d != EOF) && (d!='\"')); if (d==EOF) ebf_error("'\"'", "end of file"); - *(lex_p-1) = 0; + lexdelc(); circle[circle_position].type = DQ_TT; break; case IDENTIFIER_CODE: /* Letter or underscore: an identifier */ - *lex_p++ = d; n=1; + lexaddc(d); n=1; while ((n<=MAX_IDENTIFIER_LENGTH) && ((tokeniser_grid[lookahead] == IDENTIFIER_CODE) || (tokeniser_grid[lookahead] == DIGIT_CODE))) - n++, *lex_p++ = (*get_next_char)(); + n++, lexaddc((*get_next_char)()); - *lex_p++ = 0; + lexaddc(0); if (n > MAX_IDENTIFIER_LENGTH) { char bad_length[100]; sprintf(bad_length, "Name exceeds the maximum length of %d characters:", MAX_IDENTIFIER_LENGTH); - error_named(bad_length, circle[circle_position].text); + error_named(bad_length, lextexts[lex_index].text); + /* Eat any further extra characters in the identifier */ + while (((tokeniser_grid[lookahead] == IDENTIFIER_CODE) + || (tokeniser_grid[lookahead] == DIGIT_CODE))) + (*get_next_char)(); /* Trim token so that it doesn't violate MAX_IDENTIFIER_LENGTH during error recovery */ - circle[circle_position].text[MAX_IDENTIFIER_LENGTH] = 0; + lextexts[lex_index].text[MAX_IDENTIFIER_LENGTH] = 0; } if (dont_enter_into_symbol_table) { circle[circle_position].type = DQ_TT; circle[circle_position].value = 0; if (dont_enter_into_symbol_table == -2) - interpret_identifier(circle_position, TRUE); + interpret_identifier(lextexts[lex_index].text, circle_position, TRUE); break; } - interpret_identifier(circle_position, FALSE); + interpret_identifier(lextexts[lex_index].text, circle_position, FALSE); break; default: @@ -1681,24 +1854,25 @@ extern void get_next_token(void) for (j=e>>4, k=j+(e&0x0f); j 0) - { if (tokens_trace_level == 1) + { if (tokens_trace_level == 1) { printf("'%s' ", circle[i].text); + if (circle[i].type == EOF_TT) printf("\n"); + } else - { printf("-> "); describe_token(circle[i]); + { printf("-> "); describe_token(&circle[i]); printf(" "); - if (tokens_trace_level > 2) print_context(token_contexts[i]); + if (tokens_trace_level > 2) print_context(circle[i].context); printf("\n"); } } @@ -1783,10 +1964,15 @@ extern void restart_lexer(char *lexical_source, char *name) { circle[i].type = 0; circle[i].value = 0; circle[i].text = "(if this is ever visible, there is a bug)"; - token_contexts[i] = 0; + circle[i].lextext = -1; + circle[i].context = 0; } - lex_p = lexeme_memory; + cur_lextexts = 0; + /* But we don't touch no_lextexts; those allocated blocks can be reused */ + lex_index = -1; + lex_pos = -1; + tokens_put_back = 0; forerrors_pointer = 0; dont_enter_into_symbol_table = FALSE; @@ -1819,6 +2005,17 @@ extern void restart_lexer(char *lexical_source, char *name) extern void init_lexer_vars(void) { + FileStack = NULL; + FileStack_max = 0; + CF = NULL; + CurrentLB = NULL; + + lextexts = NULL; + no_lextexts = 0; + cur_lextexts = 0; + lex_index = -1; + lex_pos = -1; + blank_brief_location.file_index = -1; blank_brief_location.line_number = 0; blank_brief_location.orig_file_index = 0; @@ -1846,15 +2043,16 @@ extern void lexer_endpass(void) } extern void lexer_allocate_arrays(void) -{ int i; - - FileStack = my_malloc(MAX_INCLUSION_DEPTH*sizeof(Sourcefile), - "filestack buffer"); - - for (i=0; i= 4) - printf("Creating action ##%s\n", (char *) symbs[index]); + printf("Creating action ##%s\n", symbols[index].name); } else switch(IE.symbol_type) { case ROUTINE_T: if ((IE.module_value == EXPORTSF_MV) - && (sflags[index] & REPLACE_SFLAG)) + && (symbols[index].flags & REPLACE_SFLAG)) { routine_replace[no_rr] = IE.symbol_value; routine_replace_with[no_rr++] = index; return; @@ -282,7 +288,7 @@ static void accept_export(void) IE.symbol_value += no_objects; break; case ARRAY_T: - IE.symbol_value += dynamic_array_area_size - (MAX_GLOBAL_VARIABLES*2); + IE.symbol_value += dynamic_array_area_size - (MAX_ZCODE_GLOBAL_VARS*2); break; case GLOBAL_VARIABLE_T: if (no_globals==233) @@ -310,21 +316,21 @@ static void accept_export(void) break; } - assign_symbol(index, IE.backpatch*0x10000 + IE.symbol_value, + assign_marked_symbol(index, IE.backpatch, IE.symbol_value, IE.symbol_type); - if (IE.backpatch != 0) sflags[index] |= CHANGE_SFLAG; - sflags[index] |= EXPORT_SFLAG; + if (IE.backpatch != 0) symbols[index].flags |= CHANGE_SFLAG; + symbols[index].flags |= EXPORT_SFLAG; if (IE.module_value == EXPORTSF_MV) - sflags[index] |= INSF_SFLAG; + symbols[index].flags |= INSF_SFLAG; if (IE.module_value == EXPORTAC_MV) - sflags[index] |= ACTION_SFLAG; + symbols[index].flags |= ACTION_SFLAG; } if (IE.module_value == EXPORTAC_MV) { if (linker_trace_level >= 4) printf("Map %d '%s' to %d\n", - IE.symbol_value, (char *) (symbs[index]), svals[index]); - actions_map[map_to] = svals[index]; + IE.symbol_value, (symbols[index].name), symbols[index].value); + actions_map[map_to] = symbols[index].value; } } @@ -332,22 +338,22 @@ static void accept_import(void) { int32 index; index = symbol_index(IE.symbol_name, -1); - sflags[index] |= USED_SFLAG; + symbols[index].flags |= USED_SFLAG; xref_table[IE.symbol_number] = index; - if (!(sflags[index] & UNKNOWN_SFLAG)) + if (!(symbols[index].flags & UNKNOWN_SFLAG)) { switch (IE.symbol_type) { case GLOBAL_VARIABLE_T: - if (stypes[index] != GLOBAL_VARIABLE_T) + if (symbols[index].type != GLOBAL_VARIABLE_T) link_error_named( "module (wrongly) declared this a variable:", IE.symbol_name); - variables_map[IE.symbol_value] = svals[index]; + variables_map[IE.symbol_value] = symbols[index].value; if (IE.symbol_value < lowest_imported_global_no) lowest_imported_global_no = IE.symbol_value; break; default: - switch(stypes[index]) + switch(symbols[index].type) { case ATTRIBUTE_T: link_error_named( "this attribute is undeclared within module:", IE.symbol_name);; break; @@ -374,7 +380,7 @@ static void accept_import(void) { switch (IE.symbol_type) { case GLOBAL_VARIABLE_T: - if (stypes[index] != GLOBAL_VARIABLE_T) + if (symbols[index].type != GLOBAL_VARIABLE_T) link_error_named( "Module tried to import a Global variable not defined here:", IE.symbol_name); @@ -463,11 +469,11 @@ static int32 backpatch_backpatch(int32 v) break; case ARRAY_MV: - if (v < (MAX_GLOBAL_VARIABLES*2)) + if (v < (MAX_ZCODE_GLOBAL_VARS*2)) { v = 2*(variables_map[v/2 + 16] - 16); } else - { v += dynamic_array_area_size - (MAX_GLOBAL_VARIABLES*2); + { v += dynamic_array_area_size - (MAX_ZCODE_GLOBAL_VARS*2); } break; @@ -512,12 +518,12 @@ static void backpatch_module_image(uchar *p, /* The main routine: linking in a module with the given filename. */ /* ------------------------------------------------------------------------- */ -char current_module_filename[128]; +char current_module_filename[PATHLEN]; void link_module(char *given_filename) { FILE *fin; int record_type; - char filename[128]; + char filename[PATHLEN]; uchar *p, p0[64]; int32 last, i, j, k, l, m, vn, len, size, link_offset, module_size, map, max_property_identifier, symbols_base = no_symbols; @@ -638,7 +644,7 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename); no_rr = 0; if ((linker_trace_level>=1) || transcript_switch) - { char link_banner[128]; + { char link_banner[PATHLEN+128]; sprintf(link_banner, "[Linking release %d.%c%c%c%c%c%c of module '%s' (size %dK)]", p[2]*256 + p[3], p[18], p[19], p[20], p[21], p[22], p[23], @@ -681,7 +687,7 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename); if (((record_type == EXPORT_MV) || (record_type == EXPORTSF_MV)) && (IE.symbol_type == INDIVIDUAL_PROPERTY_T)) { int32 si = symbol_index(IE.symbol_name, -1); - property_identifier_map[IE.symbol_value] = svals[si]; + property_identifier_map[IE.symbol_value] = symbols[si].value; } switch(record_type) { case EXPORT_MV: @@ -706,7 +712,7 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename); for (i=0; i story file '%s'\n", i, - (char *) symbs[xref_table[i]]); + symbols[xref_table[i]].name); } } @@ -728,14 +734,14 @@ of the Inform 6 compiler knows about: it may not link in correctly", filename); /* (6) Backpatch the backpatch markers attached to exported symbols */ for (i=symbols_base; i= MAX_STATIC_DATA) - memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); + i = m_static_offset - m_vars_offset - MAX_ZCODE_GLOBAL_VARS*2; + ensure_memory_list_available(&dynamic_array_area_memlist, dynamic_array_area_size + i); if (linker_trace_level >= 2) printf("Inserting dynamic array area, %04x to %04x, at %04x\n", - m_vars_offset + MAX_GLOBAL_VARIABLES*2, m_static_offset, + m_vars_offset + MAX_ZCODE_GLOBAL_VARS*2, m_static_offset, variables_offset + dynamic_array_area_size); for (k=0;km_props_offset) - { i = m_static_offset - m_vars_offset - MAX_GLOBAL_VARIABLES*2; - if (dynamic_array_area_size + i >= MAX_STATIC_DATA) - memoryerror("MAX_STATIC_DATA", MAX_STATIC_DATA); + { i = m_static_offset - m_vars_offset - MAX_ZCODE_GLOBAL_VARS*2; if (linker_trace_level >= 2) printf("Inserting object properties area, %04x to %04x, at +%04x\n", m_props_offset, last, properties_table_size); + ensure_memory_list_available(&properties_table_memlist, properties_table_size+last-m_props_offset); for (k=0;k= MAX_INDIV_PROP_TABLE_SIZE) - memoryerror("MAX_INDIV_PROP_TABLE_SIZE", - MAX_INDIV_PROP_TABLE_SIZE); + ensure_memory_list_available(&individuals_table_memlist, individuals_length + i); if (linker_trace_level >= 2) printf("Inserting individual prop tables area, %04x to %04x, at +%04x\n", @@ -987,23 +985,19 @@ at strings offset %04x (+%04x)\n", /* ------------------------------------------------------------------------- */ static void write_link_byte(int x) -{ *link_data_top=(unsigned char) x; link_data_top++; link_data_size++; - if (subtract_pointers(link_data_top,link_data_holding_area) - >= MAX_LINK_DATA_SIZE) - { memoryerror("MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE); - } +{ + ensure_memory_list_available(&link_data_holding_area_memlist, link_data_ha_size+1); + link_data_holding_area[link_data_ha_size] = (unsigned char) x; + link_data_ha_size++; link_data_size++; } extern void flush_link_data(void) { int32 i, j; - j = subtract_pointers(link_data_top, link_data_holding_area); - if (temporary_files_switch) - for (i=0;i= 1) { IE.module_value = EXPORT_MV; IE.symbol_number = symbol_number; - IE.symbol_type = stypes[symbol_number]; - IE.symbol_value = svals[symbol_number]; - IE.symbol_name = (char *) (symbs[symbol_number]); + IE.symbol_type = symbols[symbol_number].type; + IE.symbol_value = symbols[symbol_number].value; + IE.symbol_name = (symbols[symbol_number].name); describe_importexport(&IE); } - if (sflags[symbol_number] & ACTION_SFLAG) + if (symbols[symbol_number].flags & ACTION_SFLAG) write_link_byte(EXPORTAC_MV); else - if (sflags[symbol_number] & INSF_SFLAG) + if (symbols[symbol_number].flags & INSF_SFLAG) write_link_byte(EXPORTSF_MV); else write_link_byte(EXPORT_MV); write_link_word(symbol_number); - write_link_byte(stypes[symbol_number]); - if (sflags[symbol_number] & CHANGE_SFLAG) - write_link_byte(svals[symbol_number] / 0x10000); + write_link_byte(symbols[symbol_number].type); + if (symbols[symbol_number].flags & CHANGE_SFLAG) + write_link_byte(symbols[symbol_number].marker); else write_link_byte(0); - write_link_word(svals[symbol_number] % 0x10000); - write_link_string((char *) (symbs[symbol_number])); + write_link_word(symbols[symbol_number].value % 0x10000); + write_link_string((symbols[symbol_number].name)); flush_link_data(); } @@ -1086,17 +1080,17 @@ static void export_symbols(void) { if (linker_trace_level >= 1) { IE.module_value = IMPORT_MV; IE.symbol_number = symbol_number; - IE.symbol_type = stypes[symbol_number]; - IE.symbol_value = svals[symbol_number]; - IE.symbol_name = (char *) (symbs[symbol_number]); + IE.symbol_type = symbols[symbol_number].type; + IE.symbol_value = symbols[symbol_number].value; + IE.symbol_name = (symbols[symbol_number].name); describe_importexport(&IE); } write_link_byte(IMPORT_MV); write_link_word(symbol_number); - write_link_byte(stypes[symbol_number]); - write_link_word(svals[symbol_number]); - write_link_string((char *) (symbs[symbol_number])); + write_link_byte(symbols[symbol_number].type); + write_link_word(symbols[symbol_number].value); + write_link_string((symbols[symbol_number].name)); flush_link_data(); } } @@ -1109,10 +1103,10 @@ static void export_symbols(void) int mv_vref=LOWEST_SYSTEM_VAR_NUMBER-1; void import_symbol(int32 symbol_number) -{ sflags[symbol_number] |= IMPORT_SFLAG; - switch(stypes[symbol_number]) +{ symbols[symbol_number].flags |= IMPORT_SFLAG; + switch(symbols[symbol_number].type) { case GLOBAL_VARIABLE_T: - assign_symbol(symbol_number, mv_vref--, stypes[symbol_number]); + assign_symbol(symbol_number, mv_vref--, symbols[symbol_number].type); break; } } @@ -1123,11 +1117,13 @@ void import_symbol(int32 symbol_number) extern void init_linker_vars(void) { link_data_size = 0; - initialise_memory_block(&link_data_area); + link_data_area = NULL; + link_data_ha_size = 0; + link_data_holding_area = NULL; } extern void linker_begin_pass(void) -{ link_data_top = link_data_holding_area; +{ link_data_ha_size = 0; } extern void linker_endpass(void) @@ -1137,17 +1133,20 @@ extern void linker_endpass(void) } extern void linker_allocate_arrays(void) -{ if (!module_switch) - link_data_holding_area - = my_malloc(64, "link data holding area"); - else - link_data_holding_area - = my_malloc(MAX_LINK_DATA_SIZE, "link data holding area"); +{ + int initlinksize = (module_switch ? 2000 : 0); + initialise_memory_list(&link_data_holding_area_memlist, + sizeof(uchar), initlinksize, (void**)&link_data_holding_area, + "link data holding area"); + initialise_memory_list(&link_data_area_memlist, + sizeof(uchar), 128, (void**)&link_data_area, + "link data area"); } extern void linker_free_arrays(void) -{ my_free(&link_data_holding_area, "link data holding area"); - deallocate_memory_block(&link_data_area); +{ + deallocate_memory_list(&link_data_holding_area_memlist); + deallocate_memory_list(&link_data_area_memlist); } /* ========================================================================= */ diff --git a/src/memory.c b/src/memory.c index a160788..bf2eec0 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,9 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "memory" : Memory management and ICL memory setting commands */ -/* (For "memoryerror", see "errors.c") */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -16,70 +15,81 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ #include "header.h" -int32 malloced_bytes=0; /* Total amount of memory allocated */ +size_t malloced_bytes=0; /* Total amount of memory allocated */ + +/* Wrappers for malloc(), realloc(), etc. + + Note that all of these functions call memory_out_error() on failure. + This is a fatal error and does not return. However, we check my_malloc() + return values anyway as a matter of good habit. + */ #ifdef PC_QUICKC -extern void *my_malloc(int32 size, char *whatfor) +extern void *my_malloc(size_t size, char *whatfor) { char _huge *c; if (memout_switch) printf("Allocating %ld bytes for %s\n",size,whatfor); if (size==0) return(NULL); - c=(char _huge *)halloc(size,1); malloced_bytes+=size; + c=(char _huge *)halloc(size,1); + malloced_bytes+=size; if (c==0) memory_out_error(size, 1, whatfor); return(c); } -extern void my_realloc(void *pointer, int32 oldsize, int32 size, +extern void my_realloc(void *pointer, size_t oldsize, size_t size, char *whatfor) { char _huge *c; if (size==0) { my_free(pointer, whatfor); return; } - c=halloc(size,1); malloced_bytes+=size; + c=halloc(size,1); + malloced_bytes+=(size-oldsize); if (c==0) memory_out_error(size, 1, whatfor); if (memout_switch) - printf("Increasing allocation to %ld bytes for %s was (%08lx) \ -now (%08lx)\n", - (long int) size,whatfor,(long int) (*(int **)pointer), + printf("Increasing allocation from %ld to %ld bytes for %s was (%08lx) now (%08lx)\n", + (long int) oldsize, (long int) size, whatfor, + (long int) (*(int **)pointer), (long int) c); memcpy(c, *(int **)pointer, MIN(oldsize, size)); hfree(*(int **)pointer); *(int **)pointer = c; } -extern void *my_calloc(int32 size, int32 howmany, char *whatfor) +extern void *my_calloc(size_t size, size_t howmany, char *whatfor) { void _huge *c; if (memout_switch) printf("Allocating %d bytes: array (%ld entries size %ld) for %s\n", size*howmany,howmany,size,whatfor); if ((size*howmany) == 0) return(NULL); - c=(void _huge *)halloc(howmany*size,1); malloced_bytes+=size*howmany; + c=(void _huge *)halloc(howmany*size,1); + malloced_bytes+=size*howmany; if (c==0) memory_out_error(size, howmany, whatfor); return(c); } -extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany, +extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, int32 howmany, char *whatfor) { void _huge *c; if (size*howmany==0) { my_free(pointer, whatfor); return; } - c=(void _huge *)halloc(size*howmany,1); malloced_bytes+=size*howmany; + c=(void _huge *)halloc(size*howmany,1); + malloced_bytes+=size*(howmany-oldhowmany); if (c==0) memory_out_error(size, howmany, whatfor); if (memout_switch) - printf("Increasing allocation to %ld bytes: array (%ld entries size %ld) \ -for %s was (%08lx) now (%08lx)\n", + printf("Increasing allocation from %ld to %ld bytes: array (%ld entries size %ld) for %s was (%08lx) now (%08lx)\n", + ((long int)size) * ((long int)oldhowmany), ((long int)size) * ((long int)howmany), - (long int)howmany,(long int)size,whatfor, + (long int)howmany, (long int)size, whatfor, (long int) *(int **)pointer, (long int) c); memcpy(c, *(int **)pointer, MIN(size*oldhowmany, size*howmany)); hfree(*(int **)pointer); @@ -88,10 +98,11 @@ for %s was (%08lx) now (%08lx)\n", #else -extern void *my_malloc(int32 size, char *whatfor) +extern void *my_malloc(size_t size, char *whatfor) { char *c; if (size==0) return(NULL); - c=malloc((size_t) size); malloced_bytes+=size; + c=malloc(size); + malloced_bytes+=size; if (c==0) memory_out_error(size, 1, whatfor); if (memout_switch) printf("Allocating %ld bytes for %s at (%08lx)\n", @@ -99,27 +110,29 @@ extern void *my_malloc(int32 size, char *whatfor) return(c); } -extern void my_realloc(void *pointer, int32 oldsize, int32 size, +extern void my_realloc(void *pointer, size_t oldsize, size_t size, char *whatfor) { void *c; if (size==0) { my_free(pointer, whatfor); return; } - c=realloc(*(int **)pointer, (size_t) size); malloced_bytes+=size; + c=realloc(*(int **)pointer, size); + malloced_bytes+=(size-oldsize); if (c==0) memory_out_error(size, 1, whatfor); if (memout_switch) - printf("Increasing allocation to %ld bytes for %s was (%08lx) \ -now (%08lx)\n", - (long int) size,whatfor,(long int) (*(int **)pointer), + printf("Increasing allocation from %ld to %ld bytes for %s was (%08lx) now (%08lx)\n", + (long int) oldsize, (long int) size, whatfor, + (long int) (*(int **)pointer), (long int) c); *(int **)pointer = c; } -extern void *my_calloc(int32 size, int32 howmany, char *whatfor) +extern void *my_calloc(size_t size, size_t howmany, char *whatfor) { void *c; if (size*howmany==0) return(NULL); - c=calloc(howmany,(size_t) size); malloced_bytes+=size*howmany; + c=calloc(howmany, size); + malloced_bytes+=size*howmany; if (c==0) memory_out_error(size, howmany, whatfor); if (memout_switch) printf("Allocating %ld bytes: array (%ld entries size %ld) \ @@ -130,21 +143,21 @@ for %s at (%08lx)\n", return(c); } -extern void my_recalloc(void *pointer, int32 size, int32 oldhowmany, - int32 howmany, char *whatfor) +extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, + size_t howmany, char *whatfor) { void *c; if (size*howmany==0) { my_free(pointer, whatfor); return; } - c=realloc(*(int **)pointer, (size_t)size*(size_t)howmany); - malloced_bytes+=size*howmany; + c=realloc(*(int **)pointer, size*howmany); + malloced_bytes+=size*(howmany-oldhowmany); if (c==0) memory_out_error(size, howmany, whatfor); if (memout_switch) - printf("Increasing allocation to %ld bytes: array (%ld entries size %ld) \ -for %s was (%08lx) now (%08lx)\n", + printf("Increasing allocation from %ld to %ld bytes: array (%ld entries size %ld) for %s was (%08lx) now (%08lx)\n", + ((long int)size) * ((long int)oldhowmany), ((long int)size) * ((long int)howmany), - (long int)howmany,(long int)size,whatfor, + (long int)howmany, (long int)size, whatfor, (long int) *(int **)pointer, (long int) c); *(int **)pointer = c; } @@ -167,140 +180,118 @@ extern void my_free(void *pointer, char *whatitwas) } /* ------------------------------------------------------------------------- */ -/* Extensible blocks of memory, providing a kind of RAM disc as an */ -/* alternative to the temporary files option */ +/* A dynamic memory array. This grows as needed (but never shrinks). */ +/* Call ensure_memory_list_available(N) before accessing array item N-1. */ +/* */ +/* whatfor must be a static string describing the list. initalloc is */ +/* (optionally) the number of items to allocate right away. */ /* */ -/* The allocation is slightly confusing. A block can store up to 72 */ -/* chunks, which are allocated as needed when data is written. (Data does */ -/* not have to be written in order, but you should not try to read a byte */ -/* before writing it.) The size of a chunk is defined by ALLOC_CHUNK_SIZE. */ -/* So any block can store any amount of data, but you increase the limit */ -/* (for all blocks) by increasing ALLOC_CHUNK_SIZE, not the number of */ -/* chunks. */ +/* You typically initialise this with extpointer referring to an array of */ +/* structs or whatever type you need. Whenever the memory list grows, the */ +/* external array will be updated to refer to the new data. */ +/* */ +/* Add "#define DEBUG_MEMLISTS" to allocate exactly the number of items */ +/* needed, rather than increasing allocations exponentially. This is very */ +/* slow but it lets us track down array overruns. */ /* ------------------------------------------------------------------------- */ -static char chunk_name_buffer[60]; -static char *chunk_name(memory_block *MB, int no) -{ char *p = "(unknown)"; - if (MB == &static_strings_area) p = "static strings area"; - if (MB == &zcode_area) p = "Z-code area"; - if (MB == &link_data_area) p = "link data area"; - if (MB == &zcode_backpatch_table) p = "Z-code backpatch table"; - if (MB == &staticarray_backpatch_table) p = "Static array backpatch table"; - if (MB == &zmachine_backpatch_table) p = "Z-machine backpatch table"; - sprintf(chunk_name_buffer, "%s chunk %d", p, no); - return(chunk_name_buffer); -} +void initialise_memory_list(memory_list *ML, size_t itemsize, size_t initalloc, void **extpointer, char *whatfor) +{ + #ifdef DEBUG_MEMLISTS + initalloc = 0; /* No initial allocation */ + #endif + + ML->whatfor = whatfor; + ML->itemsize = itemsize; + ML->count = 0; + ML->data = NULL; + ML->extpointer = extpointer; -extern void initialise_memory_block(memory_block *MB) -{ int i; - MB->chunks = 0; - for (i=0; i<72; i++) MB->chunk[i] = NULL; - MB->extent_of_last = 0; - MB->write_pos = 0; -} + if (initalloc) { + ML->count = initalloc; + ML->data = my_calloc(ML->itemsize, ML->count, ML->whatfor); + if (ML->data == NULL) return; + } -extern void deallocate_memory_block(memory_block *MB) -{ int i; - for (i=0; i<72; i++) - if (MB->chunk[i] != NULL) - my_free(&(MB->chunk[i]), chunk_name(MB, i)); - MB->chunks = 0; - MB->extent_of_last = 0; + if (ML->extpointer) + *(ML->extpointer) = ML->data; } -extern int read_byte_from_memory_block(memory_block *MB, int32 index) -{ uchar *p; - p = MB->chunk[index/ALLOC_CHUNK_SIZE]; - if (p == NULL) - { compiler_error_named("memory: read from unwritten byte in", - chunk_name(MB, index/ALLOC_CHUNK_SIZE)); - return 0; - } - return p[index % ALLOC_CHUNK_SIZE]; +void deallocate_memory_list(memory_list *ML) +{ + ML->itemsize = 0; + ML->count = 0; + + if (ML->data) + my_free(&(ML->data), ML->whatfor); + + if (ML->extpointer) + *(ML->extpointer) = NULL; + ML->extpointer = NULL; } -extern void write_byte_to_memory_block(memory_block *MB, int32 index, int value) -{ uchar *p; int ch = index/ALLOC_CHUNK_SIZE; - if (ch < 0) - { compiler_error_named("memory: negative index to", chunk_name(MB, 0)); +/* After this is called, at least count items will be available in the list. + That is, you can freely access array[0] through array[count-1]. */ +void ensure_memory_list_available(memory_list *ML, size_t count) +{ + size_t oldcount; + + if (ML->itemsize == 0) { + /* whatfor is also null! */ + compiler_error("memory: attempt to access uninitialized memory_list"); return; } - if (ch >= 72) memoryerror("ALLOC_CHUNK_SIZE", ALLOC_CHUNK_SIZE); - if (MB->chunk[ch] == NULL) - { int i; - MB->chunk[ch] = my_malloc(ALLOC_CHUNK_SIZE, chunk_name(MB, ch)); - p = MB->chunk[ch]; - for (i=0; icount >= count) { + return; } - p = MB->chunk[ch]; - p[index % ALLOC_CHUNK_SIZE] = value; + oldcount = ML->count; + ML->count = 2*count+8; /* Allow headroom for future growth */ + + #ifdef DEBUG_MEMLISTS + ML->count = count; /* No headroom */ + #endif + + if (ML->data == NULL) + ML->data = my_calloc(ML->itemsize, ML->count, ML->whatfor); + else + my_recalloc(&(ML->data), ML->itemsize, oldcount, ML->count, ML->whatfor); + if (ML->data == NULL) return; + + if (ML->extpointer) + *(ML->extpointer) = ML->data; } /* ------------------------------------------------------------------------- */ /* Where the memory settings are declared as variables */ /* ------------------------------------------------------------------------- */ -int MAX_QTEXT_SIZE; -int MAX_SYMBOLS; -int SYMBOLS_CHUNK_SIZE; int HASH_TAB_SIZE; -int MAX_OBJECTS; -int MAX_ARRAYS; -int MAX_ACTIONS; -int MAX_ADJECTIVES; -int MAX_DICT_ENTRIES; -int MAX_STATIC_DATA; -int MAX_PROP_TABLE_SIZE; int MAX_ABBREVS; int MAX_DYNAMIC_STRINGS; -int MAX_EXPRESSION_NODES; -int MAX_VERBS; -int MAX_VERBSPACE; -int MAX_LABELS; -int MAX_LINESPACE; -int32 MAX_STATIC_STRINGS; -int32 MAX_ZCODE_SIZE; -int MAX_LOW_STRINGS; -int32 MAX_TRANSCRIPT_SIZE; -int MAX_CLASSES; -int32 MAX_LINK_DATA_SIZE; -int MAX_INCLUSION_DEPTH; -int MAX_SOURCE_FILES; -int32 MAX_INDIV_PROP_TABLE_SIZE; -int32 MAX_OBJ_PROP_TABLE_SIZE; -int MAX_OBJ_PROP_COUNT; int MAX_LOCAL_VARIABLES; -int MAX_GLOBAL_VARIABLES; int DICT_WORD_SIZE; /* number of characters in a dict word */ int DICT_CHAR_SIZE; /* (glulx) 1 for one-byte chars, 4 for Unicode chars */ int DICT_WORD_BYTES; /* DICT_WORD_SIZE*DICT_CHAR_SIZE */ int ZCODE_HEADER_EXT_WORDS; /* (zcode 1.0) requested header extension size */ int ZCODE_HEADER_FLAGS_3; /* (zcode 1.1) value to place in Flags 3 word */ +int ZCODE_LESS_DICT_DATA; /* (zcode) use 2 data bytes per dict word instead of 3 */ int NUM_ATTR_BYTES; int GLULX_OBJECT_EXT_BYTES; /* (glulx) extra bytes for each object record */ -int32 MAX_NUM_STATIC_STRINGS; -int32 MAX_UNICODE_CHARS; int32 MAX_STACK_SIZE; int32 MEMORY_MAP_EXTENSION; -int ALLOC_CHUNK_SIZE; int WARN_UNUSED_ROUTINES; /* 0: no, 1: yes except in system files, 2: yes always */ int OMIT_UNUSED_ROUTINES; /* 0: no, 1: yes */ +int STRIP_UNREACHABLE_LABELS; /* 0: no, 1: yes (default) */ int TRANSCRIPT_FORMAT; /* 0: classic, 1: prefixed */ /* The way memory sizes are set causes great nuisance for those parameters which have different defaults under Z-code and Glulx. We have to get the defaults right whether the user sets "-G $HUGE" or "$HUGE -G". And an explicit value set by the user should override both defaults. */ -static int32 MAX_ZCODE_SIZE_z, MAX_ZCODE_SIZE_g; -static int MAX_PROP_TABLE_SIZE_z, MAX_PROP_TABLE_SIZE_g; -static int MAX_GLOBAL_VARIABLES_z, MAX_GLOBAL_VARIABLES_g; -static int MAX_LOCAL_VARIABLES_z, MAX_LOCAL_VARIABLES_g; static int DICT_WORD_SIZE_z, DICT_WORD_SIZE_g; static int NUM_ATTR_BYTES_z, NUM_ATTR_BYTES_g; -static int ALLOC_CHUNK_SIZE_z, ALLOC_CHUNK_SIZE_g; static int MAX_DYNAMIC_STRINGS_z, MAX_DYNAMIC_STRINGS_g; /* ------------------------------------------------------------------------- */ @@ -312,227 +303,38 @@ static void list_memory_sizes(void) printf("| %25s = %-7s |\n","Memory setting","Value"); printf("+--------------------------------------+\n"); printf("| %25s = %-7d |\n","MAX_ABBREVS",MAX_ABBREVS); - printf("| %25s = %-7d |\n","MAX_ACTIONS",MAX_ACTIONS); - printf("| %25s = %-7d |\n","MAX_ADJECTIVES",MAX_ADJECTIVES); - printf("| %25s = %-7d |\n","ALLOC_CHUNK_SIZE",ALLOC_CHUNK_SIZE); - printf("| %25s = %-7d |\n","MAX_ARRAYS",MAX_ARRAYS); printf("| %25s = %-7d |\n","NUM_ATTR_BYTES",NUM_ATTR_BYTES); - printf("| %25s = %-7d |\n","MAX_CLASSES",MAX_CLASSES); - printf("| %25s = %-7d |\n","MAX_DICT_ENTRIES",MAX_DICT_ENTRIES); printf("| %25s = %-7d |\n","DICT_WORD_SIZE",DICT_WORD_SIZE); if (glulx_mode) printf("| %25s = %-7d |\n","DICT_CHAR_SIZE",DICT_CHAR_SIZE); printf("| %25s = %-7d |\n","MAX_DYNAMIC_STRINGS",MAX_DYNAMIC_STRINGS); - printf("| %25s = %-7d |\n","MAX_EXPRESSION_NODES",MAX_EXPRESSION_NODES); - printf("| %25s = %-7d |\n","MAX_GLOBAL_VARIABLES",MAX_GLOBAL_VARIABLES); printf("| %25s = %-7d |\n","HASH_TAB_SIZE",HASH_TAB_SIZE); if (!glulx_mode) printf("| %25s = %-7d |\n","ZCODE_HEADER_EXT_WORDS",ZCODE_HEADER_EXT_WORDS); if (!glulx_mode) printf("| %25s = %-7d |\n","ZCODE_HEADER_FLAGS_3",ZCODE_HEADER_FLAGS_3); - printf("| %25s = %-7d |\n","MAX_INCLUSION_DEPTH",MAX_INCLUSION_DEPTH); - printf("| %25s = %-7d |\n","MAX_INDIV_PROP_TABLE_SIZE", MAX_INDIV_PROP_TABLE_SIZE); + if (!glulx_mode) + printf("| %25s = %-7d |\n","ZCODE_LESS_DICT_DATA",ZCODE_LESS_DICT_DATA); printf("| %25s = %-7d |\n","INDIV_PROP_START", INDIV_PROP_START); - printf("| %25s = %-7d |\n","MAX_LABELS",MAX_LABELS); - printf("| %25s = %-7d |\n","MAX_LINESPACE",MAX_LINESPACE); - printf("| %25s = %-7d |\n","MAX_LINK_DATA_SIZE",MAX_LINK_DATA_SIZE); - if (glulx_mode) - printf("| %25s = %-7d |\n","MAX_LOCAL_VARIABLES",MAX_LOCAL_VARIABLES); - printf("| %25s = %-7d |\n","MAX_LOW_STRINGS",MAX_LOW_STRINGS); if (glulx_mode) printf("| %25s = %-7d |\n","MEMORY_MAP_EXTENSION", MEMORY_MAP_EXTENSION); - if (glulx_mode) - printf("| %25s = %-7d |\n","MAX_NUM_STATIC_STRINGS", - MAX_NUM_STATIC_STRINGS); - printf("| %25s = %-7d |\n","MAX_OBJECTS",MAX_OBJECTS); if (glulx_mode) printf("| %25s = %-7d |\n","GLULX_OBJECT_EXT_BYTES", GLULX_OBJECT_EXT_BYTES); - if (glulx_mode) - printf("| %25s = %-7d |\n","MAX_OBJ_PROP_COUNT", - MAX_OBJ_PROP_COUNT); - if (glulx_mode) - printf("| %25s = %-7d |\n","MAX_OBJ_PROP_TABLE_SIZE", - MAX_OBJ_PROP_TABLE_SIZE); - printf("| %25s = %-7d |\n","MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); - printf("| %25s = %-7d |\n","MAX_QTEXT_SIZE",MAX_QTEXT_SIZE); - printf("| %25s = %-7d |\n","MAX_SOURCE_FILES",MAX_SOURCE_FILES); if (glulx_mode) printf("| %25s = %-7ld |\n","MAX_STACK_SIZE", (long int) MAX_STACK_SIZE); - printf("| %25s = %-7d |\n","MAX_STATIC_DATA",MAX_STATIC_DATA); - printf("| %25s = %-7ld |\n","MAX_STATIC_STRINGS", - (long int) MAX_STATIC_STRINGS); - printf("| %25s = %-7d |\n","MAX_SYMBOLS",MAX_SYMBOLS); - printf("| %25s = %-7d |\n","SYMBOLS_CHUNK_SIZE",SYMBOLS_CHUNK_SIZE); printf("| %25s = %-7d |\n","TRANSCRIPT_FORMAT",TRANSCRIPT_FORMAT); - printf("| %25s = %-7ld |\n","MAX_TRANSCRIPT_SIZE", - (long int) MAX_TRANSCRIPT_SIZE); - if (glulx_mode) - printf("| %25s = %-7ld |\n","MAX_UNICODE_CHARS", - (long int) MAX_UNICODE_CHARS); printf("| %25s = %-7d |\n","WARN_UNUSED_ROUTINES",WARN_UNUSED_ROUTINES); printf("| %25s = %-7d |\n","OMIT_UNUSED_ROUTINES",OMIT_UNUSED_ROUTINES); - printf("| %25s = %-7d |\n","MAX_VERBS",MAX_VERBS); - printf("| %25s = %-7d |\n","MAX_VERBSPACE",MAX_VERBSPACE); - printf("| %25s = %-7ld |\n","MAX_ZCODE_SIZE", - (long int) MAX_ZCODE_SIZE); + printf("| %25s = %-7d |\n","STRIP_UNREACHABLE_LABELS",STRIP_UNREACHABLE_LABELS); printf("+--------------------------------------+\n"); } -extern void set_memory_sizes(int size_flag) +extern void set_memory_sizes(void) { - if (size_flag == HUGE_SIZE) - { - MAX_QTEXT_SIZE = 4000; - MAX_SYMBOLS = 10000; - - SYMBOLS_CHUNK_SIZE = 5000; - HASH_TAB_SIZE = 512; - - MAX_OBJECTS = 640; - - MAX_ACTIONS = 200; - MAX_ADJECTIVES = 50; - MAX_DICT_ENTRIES = 2000; - MAX_STATIC_DATA = 10000; - - MAX_PROP_TABLE_SIZE_z = 30000; - MAX_PROP_TABLE_SIZE_g = 60000; - - MAX_EXPRESSION_NODES = 100; - MAX_VERBS = 200; - MAX_VERBSPACE = 4096; - MAX_LABELS = 1000; - MAX_LINESPACE = 16000; - - MAX_STATIC_STRINGS = 8000; - MAX_ZCODE_SIZE_z = 20000; - MAX_ZCODE_SIZE_g = 40000; - MAX_LINK_DATA_SIZE = 2000; - - MAX_LOW_STRINGS = 2048; - - MAX_TRANSCRIPT_SIZE = 200000; - MAX_NUM_STATIC_STRINGS = 20000; - - MAX_CLASSES = 64; - - MAX_OBJ_PROP_COUNT = 128; - MAX_OBJ_PROP_TABLE_SIZE = 4096; - - MAX_INDIV_PROP_TABLE_SIZE = 15000; - MAX_ARRAYS = 128; - - MAX_GLOBAL_VARIABLES_z = 240; - MAX_GLOBAL_VARIABLES_g = 512; - - ALLOC_CHUNK_SIZE_z = 8192; - ALLOC_CHUNK_SIZE_g = 32768; - } - if (size_flag == LARGE_SIZE) - { - MAX_QTEXT_SIZE = 4000; - MAX_SYMBOLS = 6400; - - SYMBOLS_CHUNK_SIZE = 5000; - HASH_TAB_SIZE = 512; - - MAX_OBJECTS = 512; - - MAX_ACTIONS = 200; - MAX_ADJECTIVES = 50; - MAX_DICT_ENTRIES = 1300; - MAX_STATIC_DATA = 10000; - - MAX_PROP_TABLE_SIZE_z = 15000; - MAX_PROP_TABLE_SIZE_g = 30000; - - MAX_EXPRESSION_NODES = 100; - MAX_VERBS = 140; - MAX_VERBSPACE = 4096; - MAX_LINESPACE = 10000; - - MAX_LABELS = 1000; - MAX_STATIC_STRINGS = 8000; - MAX_ZCODE_SIZE_z = 20000; - MAX_ZCODE_SIZE_g = 40000; - MAX_LINK_DATA_SIZE = 2000; - - MAX_LOW_STRINGS = 2048; - - MAX_TRANSCRIPT_SIZE = 200000; - MAX_NUM_STATIC_STRINGS = 20000; - - MAX_CLASSES = 64; - - MAX_OBJ_PROP_COUNT = 64; - MAX_OBJ_PROP_TABLE_SIZE = 2048; - - MAX_INDIV_PROP_TABLE_SIZE = 10000; - MAX_ARRAYS = 128; - - MAX_GLOBAL_VARIABLES_z = 240; - MAX_GLOBAL_VARIABLES_g = 512; - - ALLOC_CHUNK_SIZE_z = 8192; - ALLOC_CHUNK_SIZE_g = 16384; - } - if (size_flag == SMALL_SIZE) - { - MAX_QTEXT_SIZE = 4000; - MAX_SYMBOLS = 3000; - - SYMBOLS_CHUNK_SIZE = 2500; - HASH_TAB_SIZE = 512; - - MAX_OBJECTS = 300; - - MAX_ACTIONS = 200; - MAX_ADJECTIVES = 50; - MAX_DICT_ENTRIES = 700; - MAX_STATIC_DATA = 10000; - - MAX_PROP_TABLE_SIZE_z = 8000; - MAX_PROP_TABLE_SIZE_g = 16000; - - MAX_EXPRESSION_NODES = 40; - MAX_VERBS = 110; - MAX_VERBSPACE = 2048; - MAX_LINESPACE = 10000; - MAX_LABELS = 1000; - - MAX_STATIC_STRINGS = 8000; - MAX_ZCODE_SIZE_z = 10000; - MAX_ZCODE_SIZE_g = 20000; - MAX_LINK_DATA_SIZE = 1000; - - MAX_LOW_STRINGS = 1024; - - MAX_TRANSCRIPT_SIZE = 100000; - MAX_NUM_STATIC_STRINGS = 10000; - - MAX_CLASSES = 32; - - MAX_OBJ_PROP_COUNT = 64; - MAX_OBJ_PROP_TABLE_SIZE = 1024; - - MAX_INDIV_PROP_TABLE_SIZE = 5000; - MAX_ARRAYS = 64; - - MAX_GLOBAL_VARIABLES_z = 240; - MAX_GLOBAL_VARIABLES_g = 256; - - ALLOC_CHUNK_SIZE_z = 8192; - ALLOC_CHUNK_SIZE_g = 8192; - } - - /* Regardless of size_flag... */ - MAX_SOURCE_FILES = 256; - MAX_INCLUSION_DEPTH = 5; - MAX_LOCAL_VARIABLES_z = 16; - MAX_LOCAL_VARIABLES_g = 32; + HASH_TAB_SIZE = 512; DICT_CHAR_SIZE = 1; DICT_WORD_SIZE_z = 6; DICT_WORD_SIZE_g = 9; @@ -540,14 +342,14 @@ extern void set_memory_sizes(int size_flag) NUM_ATTR_BYTES_g = 7; MAX_ABBREVS = 64; MAX_DYNAMIC_STRINGS_z = 32; - MAX_DYNAMIC_STRINGS_g = 64; + MAX_DYNAMIC_STRINGS_g = 100; /* Backwards-compatible behavior: allow for a unicode table whether we need one or not. The user can set this to zero if there's no unicode table. */ ZCODE_HEADER_EXT_WORDS = 3; ZCODE_HEADER_FLAGS_3 = 0; + ZCODE_LESS_DICT_DATA = 0; GLULX_OBJECT_EXT_BYTES = 0; - MAX_UNICODE_CHARS = 64; MEMORY_MAP_EXTENSION = 0; /* We estimate the default Glulx stack size at 4096. That's about enough for 90 nested function calls with 8 locals each -- the @@ -557,6 +359,7 @@ extern void set_memory_sizes(int size_flag) MAX_STACK_SIZE = 4096; OMIT_UNUSED_ROUTINES = 0; WARN_UNUSED_ROUTINES = 0; + STRIP_UNREACHABLE_LABELS = 1; TRANSCRIPT_FORMAT = 0; adjust_memory_sizes(); @@ -565,24 +368,14 @@ extern void set_memory_sizes(int size_flag) extern void adjust_memory_sizes() { if (!glulx_mode) { - MAX_ZCODE_SIZE = MAX_ZCODE_SIZE_z; - MAX_PROP_TABLE_SIZE = MAX_PROP_TABLE_SIZE_z; - MAX_GLOBAL_VARIABLES = MAX_GLOBAL_VARIABLES_z; - MAX_LOCAL_VARIABLES = MAX_LOCAL_VARIABLES_z; DICT_WORD_SIZE = DICT_WORD_SIZE_z; NUM_ATTR_BYTES = NUM_ATTR_BYTES_z; - ALLOC_CHUNK_SIZE = ALLOC_CHUNK_SIZE_z; MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_z; INDIV_PROP_START = 64; } else { - MAX_ZCODE_SIZE = MAX_ZCODE_SIZE_g; - MAX_PROP_TABLE_SIZE = MAX_PROP_TABLE_SIZE_g; - MAX_GLOBAL_VARIABLES = MAX_GLOBAL_VARIABLES_g; - MAX_LOCAL_VARIABLES = MAX_LOCAL_VARIABLES_g; DICT_WORD_SIZE = DICT_WORD_SIZE_g; NUM_ATTR_BYTES = NUM_ATTR_BYTES_g; - ALLOC_CHUNK_SIZE = ALLOC_CHUNK_SIZE_g; MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_g; INDIV_PROP_START = 256; } @@ -590,56 +383,12 @@ extern void adjust_memory_sizes() static void explain_parameter(char *command) { printf("\n"); - if (strcmp(command,"MAX_QTEXT_SIZE")==0) - { printf( -" MAX_QTEXT_SIZE is the maximum length of a quoted string. Increasing\n\ - by 1 costs 5 bytes (for lexical analysis memory). Inform automatically\n\ - ensures that MAX_STATIC_STRINGS is at least twice the size of this."); - return; - } - if (strcmp(command,"MAX_SYMBOLS")==0) - { printf( -" MAX_SYMBOLS is the maximum number of symbols - names of variables, \n\ - objects, routines, the many internal Inform-generated names and so on.\n"); - return; - } - if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0) - { printf( -" The symbols names are stored in memory which is allocated in chunks \n\ - of size SYMBOLS_CHUNK_SIZE.\n"); - return; - } if (strcmp(command,"HASH_TAB_SIZE")==0) { printf( " HASH_TAB_SIZE is the size of the hash tables used for the heaviest \n\ symbols banks.\n"); return; } - if (strcmp(command,"MAX_OBJECTS")==0) - { printf( -" MAX_OBJECTS is the maximum number of objects. (If compiling a version-3 \n\ - game, 255 is an absolute maximum in any event.)\n"); - return; - } - if (strcmp(command,"MAX_ACTIONS")==0) - { printf( -" MAX_ACTIONS is the maximum number of actions - that is, routines such as \n\ - TakeSub which are referenced in the grammar table.\n"); - return; - } - if (strcmp(command,"MAX_ADJECTIVES")==0) - { printf( -" MAX_ADJECTIVES is the maximum number of different \"adjectives\" in the \n\ - grammar table. Adjectives are misleadingly named: they are words such as \n\ - \"in\", \"under\" and the like.\n"); - return; - } - if (strcmp(command,"MAX_DICT_ENTRIES")==0) - { printf( -" MAX_DICT_ENTRIES is the maximum number of words which can be entered \n\ - into the game's dictionary. It costs 29 bytes to increase this by one.\n"); - return; - } if (strcmp(command,"DICT_WORD_SIZE")==0) { printf( " DICT_WORD_SIZE is the number of characters in a dictionary word. In \n\ @@ -677,6 +426,12 @@ static void explain_parameter(char *command) header extension table (Z-Spec 1.1).\n"); return; } + if (strcmp(command,"ZCODE_LESS_DICT_DATA")==0) + { printf( +" ZCODE_LESS_DICT_DATA, if set, provides each dict word with two data bytes\n\ + rather than three. (Z-code only.)\n"); + return; + } if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0) { printf( " GLULX_OBJECT_EXT_BYTES is an amount of additional space to add to each \n\ @@ -685,134 +440,17 @@ static void explain_parameter(char *command) specifies the object structure.)\n"); return; } - if (strcmp(command,"MAX_STATIC_DATA")==0) - { printf( -" MAX_STATIC_DATA is the size of an array of integers holding initial \n\ - values for arrays and strings stored as ASCII inside the Z-machine. It \n\ - should be at least 1024 but seldom needs much more.\n"); - return; - } - if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0) - { printf( -" MAX_PROP_TABLE_SIZE is the number of bytes allocated to hold the \n\ - properties table.\n"); - return; - } if (strcmp(command,"MAX_ABBREVS")==0) { printf( " MAX_ABBREVS is the maximum number of declared abbreviations. It is not \n\ - allowed to exceed 96 in Z-code.\n"); + allowed to exceed 96 in Z-code. (This is not meaningful in Glulx, where \n\ + there is no limit on abbreviations.)\n"); return; } if (strcmp(command,"MAX_DYNAMIC_STRINGS")==0) { printf( " MAX_DYNAMIC_STRINGS is the maximum number of string substitution variables\n\ - (\"@00\"). It is not allowed to exceed 96 in Z-code or 100 in Glulx.\n"); - return; - } - if (strcmp(command,"MAX_ARRAYS")==0) - { printf( -" MAX_ARRAYS is the maximum number of declared arrays.\n"); - return; - } - if (strcmp(command,"MAX_EXPRESSION_NODES")==0) - { printf( -" MAX_EXPRESSION_NODES is the maximum number of nodes in the expression \n\ - evaluator's storage for parse trees. In effect, it measures how \n\ - complicated algebraic expressions are allowed to be. Increasing it by \n\ - one costs about 80 bytes.\n"); - return; - } - if (strcmp(command,"MAX_VERBS")==0) - { printf( -" MAX_VERBS is the maximum number of verbs (such as \"take\") which can be \n\ - defined, each with its own grammar. To increase it by one costs about\n\ - 128 bytes. A full game will contain at least 100.\n"); - return; - } - if (strcmp(command,"MAX_VERBSPACE")==0) - { printf( -" MAX_VERBSPACE is the size of workspace used to store verb words, so may\n\ - need increasing in games with many synonyms: unlikely to exceed 4K.\n"); - return; - } - if (strcmp(command,"MAX_LABELS")==0) - { printf( -" MAX_LABELS is the maximum number of label points in any one routine.\n\ - (If the -k debugging information switch is set, MAX_LABELS is raised to\n\ - a minimum level of 2000, as about twice the normal number of label points\n\ - are needed to generate tables of how source code corresponds to positions\n\ - in compiled code.)"); - return; - } - if (strcmp(command,"MAX_LINESPACE")==0) - { printf( -" MAX_LINESPACE is the size of workspace used to store grammar lines, so \n\ - may need increasing in games with complex or extensive grammars.\n"); - return; - } - if (strcmp(command,"MAX_STATIC_STRINGS")==0) - { - printf( -" MAX_STATIC_STRINGS is the size in bytes of a buffer to hold compiled\n\ - strings before they're written into longer-term storage. 2000 bytes is \n\ - plenty, allowing string constants of up to about 3000 characters long.\n\ - Inform automatically ensures that this is at least twice the size of\n\ - MAX_QTEXT_SIZE, to be on the safe side."); - return; - } - if (strcmp(command,"MAX_ZCODE_SIZE")==0) - { - printf( -" MAX_ZCODE_SIZE is the size in bytes of a buffer to hold compiled \n\ - code for a single routine. (It applies to both Z-code and Glulx, \n\ - despite the name.) As a guide, the longest library routine is \n\ - about 6500 bytes long in Z-code; about twice that in Glulx."); - return; - } - if (strcmp(command,"MAX_LINK_DATA_SIZE")==0) - { - printf( -" MAX_LINK_DATA_SIZE is the size in bytes of a buffer to hold module \n\ - link data before it's written into longer-term storage. 2000 bytes \n\ - is plenty."); - return; - } - if (strcmp(command,"MAX_LOW_STRINGS")==0) - { printf( -" MAX_LOW_STRINGS is the size in bytes of a buffer to hold all the \n\ - compiled \"low strings\" which are to be written above the synonyms table \n\ - in the Z-machine. 1024 is plenty.\n"); - return; - } - if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0) - { printf( -" MAX_TRANSCRIPT_SIZE is only allocated for the abbreviations optimisation \n\ - switch, and has the size in bytes of a buffer to hold the entire text of\n\ - the game being compiled: it has to be enormous, say 100000 to 200000.\n"); - return; - } - if (strcmp(command,"MAX_CLASSES")==0) - { printf( -" MAX_CLASSES maximum number of object classes which can be defined. This\n\ - is cheap to increase.\n"); - return; - } - if (strcmp(command,"MAX_INCLUSION_DEPTH")==0) - { printf( -" MAX_INCLUSION_DEPTH is the number of nested includes permitted.\n"); - return; - } - if (strcmp(command,"MAX_SOURCE_FILES")==0) - { printf( -" MAX_SOURCE_FILES is the number of source files that can be read in the \n\ - compilation.\n"); - return; - } - if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0) - { printf( -" MAX_INDIV_PROP_TABLE_SIZE is the number of bytes allocated to hold the \n\ - table of ..variable values.\n"); + (\"@00\" or \"@(0)\"). It is not allowed to exceed 96 in Z-code.\n"); return; } if (strcmp(command,"INDIV_PROP_START")==0) @@ -821,52 +459,6 @@ static void explain_parameter(char *command) properties are numbered INDIV_PROP_START and up.\n"); return; } - if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0) - { printf( -" MAX_OBJ_PROP_COUNT is the maximum number of properties a single object \n\ - can have. (Glulx only)\n"); - return; - } - if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0) - { printf( -" MAX_OBJ_PROP_TABLE_SIZE is the number of words allocated to hold a \n\ - single object's properties. (Glulx only)\n"); - return; - } - if (strcmp(command,"MAX_LOCAL_VARIABLES")==0) - { printf( -" MAX_LOCAL_VARIABLES is the number of local variables (including \n\ - arguments) allowed in a procedure. (Glulx only)\n"); - return; - } - if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0) - { printf( -" MAX_GLOBAL_VARIABLES is the number of global variables allowed in the \n\ - program. (Glulx only)\n"); - return; - } - if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0) - { - printf( -" MAX_NUM_STATIC_STRINGS is the maximum number of compiled strings \n\ - allowed in the program. (Glulx only)\n"); - return; - } - if (strcmp(command,"MAX_UNICODE_CHARS")==0) - { - printf( -" MAX_UNICODE_CHARS is the maximum number of different Unicode characters \n\ - (beyond the Latin-1 range, $00..$FF) which the game text can use. \n\ - (Glulx only)\n"); - return; - } - if (strcmp(command,"ALLOC_CHUNK_SIZE")==0) - { - printf( -" ALLOC_CHUNK_SIZE is a base unit of Inform's internal memory allocation \n\ - for various structures.\n"); - return; - } if (strcmp(command,"MAX_STACK_SIZE")==0) { printf( @@ -904,6 +496,14 @@ static void explain_parameter(char *command) into the game file.\n"); return; } + if (strcmp(command,"STRIP_UNREACHABLE_LABELS")==0) + { + printf( +" STRIP_UNREACHABLE_LABELS, if set to 1, will skip labels in unreachable \n\ + statements. Jumping to a skipped label is an error. If 0, all labels \n\ + will be compiled, at the cost of less optimized code. The default is 1.\n"); + return; + } if (strcmp(command,"SERIAL")==0) { printf( @@ -1002,6 +602,136 @@ static void add_predefined_symbol(char *command) add_config_symbol_definition(command, value); } +static void set_trace_option(char *command) +{ + char *cx; + int value; + + /* Parse options of the form STRING or STRING=NUM. (The $! has already been eaten.) If the string is null or empty, show help. */ + + if (!command || *command == '\0') { + printf("The full list of trace options:\n\n"); + printf(" ACTIONS: show actions defined\n"); + printf(" ASM: trace assembly (same as -a)\n"); + printf(" ASM=2: also show hex dumps\n"); + printf(" ASM=3: also show branch optimization info\n"); + printf(" ASM=4: more verbose branch info\n"); + printf(" BPATCH: show backpatch results\n"); + printf(" BPATCH=2: also show markers added\n"); + printf(" DICT: display the dictionary table\n"); + printf(" DICT=2: also the byte encoding of entries\n"); + printf(" EXPR: show expression trees\n"); + printf(" EXPR=2: more verbose\n"); + printf(" EXPR=3: even more verbose\n"); + printf(" FILES: show files opened\n"); + printf(" FINDABBREVS: show selection decisions during abbreviation optimization\n (only meaningful with -u)\n"); + printf(" FINDABBREVS=2: also show three-letter-block decisions\n"); + printf(" FREQ: show how efficient abbreviations were (same as -f)\n (only meaningful with -e)\n"); + printf(" LINKER: show module linking info\n"); + printf(" LINKER=2: more verbose (or 3, 4 for even more)\n"); + printf(" MAP: print memory map of the virtual machine (same as -z)\n"); + printf(" MAP=2: also show percentage of VM that each segment occupies\n"); + printf(" MEM: show internal memory allocations\n"); + printf(" OBJECTS: display the object table\n"); + printf(" PROPS: show attributes and properties defined\n"); + printf(" RUNTIME: show game function calls at runtime (same as -g)\n"); + printf(" RUNTIME=2: also show library calls (not supported in Glulx)\n"); + printf(" RUNTIME=3: also show veneer calls (not supported in Glulx)\n"); + printf(" STATS: give compilation statistics (same as -s)\n"); + printf(" SYMBOLS: display the symbol table\n"); + printf(" SYMBOLS=2: also show compiler-defined symbols\n"); + printf(" SYMDEF: show when symbols are noticed and defined\n"); + printf(" TOKENS: show token lexing\n"); + printf(" TOKENS=2: also show token types\n"); + printf(" TOKENS=3: also show lexical context\n"); + printf(" VERBS: display the verb grammar table\n"); + return; + } + + for (cx=command; *cx && *cx != '='; cx++) { + if (!(*cx >= 'A' && *cx <= 'Z')) { + printf("Invalid $! trace command \"%s\"\n", command); + return; + } + } + + value = 1; + if (*cx == '=') { + char *ex; + value = strtol(cx+1, &ex, 10); + + if (ex == cx+1 || *ex != '\0' || value < 0) { + printf("Bad numerical setting in $! trace command \"%s\"\n", command); + return; + } + + *cx = '\0'; + } + + /* We accept some reasonable synonyms, including plausible singular/plural confusion. */ + + if (strcmp(command, "ASSEMBLY")==0 || strcmp(command, "ASM")==0) { + asm_trace_setting = value; + } + else if (strcmp(command, "ACTION")==0 || strcmp(command, "ACTIONS")==0) { + printactions_switch = value; + } + else if (strcmp(command, "BPATCH")==0 || strcmp(command, "BACKPATCH")==0) { + bpatch_trace_setting = value; + } + else if (strcmp(command, "DICTIONARY")==0 || strcmp(command, "DICT")==0) { + list_dict_setting = value; + } + else if (strcmp(command, "EXPR")==0 || strcmp(command, "EXPRESSION")==0 || strcmp(command, "EXPRESSIONS")==0) { + expr_trace_setting = value; + } + else if (strcmp(command, "FILE")==0 || strcmp(command, "FILES")==0) { + files_trace_setting = value; + } + else if (strcmp(command, "FINDABBREV")==0 || strcmp(command, "FINDABBREVS")==0) { + optabbrevs_trace_setting = value; + } + else if (strcmp(command, "FREQUENCY")==0 || strcmp(command, "FREQUENCIES")==0 || strcmp(command, "FREQ")==0) { + frequencies_setting = value; + } + else if (strcmp(command, "LINK")==0 || strcmp(command, "LINKER")==0) { + linker_trace_setting = value; + } + else if (strcmp(command, "MAP")==0) { + memory_map_setting = value; + } + else if (strcmp(command, "MEM")==0 || strcmp(command, "MEMORY")==0) { + memout_switch = value; + } + else if (strcmp(command, "OBJECTS")==0 || strcmp(command, "OBJECT")==0 || strcmp(command, "OBJS")==0 || strcmp(command, "OBJ")==0) { + list_objects_setting = value; + } + else if (strcmp(command, "PROP")==0 || strcmp(command, "PROPERTY")==0 || strcmp(command, "PROPS")==0 || strcmp(command, "PROPERTIES")==0) { + printprops_switch = value; + } + else if (strcmp(command, "RUNTIME")==0) { + trace_fns_setting = value; + } + else if (strcmp(command, "STATISTICS")==0 || strcmp(command, "STATS")==0 || strcmp(command, "STAT")==0) { + statistics_switch = value; + } + else if (strcmp(command, "SYMBOLS")==0 || strcmp(command, "SYMBOL")==0) { + list_symbols_setting = value; + } + else if (strcmp(command, "SYMDEF")==0 || strcmp(command, "SYMBOLDEF")==0) { + symdef_trace_setting = value; + } + else if (strcmp(command, "TOKEN")==0 || strcmp(command, "TOKENS")==0) { + tokens_trace_setting = value; + } + else if (strcmp(command, "VERBS")==0 || strcmp(command, "VERB")==0) { + list_verbs_setting = value; + } + else { + printf("Unrecognized $! trace command \"%s\"\n", command); + } +} + /* Handle a dollar-sign command option: $LIST, $FOO=VAL, and so on. The option may come from the command line, an ICL file, or a header comment. @@ -1020,11 +750,18 @@ extern void memory_command(char *command) if (command[0]=='?') { explain_parameter(command+1); return; } if (command[0]=='#') { add_predefined_symbol(command+1); return; } + if (command[0]=='!') { set_trace_option(command+1); return; } - if (strcmp(command, "HUGE")==0) { set_memory_sizes(HUGE_SIZE); return; } - if (strcmp(command, "LARGE")==0) { set_memory_sizes(LARGE_SIZE); return; } - if (strcmp(command, "SMALL")==0) { set_memory_sizes(SMALL_SIZE); return; } + if (strcmp(command, "HUGE")==0 + || strcmp(command, "LARGE")==0 + || strcmp(command, "SMALL")==0) { + if (!nowarnings_switch) + printf("The Inform 6 memory size commands (\"SMALL, LARGE, HUGE\") are no longer needed and has been withdrawn.\n"); + return; + } + if (strcmp(command, "LIST")==0) { list_memory_sizes(); return; } + for (i=0; command[i]!=0; i++) { if (command[i]=='=') { command[i]=0; @@ -1034,28 +771,25 @@ extern void memory_command(char *command) if (strcmp(command,"BUFFER_LENGTH")==0) flag=2; if (strcmp(command,"MAX_QTEXT_SIZE")==0) - { MAX_QTEXT_SIZE=j, flag=1; - if (2*MAX_QTEXT_SIZE > MAX_STATIC_STRINGS) - MAX_STATIC_STRINGS = 2*MAX_QTEXT_SIZE; - } + flag=3; if (strcmp(command,"MAX_SYMBOLS")==0) - MAX_SYMBOLS=j, flag=1; + flag=3; if (strcmp(command,"MAX_BANK_SIZE")==0) flag=2; if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0) - SYMBOLS_CHUNK_SIZE=j, flag=1; + flag=3; if (strcmp(command,"BANK_CHUNK_SIZE")==0) flag=2; if (strcmp(command,"HASH_TAB_SIZE")==0) HASH_TAB_SIZE=j, flag=1; if (strcmp(command,"MAX_OBJECTS")==0) - MAX_OBJECTS=j, flag=1; + flag=3; if (strcmp(command,"MAX_ACTIONS")==0) - MAX_ACTIONS=j, flag=1; + flag=3; if (strcmp(command,"MAX_ADJECTIVES")==0) - MAX_ADJECTIVES=j, flag=1; + flag=3; if (strcmp(command,"MAX_DICT_ENTRIES")==0) - MAX_DICT_ENTRIES=j, flag=1; + flag=3; if (strcmp(command,"DICT_WORD_SIZE")==0) { DICT_WORD_SIZE=j, flag=1; DICT_WORD_SIZE_g=DICT_WORD_SIZE_z=j; @@ -1070,10 +804,12 @@ extern void memory_command(char *command) ZCODE_HEADER_EXT_WORDS=j, flag=1; if (strcmp(command,"ZCODE_HEADER_FLAGS_3")==0) ZCODE_HEADER_FLAGS_3=j, flag=1; + if (strcmp(command,"ZCODE_LESS_DICT_DATA")==0) + ZCODE_LESS_DICT_DATA=j, flag=1; if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0) GLULX_OBJECT_EXT_BYTES=j, flag=1; if (strcmp(command,"MAX_STATIC_DATA")==0) - MAX_STATIC_DATA=j, flag=1; + flag=3; if (strcmp(command,"MAX_OLDEPTH")==0) flag=2; if (strcmp(command,"MAX_ROUTINES")==0) @@ -1081,9 +817,7 @@ extern void memory_command(char *command) if (strcmp(command,"MAX_GCONSTANTS")==0) flag=2; if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0) - { MAX_PROP_TABLE_SIZE=j, flag=1; - MAX_PROP_TABLE_SIZE_g=MAX_PROP_TABLE_SIZE_z=j; - } + flag=3; if (strcmp(command,"MAX_FORWARD_REFS")==0) flag=2; if (strcmp(command,"STACK_SIZE")==0) @@ -1099,62 +833,51 @@ extern void memory_command(char *command) MAX_DYNAMIC_STRINGS_g=MAX_DYNAMIC_STRINGS_z=j; } if (strcmp(command,"MAX_ARRAYS")==0) - MAX_ARRAYS=j, flag=1; + flag=3; if (strcmp(command,"MAX_EXPRESSION_NODES")==0) - MAX_EXPRESSION_NODES=j, flag=1; + flag=3; if (strcmp(command,"MAX_VERBS")==0) - MAX_VERBS=j, flag=1; + flag=3; if (strcmp(command,"MAX_VERBSPACE")==0) - MAX_VERBSPACE=j, flag=1; + flag=3; if (strcmp(command,"MAX_LABELS")==0) - MAX_LABELS=j, flag=1; + flag=3; if (strcmp(command,"MAX_LINESPACE")==0) - MAX_LINESPACE=j, flag=1; + flag=3; if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0) - MAX_NUM_STATIC_STRINGS=j, flag=1; + flag=3; if (strcmp(command,"MAX_STATIC_STRINGS")==0) - { MAX_STATIC_STRINGS=j, flag=1; - if (2*MAX_QTEXT_SIZE > MAX_STATIC_STRINGS) - MAX_STATIC_STRINGS = 2*MAX_QTEXT_SIZE; - } + flag=3; if (strcmp(command,"MAX_ZCODE_SIZE")==0) - { MAX_ZCODE_SIZE=j, flag=1; - MAX_ZCODE_SIZE_g=MAX_ZCODE_SIZE_z=j; - } + flag=3; if (strcmp(command,"MAX_LINK_DATA_SIZE")==0) - MAX_LINK_DATA_SIZE=j, flag=1; + flag=3; if (strcmp(command,"MAX_LOW_STRINGS")==0) - MAX_LOW_STRINGS=j, flag=1; + flag=3; if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0) - MAX_TRANSCRIPT_SIZE=j, flag=1; + flag=3; if (strcmp(command,"MAX_CLASSES")==0) - MAX_CLASSES=j, flag=1; + flag=3; if (strcmp(command,"MAX_INCLUSION_DEPTH")==0) - MAX_INCLUSION_DEPTH=j, flag=1; + flag=3; if (strcmp(command,"MAX_SOURCE_FILES")==0) - MAX_SOURCE_FILES=j, flag=1; + flag=3; if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0) - MAX_INDIV_PROP_TABLE_SIZE=j, flag=1; + flag=3; if (strcmp(command,"INDIV_PROP_START")==0) INDIV_PROP_START=j, flag=1; if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0) - MAX_OBJ_PROP_TABLE_SIZE=j, flag=1; + flag=3; if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0) - MAX_OBJ_PROP_COUNT=j, flag=1; + flag=3; if (strcmp(command,"MAX_LOCAL_VARIABLES")==0) - { MAX_LOCAL_VARIABLES=j, flag=1; - MAX_LOCAL_VARIABLES_g=MAX_LOCAL_VARIABLES_z=j; - } + flag=3; if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0) - { MAX_GLOBAL_VARIABLES=j, flag=1; - MAX_GLOBAL_VARIABLES_g=MAX_GLOBAL_VARIABLES_z=j; - } + flag=3; if (strcmp(command,"ALLOC_CHUNK_SIZE")==0) - { ALLOC_CHUNK_SIZE=j, flag=1; - ALLOC_CHUNK_SIZE_g=ALLOC_CHUNK_SIZE_z=j; - } + flag=3; if (strcmp(command,"MAX_UNICODE_CHARS")==0) - MAX_UNICODE_CHARS=j, flag=1; + flag=3; if (strcmp(command,"MAX_STACK_SIZE")==0) { MAX_STACK_SIZE=j, flag=1; @@ -1185,6 +908,12 @@ extern void memory_command(char *command) if (OMIT_UNUSED_ROUTINES > 1 || OMIT_UNUSED_ROUTINES < 0) OMIT_UNUSED_ROUTINES = 1; } + if (strcmp(command,"STRIP_UNREACHABLE_LABELS")==0) + { + STRIP_UNREACHABLE_LABELS=j, flag=1; + if (STRIP_UNREACHABLE_LABELS > 1 || STRIP_UNREACHABLE_LABELS < 0) + STRIP_UNREACHABLE_LABELS = 1; + } if (strcmp(command,"SERIAL")==0) { if (j >= 0 && j <= 999999) @@ -1197,9 +926,10 @@ extern void memory_command(char *command) if (flag==0) printf("No such memory setting as \"%s\"\n", command); - if (flag==2) - printf("The Inform 5 memory setting \"%s\" has been withdrawn.\n\ -It should be safe to omit it (putting nothing in its place).\n", command); + if (flag==2 && !nowarnings_switch) + printf("The Inform 5 memory setting \"%s\" has been withdrawn.\n", command); + if (flag==3 && !nowarnings_switch) + printf("The Inform 6 memory setting \"%s\" is no longer needed and has been withdrawn.\n", command); return; } } diff --git a/src/objects.c b/src/objects.c index b957073..34e0b86 100644 --- a/src/objects.c +++ b/src/objects.c @@ -6,8 +6,8 @@ /* checks syntax and translates such directives into */ /* specifications for the object-maker. */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -20,7 +20,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -47,30 +47,34 @@ static fpropt full_object; /* "fpropt" is a typedef for a struct sizeof(fpropt) is about 6200 bytes */ static fproptg full_object_g; /* Equivalent for Glulx. This object is very small, since the large arrays - are allocated dynamically by the - Glulx compiler */ + are allocated dynamically as + memory-lists */ + static char shortname_buffer[766]; /* Text buffer to hold the short name (which is read in first, but written almost last) */ static int parent_of_this_obj; -static char *classname_text, *objectname_text; - /* For printing names of embedded - routines only */ +static memory_list current_object_name; /* The name of the object currently + being defined. */ + +static int current_classname_symbol; /* The symbol index of the class + currently being defined. + For error-checking and printing + names of embedded routines only. */ + +static memory_list embedded_function_name; /* Temporary storage for inline + function name in property. */ /* ------------------------------------------------------------------------- */ /* Classes. */ /* ------------------------------------------------------------------------- */ /* Arrays defined below: */ /* */ -/* int32 class_begins_at[n] offset of properties block for */ -/* nth class (always an offset */ -/* inside the properties_table) */ +/* classinfo class_info[] Object number and prop offset */ /* int classes_to_inherit_from[] The list of classes to inherit */ /* from as taken from the current */ /* Nearby/Object/Class definition */ -/* int class_object_numbers[n] The number of the prototype-object */ -/* for the nth class */ /* ------------------------------------------------------------------------- */ int no_classes; /* Number of class defns made so far */ @@ -92,13 +96,19 @@ int no_attributes, /* Number of attributes defined so far */ others itself, so the variable begins the compilation pass set to 4) */ +/* Print a PROPS trace line. The f flag is 0 for an attribute, 1 for + a common property, 2 for an individual property. */ static void trace_s(char *name, int32 number, int f) { if (!printprops_switch) return; - printf("%s %02ld ",(f==0)?"Attr":"Prop",(long int) number); - if (f==0) printf(" "); - else printf("%s%s",(prop_is_long[number])?"L":" ", - (prop_is_additive[number])?"A":" "); - printf(" %s\n",name); + char *stype = ""; + if (f == 0) stype = "Attr"; + else if (f == 1) stype = "Prop"; + else if (f == 2) stype = "Indiv"; + printf("%-5s %02ld ", stype, (long int) number); + if (f != 1) printf(" "); + else printf("%s%s",(commonprops[number].is_long)?"L":" ", + (commonprops[number].is_additive)?"A":" "); + printf(" %s\n", name); } extern void make_attribute(void) @@ -134,6 +144,7 @@ more than", get_next_token(); i = token_value; name = token_text; + /* We hold onto token_text through the end of this Property directive, which should be okay. */ if (token_type != SYMBOL_TT) { discard_token_location(beginning_debug_location); ebf_error("new attribute name", token_text); @@ -141,9 +152,9 @@ more than", put_token_back(); return; } - if (!(sflags[i] & UNKNOWN_SFLAG)) + if (!(symbols[i].flags & UNKNOWN_SFLAG)) { discard_token_location(beginning_debug_location); - ebf_symbol_error("new attribute name", token_text, typename(stypes[i]), slines[i]); + ebf_symbol_error("new attribute name", token_text, typename(symbols[i].type), symbols[i].line); panic_mode_error_recovery(); put_token_back(); return; @@ -156,7 +167,7 @@ more than", if ((token_type == DIR_KEYWORD_TT) && (token_value == ALIAS_DK)) { get_next_token(); if (!((token_type == SYMBOL_TT) - && (stypes[token_value] == ATTRIBUTE_T))) + && (symbols[token_value].type == ATTRIBUTE_T))) { discard_token_location(beginning_debug_location); ebf_error("an existing attribute name after 'alias'", token_text); @@ -164,9 +175,9 @@ more than", put_token_back(); return; } - assign_symbol(i, svals[token_value], ATTRIBUTE_T); - sflags[token_value] |= ALIASED_SFLAG; - sflags[i] |= ALIASED_SFLAG; + assign_symbol(i, symbols[token_value].value, ATTRIBUTE_T); + symbols[token_value].flags |= ALIASED_SFLAG; + symbols[i].flags |= ALIASED_SFLAG; } else { assign_symbol(i, no_attributes++, ATTRIBUTE_T); @@ -176,62 +187,82 @@ more than", if (debugfile_switch) { debug_file_printf(""); debug_file_printf("%s", name); - debug_file_printf("%d", svals[i]); + debug_file_printf("%d", symbols[i].value); write_debug_locations(get_token_location_end(beginning_debug_location)); debug_file_printf(""); } - trace_s(name, svals[i], 0); + trace_s(name, symbols[i].value, 0); return; } +/* Format: + Property [long] [additive] name + Property [long] [additive] name alias oldname + Property [long] [additive] name defaultvalue + Property [long] individual name + */ extern void make_property(void) { int32 default_value, i; - int additive_flag=FALSE; char *name; - assembly_operand AO; + int keywords, prevkeywords; + char *name; + int namelen; + int additive_flag, indiv_flag; debug_location_beginning beginning_debug_location = get_token_location_beginning(); - if (!glulx_mode) { - if (no_properties==((version_number==3)?32:64)) - { discard_token_location(beginning_debug_location); - if (version_number==3) - error("All 30 properties already declared (compile as \ -Advanced game to get an extra 62)"); - else - error("All 62 properties already declared"); - panic_mode_error_recovery(); - put_token_back(); - return; - } - } - else { - if (no_properties==INDIV_PROP_START) { - discard_token_location(beginning_debug_location); - error_numbered("All properties already declared -- max is", - INDIV_PROP_START); - panic_mode_error_recovery(); - put_token_back(); - return; - } - } - + /* The next bit is tricky. We want to accept any number of the keywords + "long", "additive", "individual" before the property name. But we + also want to accept "Property long" -- that's a legitimate + property name. + The solution is to keep track of which keywords we've seen in + a bitmask, and another for one token previous. That way we + can back up one token if there's no name visible. */ + keywords = prevkeywords = 0; do { directive_keywords.enabled = TRUE; get_next_token(); - if ((token_type == DIR_KEYWORD_TT) && (token_value == LONG_DK)) - obsolete_warning("all properties are now automatically 'long'"); - else - if ((token_type == DIR_KEYWORD_TT) && (token_value == ADDITIVE_DK)) - additive_flag = TRUE; - else break; + if ((token_type == DIR_KEYWORD_TT) && (token_value == LONG_DK)) { + prevkeywords = keywords; + keywords |= 1; + } + else if ((token_type == DIR_KEYWORD_TT) && (token_value == ADDITIVE_DK)) { + prevkeywords = keywords; + keywords |= 2; + } + else if ((token_type == DIR_KEYWORD_TT) && (token_value == INDIVIDUAL_DK)) { + prevkeywords = keywords; + keywords |= 4; + } + else { + break; + } } while (TRUE); - + + /* Re-parse the name with keywords turned off. (This allows us to + accept a property name like "table".) */ put_token_back(); directive_keywords.enabled = FALSE; get_next_token(); + if (token_type != SYMBOL_TT && keywords) { + /* This can't be a name. Try putting back the last keyword. */ + keywords = prevkeywords; + put_token_back(); + put_token_back(); + get_next_token(); + } + + additive_flag = indiv_flag = FALSE; + if (keywords & 1) + obsolete_warning("all properties are now automatically 'long'"); + if (keywords & 2) + additive_flag = TRUE; + if (keywords & 4) + indiv_flag = TRUE; + i = token_value; name = token_text; + /* We hold onto token_text through the end of this Property directive, which should be okay. */ if (token_type != SYMBOL_TT) { discard_token_location(beginning_debug_location); ebf_error("new property name", token_text); @@ -239,19 +270,50 @@ Advanced game to get an extra 62)"); put_token_back(); return; } - if (!(sflags[i] & UNKNOWN_SFLAG)) + if (!(symbols[i].flags & UNKNOWN_SFLAG)) { discard_token_location(beginning_debug_location); - ebf_symbol_error("new property name", token_text, typename(stypes[i]), slines[i]); + ebf_symbol_error("new property name", token_text, typename(symbols[i].type), symbols[i].line); panic_mode_error_recovery(); put_token_back(); return; } + if (indiv_flag) { + int this_identifier_number; + + if (additive_flag) + { error("'individual' incompatible with 'additive'"); + panic_mode_error_recovery(); + put_token_back(); + return; + } + + this_identifier_number = no_individual_properties++; + assign_symbol(i, this_identifier_number, INDIVIDUAL_PROPERTY_T); + if (debugfile_switch) { + debug_file_printf(""); + debug_file_printf + ("%s", name); + debug_file_printf + ("%d", this_identifier_number); + debug_file_printf(""); + } + trace_s(name, symbols[i].value, 2); + return; + } + directive_keywords.enabled = TRUE; get_next_token(); directive_keywords.enabled = FALSE; - if (strcmp(name+strlen(name)-3, "_to") == 0) sflags[i] |= STAR_SFLAG; + namelen = strlen(name); + if (namelen > 3 && strcmp(name+namelen-3, "_to") == 0) { + /* Direction common properties "n_to", etc are compared in some + libraries. They have STAR_SFLAG to tell us to skip a warning. */ + symbols[i].flags |= STAR_SFLAG; + } + + /* Now we might have "alias" or a default value (but not both). */ if ((token_type == DIR_KEYWORD_TT) && (token_value == ALIAS_DK)) { discard_token_location(beginning_debug_location); @@ -263,7 +325,7 @@ Advanced game to get an extra 62)"); } get_next_token(); if (!((token_type == SYMBOL_TT) - && (stypes[token_value] == PROPERTY_T))) + && (symbols[token_value].type == PROPERTY_T))) { ebf_error("an existing property name after 'alias'", token_text); panic_mode_error_recovery(); @@ -271,59 +333,88 @@ Advanced game to get an extra 62)"); return; } - assign_symbol(i, svals[token_value], PROPERTY_T); - trace_s(name, svals[i], 1); - sflags[token_value] |= ALIASED_SFLAG; - sflags[i] |= ALIASED_SFLAG; + assign_symbol(i, symbols[token_value].value, PROPERTY_T); + trace_s(name, symbols[i].value, 1); + symbols[token_value].flags |= ALIASED_SFLAG; + symbols[i].flags |= ALIASED_SFLAG; return; } + /* We now know we're allocating a new common property. Make sure + there's room. */ + if (!glulx_mode) { + if (no_properties==((version_number==3)?32:64)) + { discard_token_location(beginning_debug_location); + /* The maximum listed here includes "name" but not the + unused zero value or the two hidden properties (class + inheritance and indiv table). */ + if (version_number==3) + error("All 29 properties already declared (compile as \ +Advanced game to get 32 more)"); + else + error("All 61 properties already declared"); + panic_mode_error_recovery(); + put_token_back(); + return; + } + } + else { + if (no_properties==INDIV_PROP_START) { + char error_b[128]; + discard_token_location(beginning_debug_location); + sprintf(error_b, + "All %d properties already declared (increase INDIV_PROP_START to get more)", + INDIV_PROP_START-3); + error(error_b); + panic_mode_error_recovery(); + put_token_back(); + return; + } + } + default_value = 0; put_token_back(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) - { AO = parse_expression(CONSTANT_CONTEXT); + { + assembly_operand AO = parse_expression(CONSTANT_CONTEXT); default_value = AO.value; if (AO.marker != 0) backpatch_zmachine(AO.marker, PROP_DEFAULTS_ZA, (no_properties-1) * WORDSIZE); } - prop_default_value[no_properties] = default_value; - prop_is_long[no_properties] = TRUE; - prop_is_additive[no_properties] = additive_flag; + commonprops[no_properties].default_value = default_value; + commonprops[no_properties].is_long = TRUE; + commonprops[no_properties].is_additive = additive_flag; assign_symbol(i, no_properties++, PROPERTY_T); if (debugfile_switch) { debug_file_printf(""); debug_file_printf("%s", name); - debug_file_printf("%d", svals[i]); + debug_file_printf("%d", symbols[i].value); write_debug_locations (get_token_location_end(beginning_debug_location)); debug_file_printf(""); } - trace_s(name, svals[i], 1); + trace_s(name, symbols[i].value, 1); } /* ------------------------------------------------------------------------- */ /* Properties. */ /* ------------------------------------------------------------------------- */ -int32 *prop_default_value; /* Default values for properties */ -int *prop_is_long, /* Property modifiers, TRUE or FALSE: - "long" means "never write a 1-byte - value to this property", and is an - obsolete feature: since Inform 5 - all properties have been "long" */ - *prop_is_additive; /* "additive" means that values - accumulate rather than erase each - other during class inheritance */ -char *properties_table; /* Holds the table of property values +commonpropinfo *commonprops; /* Info about common properties + (fixed allocation of + INDIV_PROP_START entries) */ + +uchar *properties_table; /* Holds the table of property values (holding one block for each object and coming immediately after the object tree in Z-memory) */ +memory_list properties_table_memlist; int properties_table_size; /* Number of bytes in this table */ /* ------------------------------------------------------------------------- */ @@ -363,6 +454,7 @@ static int individual_prop_table_size; /* Size of the table of individual properties so far for current obj */ uchar *individuals_table; /* Table of records, each being the i.p. table for an object */ + memory_list individuals_table_memlist; int i_m; /* Write mark position in the above */ int individuals_length; /* Extent of individuals_table */ @@ -370,13 +462,16 @@ static int individual_prop_table_size; /* Size of the table of individual /* Arrays used by this file */ /* ------------------------------------------------------------------------- */ -objecttz *objectsz; /* Z-code only */ -objecttg *objectsg; /* Glulx only */ -uchar *objectatts; /* Glulx only */ -static int *classes_to_inherit_from; -int *class_object_numbers; -int32 *class_begins_at; - +objecttz *objectsz; /* Allocated to no_objects; Z-code only */ +memory_list objectsz_memlist; +objecttg *objectsg; /* Allocated to no_objects; Glulx only */ +static memory_list objectsg_memlist; +uchar *objectatts; /* Allocated to no_objects; Glulx only */ +static memory_list objectatts_memlist; +static int *classes_to_inherit_from; /* Allocated to no_classes_to_inherit_from */ +static memory_list classes_to_inherit_from_memlist; +classinfo *class_info; /* Allocated up to no_classes */ +memory_list class_info_memlist; /* ------------------------------------------------------------------------- */ /* Tracing for compiler maintenance */ @@ -384,10 +479,24 @@ int32 *class_begins_at; extern void list_object_tree(void) { int i; - printf("obj par nxt chl Object tree:\n"); - for (i=0; i 0) ? symbols[sym].name : "..."); + printf("%3d %-32s %3d %3d %3d\n", + i+1, symname, + objectsz[i].parent, objectsz[i].next, objectsz[i].child); + } + else { + int sym = objectsg[i].symbol; + char *symname = ((sym > 0) ? symbols[sym].name : "..."); + printf("%3d %-32s %3d %3d %3d\n", + i+1, symname, + objectsg[i].parent, objectsg[i].next, objectsg[i].child); + } + } } /* ------------------------------------------------------------------------- */ @@ -437,8 +546,8 @@ static void property_inheritance_z(void) for (class=0; class 64) + fatalerror("More than 64 property entries in an object"); for (k=0; k MAX_INDIV_PROP_TABLE_SIZE) - memoryerror("MAX_INDIV_PROP_TABLE_SIZE", - MAX_INDIV_PROP_TABLE_SIZE); - individuals_table[i_m++] = p[0]; - individuals_table[i_m++] = p[1]; - individuals_table[i_m++] = p[2]; - for (y=0;y < p[2]/2;y++) + ensure_memory_list_available(&individuals_table_memlist, i_m+3+individuals_table[z+2]); + individuals_table[i_m++] = individuals_table[z]; + individuals_table[i_m++] = individuals_table[z+1]; + individuals_table[i_m++] = individuals_table[z+2]; + for (y=0;y < individuals_table[z+2]/2;y++) { individuals_table[i_m++] = (z+3+y*2)/256; individuals_table[i_m++] = (z+3+y*2)%256; backpatch_zmachine(INHERIT_INDIV_MV, INDIVIDUAL_PROP_ZA, i_m-2); } } - z += p[2] + 3; - p += p[2] + 3; + z += individuals_table[z+2] + 3; } individuals_length = i_m; } @@ -556,18 +661,19 @@ so many values that the list has overflowed the maximum 32 entries"); a new property added to full_object */ k=full_object.l++; + if (k >= 64) + fatalerror("More than 64 property entries in an object"); full_object.pp[k].num = prop_number; full_object.pp[k].l = prop_length/2; for (i=0; i MAX_INDIV_PROP_TABLE_SIZE) - memoryerror("MAX_INDIV_PROP_TABLE_SIZE", - MAX_INDIV_PROP_TABLE_SIZE); - individuals_table[i_m++] = p[0]; - individuals_table[i_m++] = p[1]; - individuals_table[i_m++] = p[2]; - for (y=0;y < p[2]/2;y++) + ensure_memory_list_available(&individuals_table_memlist, i_m+3+individuals_table[z+2]); + individuals_table[i_m++] = individuals_table[z]; + individuals_table[i_m++] = individuals_table[z+1]; + individuals_table[i_m++] = individuals_table[z+2]; + for (y=0;y < individuals_table[z+2]/2;y++) { individuals_table[i_m++] = (z+3+y*2)/256; individuals_table[i_m++] = (z+3+y*2)%256; backpatch_zmachine(INHERIT_INDIV_MV, INDIVIDUAL_PROP_ZA, i_m-2); } - z += p[2] + 3; - p += p[2] + 3; + z += individuals_table[z+2] + 3; } individuals_length = i_m; } @@ -614,9 +714,7 @@ so many values that the list has overflowed the maximum 32 entries"); if (individual_prop_table_size > 0) { - if (i_m+2 > MAX_INDIV_PROP_TABLE_SIZE) - memoryerror("MAX_INDIV_PROP_TABLE_SIZE", - MAX_INDIV_PROP_TABLE_SIZE); + ensure_memory_list_available(&individuals_table_memlist, i_m+2); individuals_table[i_m++] = 0; individuals_table[i_m++] = 0; @@ -641,8 +739,8 @@ static void property_inheritance_g(void) ASSERT_GLULX(); for (class=0; class MAX_OBJ_PROP_TABLE_SIZE) { - memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE); - } - + + ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize + prop_length); for (i=0; i MAX_OBJ_PROP_TABLE_SIZE) { - memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE); - } + ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize + prop_length); for (i=0; i=from; prop_number--) { for (j=0; j= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); + { + int prop_length = 2*full_object.pp[j].l; + ensure_memory_list_available(&properties_table_memlist, mark+2+prop_length); if (version_number == 3) - p[mark++] = prop_number + (prop_length - 1)*32; + properties_table[mark++] = prop_number + (prop_length - 1)*32; else { switch(prop_length) { case 1: - p[mark++] = prop_number; break; + properties_table[mark++] = prop_number; break; case 2: - p[mark++] = prop_number + 0x40; break; + properties_table[mark++] = prop_number + 0x40; break; default: - p[mark++] = prop_number + 0x80; - p[mark++] = prop_length + 0x80; break; + properties_table[mark++] = prop_number + 0x80; + properties_table[mark++] = prop_length + 0x80; break; } } @@ -780,14 +869,15 @@ static int write_properties_between(uchar *p, int mark, int from, int to) { if (full_object.pp[j].ao[k].marker != 0) backpatch_zmachine(full_object.pp[j].ao[k].marker, PROP_ZA, mark); - p[mark++] = full_object.pp[j].ao[k].value/256; - p[mark++] = full_object.pp[j].ao[k].value%256; + properties_table[mark++] = full_object.pp[j].ao[k].value/256; + properties_table[mark++] = full_object.pp[j].ao[k].value%256; } } } } - p[mark++]=0; + ensure_memory_list_available(&properties_table_memlist, mark+1); + properties_table[mark++]=0; return(mark); } @@ -801,28 +891,31 @@ static int write_property_block_z(char *shortname) Return the number of bytes written to the block. */ int32 mark = properties_table_size, i; - uchar *p = (uchar *) properties_table; /* printf("Object at %04x\n", mark); */ if (shortname != NULL) - { uchar *tmp; - if (mark+1+510 >= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); - tmp = translate_text(p+mark+1,p+mark+1+510,shortname,STRCTX_OBJNAME); - if (!tmp) error ("Short name of object exceeded 765 Z-characters"); - i = subtract_pointers(tmp,(p+mark+1)); - p[mark] = i/2; + { + i = translate_text(510,shortname,STRCTX_OBJNAME); + if (i < 0) { + error ("Short name of object exceeded 765 Z-characters"); + i = 0; + } + ensure_memory_list_available(&properties_table_memlist, mark+1+i); + memcpy(properties_table + mark+1, translated_text, i); + properties_table[mark] = i/2; mark += i+1; } if (current_defn_is_class) - { mark = write_properties_between(p,mark,3,3); + { mark = write_properties_between(mark,3,3); + ensure_memory_list_available(&properties_table_memlist, mark+6); for (i=0;i<6;i++) - p[mark++] = full_object.atts[i]; - class_begins_at[no_classes++] = mark; + properties_table[mark++] = full_object.atts[i]; + ensure_memory_list_available(&class_info_memlist, no_classes+1); + class_info[no_classes++].begins_at = mark; } - mark = write_properties_between(p, mark, 1, (version_number==3)?31:63); + mark = write_properties_between(mark, 1, (version_number==3)?31:63); i = mark - properties_table_size; properties_table_size = mark; @@ -859,12 +952,13 @@ static int32 write_property_block_g(void) int ix, jx, kx, totalprops; int32 mark = properties_table_size; int32 datamark; - uchar *p = (uchar *) properties_table; if (current_defn_is_class) { + ensure_memory_list_available(&properties_table_memlist, mark+NUM_ATTR_BYTES); for (i=0;i= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); - WriteInt32(p+mark, totalprops); + ensure_memory_list_available(&properties_table_memlist, mark+4); + WriteInt32(properties_table+mark, totalprops); mark += 4; datamark = mark + 10*totalprops; @@ -903,11 +996,10 @@ static int32 write_property_block_g(void) jx= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); + ensure_memory_list_available(&properties_table_memlist, datamark+4*full_object_g.props[jx].datalen); for (kx=0; kx= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); - WriteInt16(p+mark, propnum); + ensure_memory_list_available(&properties_table_memlist, mark+10); + WriteInt16(properties_table+mark, propnum); mark += 2; - WriteInt16(p+mark, totallen); + WriteInt16(properties_table+mark, totallen); mark += 2; - WriteInt32(p+mark, datamarkstart); + WriteInt32(properties_table+mark, datamarkstart); mark += 4; - WriteInt16(p+mark, flags); + WriteInt16(properties_table+mark, flags); mark += 2; } @@ -944,6 +1035,10 @@ static void manufacture_object_z(void) segment_markers.enabled = FALSE; directives.enabled = TRUE; + ensure_memory_list_available(&objectsz_memlist, no_objects+1); + + objectsz[no_objects].symbol = full_object.symbol; + property_inheritance_z(); objectsz[no_objects].parent = parent_of_this_obj; @@ -967,8 +1062,6 @@ static void manufacture_object_z(void) j = write_property_block_z(shortname_buffer); objectsz[no_objects].propsize = j; - if (properties_table_size >= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); if (current_defn_is_class) for (i=0;i<6;i++) objectsz[no_objects].atts[i] = 0; @@ -985,6 +1078,11 @@ static void manufacture_object_g(void) segment_markers.enabled = FALSE; directives.enabled = TRUE; + ensure_memory_list_available(&objectsg_memlist, no_objects+1); + ensure_memory_list_available(&objectatts_memlist, no_objects+1); + + objectsg[no_objects].symbol = full_object_g.symbol; + property_inheritance_g(); objectsg[no_objects].parent = parent_of_this_obj; @@ -1013,8 +1111,6 @@ static void manufacture_object_g(void) objectsg[no_objects].propaddr = full_object_g.finalpropaddr; objectsg[no_objects].propsize = j; - if (properties_table_size >= MAX_PROP_TABLE_SIZE) - memoryerror("MAX_PROP_TABLE_SIZE",MAX_PROP_TABLE_SIZE); if (current_defn_is_class) for (i=0;i"); } + trace_s(token_text, symbols[token_value].value, 2); } else - { if (stypes[token_value]==INDIVIDUAL_PROPERTY_T) - this_identifier_number = svals[token_value]; + { if (symbols[token_value].type==INDIVIDUAL_PROPERTY_T) + this_identifier_number = symbols[token_value].value; else - { ebf_symbol_error("property name", token_text, typename(stypes[token_value]), slines[token_value]); + { ebf_symbol_error("property name", token_text, typename(symbols[token_value].type), symbols[token_value].line); return; } } @@ -1104,16 +1201,18 @@ static void properties_segment_z(int this_segment) defined_this_segment[def_t_s++] = token_value; if (individual_prop_table_size++ == 0) - { full_object.pp[full_object.l].num = 3; - full_object.pp[full_object.l].l = 1; - full_object.pp[full_object.l].ao[0].value - = individuals_length; - full_object.pp[full_object.l].ao[0].type = LONG_CONSTANT_OT; - full_object.pp[full_object.l].ao[0].marker = INDIVPT_MV; + { + int k=full_object.l++; + if (k >= 64) + fatalerror("More than 64 property entries in an object"); + full_object.pp[k].num = 3; + full_object.pp[k].l = 1; + INITAOTV(&full_object.pp[k].ao[0], LONG_CONSTANT_OT, individuals_length); + full_object.pp[k].ao[0].marker = INDIVPT_MV; i_m = individuals_length; - full_object.l++; } + ensure_memory_list_available(&individuals_table_memlist, i_m+3); individuals_table[i_m] = this_identifier_number/256; if (this_segment == PRIVATE_SEGMENT) individuals_table[i_m] |= 0x80; @@ -1123,7 +1222,7 @@ static void properties_segment_z(int this_segment) individuals_table[i_m+2] = 0; } else - { if (sflags[token_value] & UNKNOWN_SFLAG) + { if (symbols[token_value].flags & UNKNOWN_SFLAG) { error_named("No such property name as", token_text); return; } @@ -1133,30 +1232,32 @@ not 'private':", token_text); if (def_t_s >= defined_this_segment_size) ensure_defined_this_segment(def_t_s*2); defined_this_segment[def_t_s++] = token_value; - property_number = svals[token_value]; + property_number = symbols[token_value].value; next_prop=full_object.l++; + if (next_prop >= 64) + fatalerror("More than 64 property entries in an object"); full_object.pp[next_prop].num = property_number; } for (i=0; i<(def_t_s-1); i++) if (defined_this_segment[i] == token_value) { error_named("Property given twice in the same declaration:", - (char *) symbs[token_value]); + symbols[token_value].name); } else - if (svals[defined_this_segment[i]] == svals[token_value]) - { char error_b[128]; + if (symbols[defined_this_segment[i]].value == symbols[token_value].value) + { char error_b[128+2*MAX_IDENTIFIER_LENGTH]; sprintf(error_b, "Property given twice in the same declaration, because \ the names '%s' and '%s' actually refer to the same property", - (char *) symbs[defined_this_segment[i]], - (char *) symbs[token_value]); + symbols[defined_this_segment[i]].name, + symbols[token_value].name); error(error_b); } property_name_symbol = token_value; - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; length=0; do @@ -1176,18 +1277,24 @@ the names '%s' and '%s' actually refer to the same property", warning ("'name' property should only contain dictionary words"); if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP)) - { char embedded_name[80]; + { + char *prefix, *sep, *sym; + sym = symbols[property_name_symbol].name; if (current_defn_is_class) - { sprintf(embedded_name, - "%s::%s", classname_text, - (char *) symbs[property_name_symbol]); + { + prefix = symbols[current_classname_symbol].name; + sep = "::"; } else - { sprintf(embedded_name, - "%s.%s", objectname_text, - (char *) symbs[property_name_symbol]); + { + prefix = current_object_name.data; + sep = "."; } - AO.value = parse_routine(NULL, TRUE, embedded_name, FALSE, -1); + ensure_memory_list_available(&embedded_function_name, strlen(prefix)+strlen(sep)+strlen(sym)+1); + sprintf(embedded_function_name.data, "%s%s%s", prefix, sep, sym); + + /* parse_routine() releases lexer text! */ + AO.value = parse_routine(NULL, TRUE, embedded_function_name.data, FALSE, -1); AO.type = LONG_CONSTANT_OT; AO.marker = IROUTINE_MV; @@ -1217,7 +1324,7 @@ the names '%s' and '%s' actually refer to the same property", { if (length!=0) { if ((token_type == SYMBOL_TT) - && (stypes[token_value]==PROPERTY_T)) + && (symbols[token_value].type==PROPERTY_T)) { /* This is not necessarily an error: it's possible to imagine a property whose value is a list @@ -1239,7 +1346,7 @@ the names '%s' and '%s' actually refer to the same property", if (length == 64) { error_named("Limit (of 32 values) exceeded for property", - (char *) symbs[property_name_symbol]); + symbols[property_name_symbol].name); break; } @@ -1247,6 +1354,7 @@ the names '%s' and '%s' actually refer to the same property", { if (AO.marker != 0) backpatch_zmachine(AO.marker, INDIVIDUAL_PROP_ZA, i_m+3+length); + ensure_memory_list_available(&individuals_table_memlist, i_m+3+length+2); individuals_table[i_m+3+length++] = AO.value/256; individuals_table[i_m+3+length++] = AO.value%256; } @@ -1267,13 +1375,14 @@ the names '%s' and '%s' actually refer to the same property", if (length == 0) { if (individual_property) - { individuals_table[i_m+3+length++] = 0; + { + ensure_memory_list_available(&individuals_table_memlist, i_m+3+length+2); + individuals_table[i_m+3+length++] = 0; individuals_table[i_m+3+length++] = 0; } else - { full_object.pp[next_prop].ao[0].value = 0; - full_object.pp[next_prop].ao[0].type = LONG_CONSTANT_OT; - full_object.pp[next_prop].ao[0].marker = 0; + { + INITAOTV(&full_object.pp[next_prop].ao[0], LONG_CONSTANT_OT, 0); length = 2; } } @@ -1283,16 +1392,14 @@ the names '%s' and '%s' actually refer to the same property", { warning_named("Version 3 limit of 4 values per property exceeded \ (use -v5 to get 32), so truncating property", - (char *) symbs[property_name_symbol]); + symbols[property_name_symbol].name); length = 8; } } if (individual_property) { - if (individuals_length+length+3 > MAX_INDIV_PROP_TABLE_SIZE) - memoryerror("MAX_INDIV_PROP_TABLE_SIZE", - MAX_INDIV_PROP_TABLE_SIZE); + ensure_memory_list_available(&individuals_table_memlist, individuals_length+length+3); individuals_table[i_m + 2] = length; individuals_length += length+3; i_m = individuals_length; @@ -1336,10 +1443,10 @@ static void properties_segment_g(int this_segment) return; } - individual_property = (stypes[token_value] != PROPERTY_T); + individual_property = (symbols[token_value].type != PROPERTY_T); if (individual_property) - { if (sflags[token_value] & UNKNOWN_SFLAG) + { if (symbols[token_value].flags & UNKNOWN_SFLAG) { this_identifier_number = no_individual_properties++; assign_symbol(token_value, this_identifier_number, INDIVIDUAL_PROPERTY_T); @@ -1353,12 +1460,13 @@ static void properties_segment_g(int this_segment) debug_file_printf(""); } + trace_s(token_text, symbols[token_value].value, 2); } else - { if (stypes[token_value]==INDIVIDUAL_PROPERTY_T) - this_identifier_number = svals[token_value]; + { if (symbols[token_value].type==INDIVIDUAL_PROPERTY_T) + this_identifier_number = symbols[token_value].value; else - { ebf_symbol_error("property name", token_text, typename(stypes[token_value]), slines[token_value]); + { ebf_symbol_error("property name", token_text, typename(symbols[token_value].type), symbols[token_value].line); return; } } @@ -1366,9 +1474,10 @@ static void properties_segment_g(int this_segment) if (def_t_s >= defined_this_segment_size) ensure_defined_this_segment(def_t_s*2); defined_this_segment[def_t_s++] = token_value; - property_number = svals[token_value]; + property_number = symbols[token_value].value; next_prop=full_object_g.numprops++; + ensure_memory_list_available(&full_object_g.props_memlist, next_prop+1); full_object_g.props[next_prop].num = property_number; full_object_g.props[next_prop].flags = ((this_segment == PRIVATE_SEGMENT) ? 1 : 0); @@ -1377,7 +1486,7 @@ static void properties_segment_g(int this_segment) full_object_g.props[next_prop].datalen = 0; } else - { if (sflags[token_value] & UNKNOWN_SFLAG) + { if (symbols[token_value].flags & UNKNOWN_SFLAG) { error_named("No such property name as", token_text); return; } @@ -1388,9 +1497,10 @@ not 'private':", token_text); if (def_t_s >= defined_this_segment_size) ensure_defined_this_segment(def_t_s*2); defined_this_segment[def_t_s++] = token_value; - property_number = svals[token_value]; + property_number = symbols[token_value].value; next_prop=full_object_g.numprops++; + ensure_memory_list_available(&full_object_g.props_memlist, next_prop+1); full_object_g.props[next_prop].num = property_number; full_object_g.props[next_prop].flags = 0; full_object_g.props[next_prop].datastart = full_object_g.propdatasize; @@ -1401,25 +1511,21 @@ not 'private':", token_text); for (i=0; i<(def_t_s-1); i++) if (defined_this_segment[i] == token_value) { error_named("Property given twice in the same declaration:", - (char *) symbs[token_value]); + symbols[token_value].name); } else - if (svals[defined_this_segment[i]] == svals[token_value]) - { char error_b[128]; + if (symbols[defined_this_segment[i]].value == symbols[token_value].value) + { char error_b[128+2*MAX_IDENTIFIER_LENGTH]; sprintf(error_b, "Property given twice in the same declaration, because \ the names '%s' and '%s' actually refer to the same property", - (char *) symbs[defined_this_segment[i]], - (char *) symbs[token_value]); + symbols[defined_this_segment[i]].name, + symbols[token_value].name); error(error_b); } - if (full_object_g.numprops == MAX_OBJ_PROP_COUNT) { - memoryerror("MAX_OBJ_PROP_COUNT",MAX_OBJ_PROP_COUNT); - } - property_name_symbol = token_value; - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; length=0; do @@ -1439,19 +1545,25 @@ the names '%s' and '%s' actually refer to the same property", warning ("'name' property should only contain dictionary words"); if ((token_type == SEP_TT) && (token_value == OPEN_SQUARE_SEP)) - { char embedded_name[80]; + { + char *prefix, *sep, *sym; + sym = symbols[property_name_symbol].name; if (current_defn_is_class) - { sprintf(embedded_name, - "%s::%s", classname_text, - (char *) symbs[property_name_symbol]); + { + prefix = symbols[current_classname_symbol].name; + sep = "::"; } else - { sprintf(embedded_name, - "%s.%s", objectname_text, - (char *) symbs[property_name_symbol]); + { + prefix = current_object_name.data; + sep = "."; } - AO.value = parse_routine(NULL, TRUE, embedded_name, FALSE, -1); - AO.type = CONSTANT_OT; + ensure_memory_list_available(&embedded_function_name, strlen(prefix)+strlen(sep)+strlen(sym)+1); + sprintf(embedded_function_name.data, "%s%s%s", prefix, sep, sym); + + INITAOT(&AO, CONSTANT_OT); + /* parse_routine() releases lexer text! */ + AO.value = parse_routine(NULL, TRUE, embedded_function_name.data, FALSE, -1); AO.marker = IROUTINE_MV; directives.enabled = FALSE; @@ -1480,7 +1592,7 @@ the names '%s' and '%s' actually refer to the same property", { if (length!=0) { if ((token_type == SYMBOL_TT) - && (stypes[token_value]==PROPERTY_T)) + && (symbols[token_value].type==PROPERTY_T)) { /* This is not necessarily an error: it's possible to imagine a property whose value is a list @@ -1502,13 +1614,11 @@ the names '%s' and '%s' actually refer to the same property", if (length == 32768) /* VENEER_CONSTRAINT_ON_PROP_TABLE_SIZE? */ { error_named("Limit (of 32768 values) exceeded for property", - (char *) symbs[property_name_symbol]); + symbols[property_name_symbol].name); break; } - if (full_object_g.propdatasize >= MAX_OBJ_PROP_TABLE_SIZE) { - memoryerror("MAX_OBJ_PROP_TABLE_SIZE",MAX_OBJ_PROP_TABLE_SIZE); - } + ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize+1); full_object_g.propdata[full_object_g.propdatasize++] = AO; length += 1; @@ -1526,9 +1636,8 @@ the names '%s' and '%s' actually refer to the same property", if (length == 0) { assembly_operand AO; - AO.value = 0; - AO.type = CONSTANT_OT; - AO.marker = 0; + INITAOTV(&AO, CONSTANT_OT, 0); + ensure_memory_list_available(&full_object_g.propdata_memlist, full_object_g.propdatasize+1); full_object_g.propdata[full_object_g.propdatasize++] = AO; length += 1; } @@ -1583,13 +1692,13 @@ static void attributes_segment(void) } if ((token_type != SYMBOL_TT) - || (stypes[token_value] != ATTRIBUTE_T)) + || (symbols[token_value].type != ATTRIBUTE_T)) { ebf_error("name of an already-declared attribute", token_text); return; } - attribute_number = svals[token_value]; - sflags[token_value] |= USED_SFLAG; + attribute_number = symbols[token_value].value; + symbols[token_value].flags |= USED_SFLAG; if (!glulx_mode) { bitmask = (1 << (7-attribute_number%8)); @@ -1623,7 +1732,7 @@ static void add_class_to_inheritance_list(int class_number) to be translated into its actual class number: */ for (i=0;i 0) - { sprintf(duplicate_name, "%s_1", shortname_buffer); + { + int namelen = strlen(shortname_buffer); + char *duplicate_name = my_malloc(namelen+16, "temporary storage for object duplicate names"); + strcpy(duplicate_name, shortname_buffer); for (n=1; (duplicates_to_make--) > 0; n++) - { if (n>1) - { int i = strlen(duplicate_name); - while (duplicate_name[i] != '_') i--; - sprintf(duplicate_name+i+1, "%d", n); - } + { + sprintf(duplicate_name+namelen, "_%d", n); make_object(FALSE, duplicate_name, class_number, class_number, -1); } + my_free(&duplicate_name, "temporary storage for object duplicate names"); } + + /* Finished building the class. */ + current_classname_symbol = 0; } /* ------------------------------------------------------------------------- */ @@ -1936,16 +2059,13 @@ extern void make_object(int nearby_flag, The last is used to create instances of a particular class. */ int i, tree_depth, internal_name_symbol = 0; - char internal_name[64]; debug_location_beginning beginning_debug_location = get_token_location_beginning(); directives.enabled = FALSE; - if (no_objects==MAX_OBJECTS) memoryerror("MAX_OBJECTS", MAX_OBJECTS); - - sprintf(internal_name, "nameless_obj__%d", no_objects+1); - objectname_text = internal_name; + ensure_memory_list_available(¤t_object_name, 32); + sprintf(current_object_name.data, "nameless_obj__%d", no_objects+1); current_defn_is_class = FALSE; @@ -1986,12 +2106,13 @@ extern void make_object(int nearby_flag, ebf_error("name for new object or its textual short name", token_text); } - else if (!(sflags[token_value] & UNKNOWN_SFLAG)) { - ebf_symbol_error("new object", token_text, typename(stypes[token_value]), slines[token_value]); + else if (!(symbols[token_value].flags & UNKNOWN_SFLAG)) { + ebf_symbol_error("new object", token_text, typename(symbols[token_value].type), symbols[token_value].line); } else { internal_name_symbol = token_value; - strcpy(internal_name, token_text); + ensure_memory_list_available(¤t_object_name, strlen(token_text)+1); + strcpy(current_object_name.data, token_text); } } @@ -2009,7 +2130,7 @@ extern void make_object(int nearby_flag, } else { if ((token_type != SYMBOL_TT) - || (sflags[token_value] & UNKNOWN_SFLAG)) + || (symbols[token_value].flags & UNKNOWN_SFLAG)) { if (textual_name == NULL) ebf_error("parent object or the object's textual short name", token_text); @@ -2028,10 +2149,10 @@ extern void make_object(int nearby_flag, ebf_error("body of object definition", token_text); else { SpecParent: - if ((stypes[token_value] == OBJECT_T) - || (stypes[token_value] == CLASS_T)) - { specified_parent = svals[token_value]; - sflags[token_value] |= USED_SFLAG; + if ((symbols[token_value].type == OBJECT_T) + || (symbols[token_value].type == CLASS_T)) + { specified_parent = symbols[token_value].value; + symbols[token_value].flags |= USED_SFLAG; } else ebf_error("name of (the parent) object", token_text); } @@ -2049,13 +2170,10 @@ extern void make_object(int nearby_flag, if (internal_name_symbol > 0) assign_symbol(internal_name_symbol, no_objects + 1, OBJECT_T); - if (listobjects_switch) - printf("%3d \"%s\"\n", no_objects+1, - (textual_name==NULL)?"(with no short name)":textual_name); if (textual_name == NULL) { if (internal_name_symbol > 0) sprintf(shortname_buffer, "(%s)", - (char *) symbs[internal_name_symbol]); + symbols[internal_name_symbol].name); else sprintf(shortname_buffer, "(%d)", no_objects+1); } @@ -2116,6 +2234,11 @@ extern void make_object(int nearby_flag, } initialise_full_object(); + if (!glulx_mode) + full_object.symbol = internal_name_symbol; + else + full_object_g.symbol = internal_name_symbol; + if (instance_of != -1) add_class_to_inheritance_list(instance_of); if (specified_class == -1) parse_body_of_definition(); @@ -2124,11 +2247,12 @@ extern void make_object(int nearby_flag, if (debugfile_switch) { debug_file_printf(""); if (internal_name_symbol > 0) - { debug_file_printf("%s", internal_name); + { debug_file_printf("%s", + current_object_name.data); } else { debug_file_printf ("%s", - internal_name); + current_object_name.data); } debug_file_printf(""); write_debug_object_backpatch(no_objects + 1); @@ -2151,29 +2275,51 @@ extern void make_object(int nearby_flag, extern void init_objects_vars(void) { properties_table = NULL; - prop_is_long = NULL; - prop_is_additive = NULL; - prop_default_value = NULL; + individuals_table = NULL; + commonprops = NULL; objectsz = NULL; objectsg = NULL; objectatts = NULL; classes_to_inherit_from = NULL; - class_begins_at = NULL; + class_info = NULL; + + full_object_g.props = NULL; + full_object_g.propdata = NULL; } extern void objects_begin_pass(void) { properties_table_size=0; - prop_is_long[1] = TRUE; prop_is_additive[1] = TRUE; /* "name" */ - prop_is_long[2] = TRUE; prop_is_additive[2] = TRUE; /* inheritance prop */ - if (!glulx_mode) - prop_is_long[3] = TRUE; prop_is_additive[3] = FALSE; - /* instance variables table address */ + + /* The three predefined common properties: */ + /* (Entry 0 is not used.) */ + + /* "name" */ + commonprops[1].default_value = 0; + commonprops[1].is_long = TRUE; + commonprops[1].is_additive = TRUE; + + /* class inheritance property */ + commonprops[2].default_value = 0; + commonprops[2].is_long = TRUE; + commonprops[2].is_additive = TRUE; + + /* instance variables table address */ + /* (This property is only meaningful in Z-code; in Glulx its entry is + reserved but never used.) */ + commonprops[3].default_value = 0; + commonprops[3].is_long = TRUE; + commonprops[3].is_additive = FALSE; + no_properties = 4; if (debugfile_switch) - { debug_file_printf(""); + { + /* These two properties are not symbols, so they won't be emitted + by emit_debug_information_for_predefined_symbol(). Do it + manually. */ + debug_file_printf(""); debug_file_printf ("inheritance class"); debug_file_printf("2"); @@ -2190,15 +2336,19 @@ extern void objects_begin_pass(void) else no_attributes = 0; no_objects = 0; + /* Setting the info for object zero is probably a relic of very old code, but we do it. */ if (!glulx_mode) { + ensure_memory_list_available(&objectsz_memlist, 1); objectsz[0].parent = 0; objectsz[0].child = 0; objectsz[0].next = 0; no_individual_properties=72; } else { + ensure_memory_list_available(&objectsg_memlist, 1); objectsg[0].parent = 0; objectsg[0].child = 0; objectsg[0].next = 0; no_individual_properties = INDIV_PROP_START+8; } no_classes = 0; + current_classname_symbol = 0; no_embedded_routines = 0; @@ -2211,66 +2361,75 @@ extern void objects_allocate_arrays(void) objectsg = NULL; objectatts = NULL; - prop_default_value = my_calloc(sizeof(int32), INDIV_PROP_START, - "property default values"); - prop_is_long = my_calloc(sizeof(int), INDIV_PROP_START, - "property-is-long flags"); - prop_is_additive = my_calloc(sizeof(int), INDIV_PROP_START, - "property-is-additive flags"); + commonprops = my_calloc(sizeof(commonpropinfo), INDIV_PROP_START, + "common property info"); - classes_to_inherit_from = my_calloc(sizeof(int), MAX_CLASSES, - "inherited classes list"); - class_begins_at = my_calloc(sizeof(int32), MAX_CLASSES, - "pointers to classes"); - class_object_numbers = my_calloc(sizeof(int), MAX_CLASSES, - "class object numbers"); + initialise_memory_list(&class_info_memlist, + sizeof(classinfo), 64, (void**)&class_info, + "class info"); + initialise_memory_list(&classes_to_inherit_from_memlist, + sizeof(int), 64, (void**)&classes_to_inherit_from, + "inherited classes list"); - properties_table = my_malloc(MAX_PROP_TABLE_SIZE,"properties table"); - individuals_table = my_malloc(MAX_INDIV_PROP_TABLE_SIZE, - "individual properties table"); + initialise_memory_list(&properties_table_memlist, + sizeof(uchar), 10000, (void**)&properties_table, + "properties table"); + initialise_memory_list(&individuals_table_memlist, + sizeof(uchar), 10000, (void**)&individuals_table, + "individual properties table"); defined_this_segment_size = 128; defined_this_segment = my_calloc(sizeof(int), defined_this_segment_size, "defined this segment table"); + initialise_memory_list(¤t_object_name, + sizeof(char), 32, NULL, + "object name currently being defined"); + initialise_memory_list(&embedded_function_name, + sizeof(char), 32, NULL, + "temporary storage for inline function name"); + if (!glulx_mode) { - objectsz = my_calloc(sizeof(objecttz), MAX_OBJECTS, - "z-objects"); + initialise_memory_list(&objectsz_memlist, + sizeof(objecttz), 256, (void**)&objectsz, + "z-objects"); } else { - objectsg = my_calloc(sizeof(objecttg), MAX_OBJECTS, - "g-objects"); - objectatts = my_calloc(NUM_ATTR_BYTES, MAX_OBJECTS, - "g-attributes"); - full_object_g.props = my_calloc(sizeof(propg), MAX_OBJ_PROP_COUNT, - "object property list"); - full_object_g.propdata = my_calloc(sizeof(assembly_operand), - MAX_OBJ_PROP_TABLE_SIZE, - "object property data table"); + initialise_memory_list(&objectsg_memlist, + sizeof(objecttg), 256, (void**)&objectsg, + "g-objects"); + initialise_memory_list(&objectatts_memlist, + NUM_ATTR_BYTES, 256, (void**)&objectatts, + "g-attributes"); + initialise_memory_list(&full_object_g.props_memlist, + sizeof(propg), 64, (void**)&full_object_g.props, + "object property list"); + initialise_memory_list(&full_object_g.propdata_memlist, + sizeof(assembly_operand), 1024, (void**)&full_object_g.propdata, + "object property data table"); } } extern void objects_free_arrays(void) { - my_free(&prop_default_value, "property default values"); - my_free(&prop_is_long, "property-is-long flags"); - my_free(&prop_is_additive, "property-is-additive flags"); - - my_free(&objectsz, "z-objects"); - my_free(&objectsg, "g-objects"); - my_free(&objectatts, "g-attributes"); - my_free(&class_object_numbers,"class object numbers"); - my_free(&classes_to_inherit_from, "inherited classes list"); - my_free(&class_begins_at, "pointers to classes"); + my_free(&commonprops, "common property info"); + + deallocate_memory_list(¤t_object_name); + deallocate_memory_list(&embedded_function_name); + deallocate_memory_list(&objectsz_memlist); + deallocate_memory_list(&objectsg_memlist); + deallocate_memory_list(&objectatts_memlist); + deallocate_memory_list(&class_info_memlist); + deallocate_memory_list(&classes_to_inherit_from_memlist); - my_free(&properties_table, "properties table"); - my_free(&individuals_table,"individual properties table"); + deallocate_memory_list(&properties_table_memlist); + deallocate_memory_list(&individuals_table_memlist); my_free(&defined_this_segment,"defined this segment table"); if (!glulx_mode) { - my_free(&full_object_g.props, "object property list"); - my_free(&full_object_g.propdata, "object property data table"); + deallocate_memory_list(&full_object_g.props_memlist); + deallocate_memory_list(&full_object_g.propdata_memlist); } } diff --git a/src/states.c b/src/states.c index b569fb2..ea49550 100644 --- a/src/states.c +++ b/src/states.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "states" : Statement translator */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -15,7 +15,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -259,17 +259,17 @@ extern int parse_label(void) get_next_token(); if ((token_type == SYMBOL_TT) && - (stypes[token_value] == LABEL_T)) - { sflags[token_value] |= USED_SFLAG; - return(svals[token_value]); + (symbols[token_value].type == LABEL_T)) + { symbols[token_value].flags |= USED_SFLAG; + return(symbols[token_value].value); } - if ((token_type == SYMBOL_TT) && (sflags[token_value] & UNKNOWN_SFLAG)) + if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG)) { assign_symbol(token_value, next_label, LABEL_T); define_symbol_label(token_value); next_label++; - sflags[token_value] |= CHANGE_SFLAG + USED_SFLAG; - return(svals[token_value]); + symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG; + return(symbols[token_value].value); } ebf_error("label name", token_text); @@ -429,19 +429,21 @@ static void parse_print_z(int finally_return) break; case SYMBOL_TT: - if (sflags[token_value] & UNKNOWN_SFLAG) + if (symbols[token_value].flags & UNKNOWN_SFLAG) { INITAOT(&AO, LONG_CONSTANT_OT); AO.value = token_value; AO.marker = SYMBOL_MV; + AO.symindex = token_value; } else { INITAOT(&AO, LONG_CONSTANT_OT); - AO.value = svals[token_value]; + AO.value = symbols[token_value].value; AO.marker = IROUTINE_MV; - if (stypes[token_value] != ROUTINE_T) + AO.symindex = token_value; + if (symbols[token_value].type != ROUTINE_T) ebf_error("printing routine name", token_text); } - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; PrintByRoutine: @@ -664,19 +666,21 @@ static void parse_print_g(int finally_return) break; case SYMBOL_TT: - if (sflags[token_value] & UNKNOWN_SFLAG) + if (symbols[token_value].flags & UNKNOWN_SFLAG) { INITAOT(&AO, CONSTANT_OT); AO.value = token_value; AO.marker = SYMBOL_MV; + AO.symindex = token_value; } else { INITAOT(&AO, CONSTANT_OT); - AO.value = svals[token_value]; + AO.value = symbols[token_value].value; AO.marker = IROUTINE_MV; - if (stypes[token_value] != ROUTINE_T) + AO.symindex = token_value; + if (symbols[token_value].type != ROUTINE_T) ebf_error("printing routine name", token_text); } - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; PrintByRoutine: @@ -734,57 +738,73 @@ static void parse_print_g(int finally_return) } } -static void parse_statement_z(int break_label, int continue_label) -{ int ln, ln2, ln3, ln4, flag; - assembly_operand AO, AO2, AO3, AO4; - debug_location spare_debug_location1, spare_debug_location2; - - ASSERT_ZCODE(); - - if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP)) +/* Parse any number of ".Label;" lines before a statement. + Returns whether a statement can in fact follow. */ +static int parse_named_label_statements() +{ + while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP)) { /* That is, a full stop, signifying a label */ get_next_token(); - if (token_type == SYMBOL_TT) + if (token_type != SYMBOL_TT) { - if (sflags[token_value] & UNKNOWN_SFLAG) - { assign_symbol(token_value, next_label, LABEL_T); - sflags[token_value] |= USED_SFLAG; - assemble_label_no(next_label); - define_symbol_label(token_value); - next_label++; + ebf_error("label name", token_text); + return TRUE; + } + + if (symbols[token_value].flags & UNKNOWN_SFLAG) + { assign_symbol(token_value, next_label, LABEL_T); + symbols[token_value].flags |= USED_SFLAG; + assemble_label_no(next_label); + define_symbol_label(token_value); + next_label++; + } + else + { if (symbols[token_value].type != LABEL_T) { + ebf_error("label name", token_text); + return TRUE; } - else - { if (stypes[token_value] != LABEL_T) goto LabelError; - if (sflags[token_value] & CHANGE_SFLAG) - { sflags[token_value] &= (~(CHANGE_SFLAG)); - assemble_label_no(svals[token_value]); - define_symbol_label(token_value); - } - else error_named("Duplicate definition of label:", token_text); + if (symbols[token_value].flags & CHANGE_SFLAG) + { symbols[token_value].flags &= (~(CHANGE_SFLAG)); + assemble_label_no(symbols[token_value].value); + define_symbol_label(token_value); } + else error_named("Duplicate definition of label:", token_text); + } - get_next_token(); - if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); - put_token_back(); return; - } + get_next_token(); + if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) + { ebf_error("';'", token_text); + put_token_back(); return FALSE; + } - /* Interesting point of Inform grammar: a statement can only - consist solely of a label when it is immediately followed - by a "}". */ + /* Interesting point of Inform grammar: a statement can only + consist solely of a label when it is immediately followed + by a "}". */ - get_next_token(); - if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP)) - { put_token_back(); return; - } - statement_debug_location = get_token_location(); - parse_statement(break_label, continue_label); - return; + get_next_token(); + if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP)) + { put_token_back(); return FALSE; } - LabelError: ebf_error("label name", token_text); + /* The following line prevents labels from influencing the positions + of sequence points. */ + statement_debug_location = get_token_location(); + + /* Another label might follow */ } + /* On with the statement */ + return TRUE; +} + +static void parse_statement_z(int break_label, int continue_label) +{ int ln, ln2, ln3, ln4, flag; + int pre_unreach, labelexists; + assembly_operand AO, AO2, AO3, AO4; + debug_location spare_debug_location1, spare_debug_location2; + + ASSERT_ZCODE(); + if ((token_type == SEP_TT) && (token_value == HASH_SEP)) { parse_directive(TRUE); parse_statement(break_label, continue_label); return; @@ -901,7 +921,7 @@ static void parse_statement_z(int break_label, int continue_label) get_next_token(); if ((token_type == STATEMENT_TT) && (token_value == UNTIL_CODE)) - { assemble_label_no(ln2); + { assemble_forward_label_no(ln2); match_open_bracket(); AO = parse_expression(CONDITION_CONTEXT); match_close_bracket(); @@ -909,7 +929,7 @@ static void parse_statement_z(int break_label, int continue_label) } else error("'do' without matching 'until'"); - assemble_label_no(ln3); + assemble_forward_label_no(ln3); break; /* -------------------------------------------------------------------- */ @@ -997,7 +1017,7 @@ static void parse_statement_z(int break_label, int continue_label) sequence_point_follows = FALSE; if (!execution_never_reaches_here) assemblez_jump(ln); - assemble_label_no(ln2); + assemble_forward_label_no(ln2); return; } goto ParseUpdate; @@ -1106,7 +1126,7 @@ static void parse_statement_z(int break_label, int continue_label) } } - assemble_label_no(ln3); + assemble_forward_label_no(ln3); return; /* -------------------------------------------------------------------- */ @@ -1116,6 +1136,7 @@ static void parse_statement_z(int break_label, int continue_label) case GIVE_CODE: AO = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement"); if ((AO.type == VARIABLE_OT) && (AO.value == 0)) { INITAOTV(&AO, SHORT_CONSTANT_OT, 252); if (version_number != 6) assemblez_1(pull_zc, AO); @@ -1130,15 +1151,12 @@ static void parse_statement_z(int break_label, int continue_label) if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP)) ln = clear_attr_zc; else - { if ((token_type == SYMBOL_TT) - && (stypes[token_value] != ATTRIBUTE_T)) - warning_named("This is not a declared Attribute:", - token_text); - ln = set_attr_zc; + { ln = set_attr_zc; put_token_back(); } AO2 = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement"); if (runtime_error_checking_switch) { ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR; if (version_number >= 5) @@ -1159,8 +1177,9 @@ static void parse_statement_z(int break_label, int continue_label) /* -------------------------------------------------------------------- */ case IF_CODE: - flag = FALSE; + flag = FALSE; /* set if there's an "else" */ ln2 = 0; + pre_unreach = execution_never_reaches_here; match_open_bracket(); AO = parse_expression(CONDITION_CONTEXT); @@ -1178,8 +1197,17 @@ static void parse_statement_z(int break_label, int continue_label) ln = next_label++; } + /* The condition */ code_generate(AO, CONDITION_CONTEXT, ln); + if (!pre_unreach && ln >= 0 && execution_never_reaches_here) { + /* If the condition never falls through to here, then + it was an "if (0)" test. Our convention is to skip + the "not reached" warnings for this case. */ + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + + /* The "if" block */ if (ln >= 0) parse_code_block(break_label, continue_label, 0); else { get_next_token(); @@ -1212,13 +1240,46 @@ static void parse_statement_z(int break_label, int continue_label) } else put_token_back(); - if (ln >= 0) assemble_label_no(ln); + /* The "else" label (or end of statement, if there is no "else") */ + labelexists = FALSE; + if (ln >= 0) labelexists = assemble_forward_label_no(ln); if (flag) - { parse_code_block(break_label, continue_label, 0); - if (ln >= 0) assemble_label_no(ln2); - } + { + /* If labelexists is false, then we started with + "if (1)". In this case, we don't want a "not + reached" warning on the "else" block. We + temporarily disable the NOWARN flag, and restore it + afterwards. */ + int saved_unreach = 0; + if (execution_never_reaches_here && !labelexists) { + saved_unreach = execution_never_reaches_here; + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + + /* The "else" block */ + parse_code_block(break_label, continue_label, 0); + if (execution_never_reaches_here && !labelexists) { + if (saved_unreach & EXECSTATE_NOWARN) + execution_never_reaches_here |= EXECSTATE_NOWARN; + else + execution_never_reaches_here &= ~EXECSTATE_NOWARN; + } + + /* The post-"else" label */ + if (ln >= 0) assemble_forward_label_no(ln2); + } + else + { + /* There was no "else". If we're unreachable, then the + statement returned unconditionally, which means + "if (1) return". Skip warnings. */ + if (!pre_unreach && execution_never_reaches_here) { + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + } + return; /* -------------------------------------------------------------------- */ @@ -1271,6 +1332,8 @@ static void parse_statement_z(int break_label, int continue_label) AO2 = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); AO = code_generate(AO, QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement"); + check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement"); if ((runtime_error_checking_switch) && (veneer_mode == FALSE)) { if (version_number >= 5) assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR), @@ -1303,8 +1366,8 @@ static void parse_statement_z(int break_label, int continue_label) AO.value = token_value; else if ((token_type == SYMBOL_TT) && - (stypes[token_value] == GLOBAL_VARIABLE_T)) - AO.value = svals[token_value]; + (symbols[token_value].type == GLOBAL_VARIABLE_T)) + AO.value = symbols[token_value].value; else { ebf_error("'objectloop' variable", token_text); panic_mode_error_recovery(); break; @@ -1490,6 +1553,7 @@ static void parse_statement_z(int break_label, int continue_label) case REMOVE_CODE: AO = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement"); if ((runtime_error_checking_switch) && (veneer_mode == FALSE)) { if (version_number >= 5) assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR), @@ -1599,13 +1663,11 @@ static void parse_statement_z(int break_label, int continue_label) AO2 = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); if (is_constant_ot(AO2.type) && AO2.marker == 0) { - if (AO2.value >= 96) - { error("Z-machine dynamic strings are limited to 96"); + /* Compile-time check */ + if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) { + error_max_dynamic_strings(AO2.value); AO2.value = 0; } - if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) { - memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS); - } } get_next_token(); if (token_type == DQ_TT) @@ -1673,7 +1735,7 @@ static void parse_statement_z(int break_label, int continue_label) assemblez_store(AO2, AO); parse_code_block(ln = next_label++, continue_label, 1); - assemble_label_no(ln); + assemble_forward_label_no(ln); return; /* -------------------------------------------------------------------- */ @@ -1691,7 +1753,7 @@ static void parse_statement_z(int break_label, int continue_label) parse_code_block(ln2, ln, 0); sequence_point_follows = FALSE; assemblez_jump(ln); - assemble_label_no(ln2); + assemble_forward_label_no(ln2); return; /* -------------------------------------------------------------------- */ @@ -1716,57 +1778,12 @@ static void parse_statement_z(int break_label, int continue_label) static void parse_statement_g(int break_label, int continue_label) { int ln, ln2, ln3, ln4, flag, onstack; + int pre_unreach, labelexists; assembly_operand AO, AO2, AO3, AO4; debug_location spare_debug_location1, spare_debug_location2; ASSERT_GLULX(); - if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP)) - { /* That is, a full stop, signifying a label */ - - get_next_token(); - if (token_type == SYMBOL_TT) - { - if (sflags[token_value] & UNKNOWN_SFLAG) - { assign_symbol(token_value, next_label, LABEL_T); - sflags[token_value] |= USED_SFLAG; - assemble_label_no(next_label); - define_symbol_label(token_value); - next_label++; - } - else - { if (stypes[token_value] != LABEL_T) goto LabelError; - if (sflags[token_value] & CHANGE_SFLAG) - { sflags[token_value] &= (~(CHANGE_SFLAG)); - assemble_label_no(svals[token_value]); - define_symbol_label(token_value); - } - else error_named("Duplicate definition of label:", token_text); - } - - get_next_token(); - if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); - put_token_back(); return; - } - - /* Interesting point of Inform grammar: a statement can only - consist solely of a label when it is immediately followed - by a "}". */ - - get_next_token(); - if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP)) - { put_token_back(); return; - } - /* The following line prevents labels from influencing the positions - of sequence points. */ - statement_debug_location = get_token_location(); - parse_statement(break_label, continue_label); - return; - } - LabelError: ebf_error("label name", token_text); - } - if ((token_type == SEP_TT) && (token_value == HASH_SEP)) { parse_directive(TRUE); parse_statement(break_label, continue_label); return; @@ -1880,7 +1897,7 @@ static void parse_statement_g(int break_label, int continue_label) get_next_token(); if ((token_type == STATEMENT_TT) && (token_value == UNTIL_CODE)) - { assemble_label_no(ln2); + { assemble_forward_label_no(ln2); match_open_bracket(); AO = parse_expression(CONDITION_CONTEXT); match_close_bracket(); @@ -1888,7 +1905,7 @@ static void parse_statement_g(int break_label, int continue_label) } else error("'do' without matching 'until'"); - assemble_label_no(ln3); + assemble_forward_label_no(ln3); break; /* -------------------------------------------------------------------- */ @@ -1958,7 +1975,7 @@ static void parse_statement_g(int break_label, int continue_label) sequence_point_follows = FALSE; if (!execution_never_reaches_here) assembleg_jump(ln); - assemble_label_no(ln2); + assemble_forward_label_no(ln2); return; } goto ParseUpdate; @@ -2071,7 +2088,7 @@ static void parse_statement_g(int break_label, int continue_label) } } - assemble_label_no(ln3); + assemble_forward_label_no(ln3); return; /* -------------------------------------------------------------------- */ @@ -2081,6 +2098,7 @@ static void parse_statement_g(int break_label, int continue_label) case GIVE_CODE: AO = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement"); if ((AO.type == LOCALVAR_OT) && (AO.value == 0)) onstack = TRUE; else @@ -2098,15 +2116,12 @@ static void parse_statement_g(int break_label, int continue_label) if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP)) ln = 0; else - { if ((token_type == SYMBOL_TT) - && (stypes[token_value] != ATTRIBUTE_T)) - warning_named("This is not a declared Attribute:", - token_text); - ln = 1; + { ln = 1; put_token_back(); } AO2 = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement"); if (runtime_error_checking_switch && (!veneer_mode)) { ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR); if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) { @@ -2153,8 +2168,9 @@ static void parse_statement_g(int break_label, int continue_label) /* -------------------------------------------------------------------- */ case IF_CODE: - flag = FALSE; + flag = FALSE; /* set if there's an "else" */ ln2 = 0; + pre_unreach = execution_never_reaches_here; match_open_bracket(); AO = parse_expression(CONDITION_CONTEXT); @@ -2172,8 +2188,17 @@ static void parse_statement_g(int break_label, int continue_label) ln = next_label++; } + /* The condition */ code_generate(AO, CONDITION_CONTEXT, ln); + if (!pre_unreach && ln >= 0 && execution_never_reaches_here) { + /* If the condition never falls through to here, then + it was an "if (0)" test. Our convention is to skip + the "not reached" warnings for this case. */ + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + + /* The "if" block */ if (ln >= 0) parse_code_block(break_label, continue_label, 0); else { get_next_token(); @@ -2206,11 +2231,44 @@ static void parse_statement_g(int break_label, int continue_label) } else put_token_back(); - if (ln >= 0) assemble_label_no(ln); + /* The "else" label (or end of statement, if there is no "else") */ + labelexists = FALSE; + if (ln >= 0) labelexists = assemble_forward_label_no(ln); if (flag) - { parse_code_block(break_label, continue_label, 0); - if (ln >= 0) assemble_label_no(ln2); + { + /* If labelexists is false, then we started with + "if (1)". In this case, we don't want a "not + reached" warning on the "else" block. We + temporarily disable the NOWARN flag, and restore it + afterwards. */ + int saved_unreach = 0; + if (execution_never_reaches_here && !labelexists) { + saved_unreach = execution_never_reaches_here; + execution_never_reaches_here |= EXECSTATE_NOWARN; + } + + /* The "else" block */ + parse_code_block(break_label, continue_label, 0); + + if (execution_never_reaches_here && !labelexists) { + if (saved_unreach & EXECSTATE_NOWARN) + execution_never_reaches_here |= EXECSTATE_NOWARN; + else + execution_never_reaches_here &= ~EXECSTATE_NOWARN; + } + + /* The post-"else" label */ + if (ln >= 0) assemble_forward_label_no(ln2); + } + else + { + /* There was no "else". If we're unreachable, then the + statement returned unconditionally, which means + "if (1) return". Skip warnings. */ + if (!pre_unreach && execution_never_reaches_here) { + execution_never_reaches_here |= EXECSTATE_NOWARN; + } } return; @@ -2291,6 +2349,8 @@ static void parse_statement_g(int break_label, int continue_label) AO2 = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); AO = code_generate(AO, QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement"); + check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement"); if ((runtime_error_checking_switch) && (veneer_mode == FALSE)) assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2, zero_operand); @@ -2320,8 +2380,8 @@ static void parse_statement_g(int break_label, int continue_label) INITAOTV(&AO, LOCALVAR_OT, token_value); } else if ((token_type == SYMBOL_TT) && - (stypes[token_value] == GLOBAL_VARIABLE_T)) { - INITAOTV(&AO, GLOBALVAR_OT, svals[token_value]); + (symbols[token_value].type == GLOBAL_VARIABLE_T)) { + INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value); } else { ebf_error("'objectloop' variable", token_text); @@ -2423,7 +2483,7 @@ static void parse_statement_g(int break_label, int continue_label) sequence_point_follows = TRUE; ln = symbol_index("Class", -1); INITAOT(&AO2, CONSTANT_OT); - AO2.value = svals[ln]; + AO2.value = symbols[ln].value; AO2.marker = OBJECT_MV; assembleg_store(AO, AO2); @@ -2473,6 +2533,7 @@ static void parse_statement_g(int break_label, int continue_label) case REMOVE_CODE: AO = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); + check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement"); if ((runtime_error_checking_switch) && (veneer_mode == FALSE)) assembleg_call_1(veneer_routine(RT__ChR_VR), AO, zero_operand); @@ -2543,8 +2604,9 @@ static void parse_statement_g(int break_label, int continue_label) AO2 = code_generate(parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); if (is_constant_ot(AO2.type) && AO2.marker == 0) { + /* Compile-time check */ if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) { - memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS); + error_max_dynamic_strings(AO2.value); } } get_next_token(); @@ -2629,7 +2691,7 @@ static void parse_statement_g(int break_label, int continue_label) assembleg_store(temp_var1, AO); parse_code_block(ln = next_label++, continue_label, 1); - assemble_label_no(ln); + assemble_forward_label_no(ln); return; /* -------------------------------------------------------------------- */ @@ -2647,7 +2709,7 @@ static void parse_statement_g(int break_label, int continue_label) parse_code_block(ln2, ln, 0); sequence_point_follows = FALSE; assembleg_jump(ln); - assemble_label_no(ln2); + assemble_forward_label_no(ln2); return; /* -------------------------------------------------------------------- */ @@ -2681,10 +2743,26 @@ static void parse_statement_g(int break_label, int continue_label) extern void parse_statement(int break_label, int continue_label) { - if (!glulx_mode) - parse_statement_z(break_label, continue_label); - else - parse_statement_g(break_label, continue_label); + int res; + int saved_entire_flag; + + res = parse_named_label_statements(); + if (!res) + return; + + saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE); + if (execution_never_reaches_here) + execution_never_reaches_here |= EXECSTATE_ENTIRE; + + if (!glulx_mode) + parse_statement_z(break_label, continue_label); + else + parse_statement_g(break_label, continue_label); + + if (saved_entire_flag) + execution_never_reaches_here |= EXECSTATE_ENTIRE; + else + execution_never_reaches_here &= ~EXECSTATE_ENTIRE; } /* ========================================================================= */ diff --git a/src/symbols.c b/src/symbols.c index 6d736e9..8f2a09e 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "symbols" : The symbols table; creating stock of reserved words */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -15,7 +15,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -30,24 +30,32 @@ int no_symbols; /* Total number of symbols defined */ int no_named_constants; /* Copied into story file */ /* ------------------------------------------------------------------------- */ -/* Plus six arrays. Each symbol has its own index n (an int32) and */ +/* Plus an array of symbolinfo. Each symbol has its own index n (an */ +/* int32) in the array. The struct there contains: */ /* */ -/* svals[n] is its value (must be 32 bits wide, i.e. an int32, tho' */ -/* it is used to hold an unsigned 16 bit Z-machine value) */ -/* sflags[n] holds flags (see "header.h" for a list) */ -/* stypes[n] is the "type", distinguishing between the data type of */ +/* value is its value. In Z-code, this holds both the 16-bit value */ +/* and the 16-bit backpatch marker, so it is an int32. */ +/* marker is the backpatch marker in Glulx. */ +/* flags holds flags (see "header.h" for a list of ?_SFLAGS) */ +/* type is the "type", distinguishing between the data type of */ /* different kinds of constants/variables. */ -/* (See the "typename()" below.) */ -/* symbs[n] (needs to be cast to (char *) to be used) is the name */ -/* of the symbol, in the same case form as when created. */ -/* slines[n] is the source line on which the symbol value was first */ +/* (A ?_T constant; see the "typename()" below.) */ +/* name is the name of the symbol, in the same case form as */ +/* when created. */ +/* line is the source line on which the symbol value was first */ /* assigned */ -/* symbol_debug_backpatch_positions[n] */ +/* next_entry is the forward link in the symbol hash table. (See */ +/* start_of_list, below.) */ +/* */ +/* When generating a debug file (-k switch), we also allocate an array */ +/* of symboldebuginfo, which contains: */ +/* */ +/* backpatch_pos */ /* is a file position in the debug information file where */ /* the symbol's value should be written after backpatching, */ /* or else the null position if the value was known and */ /* written beforehand */ -/* replacement_debug_backpatch_positions[n] */ +/* replacement_backpatch_pos */ /* is a file position in the debug information file where */ /* the symbol's name can be erased if it is replaced, or */ /* else null if the name will never need to be replaced */ @@ -55,39 +63,28 @@ int no_named_constants; /* Copied into story file */ /* Comparison is case insensitive. */ /* Note that local variable names are not entered into the symbols table, */ /* as their numbers and scope are too limited for this to be efficient. */ -/* ------------------------------------------------------------------------- */ -/* Caveat editor: some array types are set up to work even on machines */ -/* where sizeof(int32 *) differs from, e.g., sizeof(char *): so do not */ -/* alter the types unless you understand what is going on! */ /* ------------------------------------------------------------------------- */ - int32 **symbs; - int32 *svals; - int *smarks; /* Glulx-only */ - brief_location *slines; - int *sflags; -#ifdef VAX - char *stypes; /* In VAX C, insanely, "signed char" is illegal */ -#else - signed char *stypes; -#endif - maybe_file_position *symbol_debug_backpatch_positions; - maybe_file_position *replacement_debug_backpatch_positions; +symbolinfo *symbols; /* Allocated up to no_symbols */ +static memory_list symbols_memlist; +symboldebuginfo *symbol_debug_info; /* Allocated up to no_symbols */ +static memory_list symbol_debug_info_memlist; /* ------------------------------------------------------------------------- */ /* Memory to hold the text of symbol names: note that this memory is */ /* allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE. */ /* ------------------------------------------------------------------------- */ -#define MAX_SYMBOL_CHUNKS (100) +#define SYMBOLS_CHUNK_SIZE (4096) -static uchar *symbols_free_space, /* Next byte free to hold new names */ +static char *symbols_free_space, /* Next byte free to hold new names */ *symbols_ceiling; /* Pointer to the end of the current allocation of memory for names */ static char** symbol_name_space_chunks; /* For chunks of memory used to hold the name strings of symbols */ static int no_symbol_name_space_chunks; +static memory_list symbol_name_space_chunks_memlist; /* Symbol replacements (used by the "Replace X Y" directive). */ @@ -131,8 +128,8 @@ static int symbol_definitions_size = 0; /* calloced size */ /* idea to choose HASH_TAB_SIZE as large as conveniently possible. */ /* ------------------------------------------------------------------------- */ -static int *next_entry; -static int32 *start_of_list; +static int32 *start_of_list; /* Allocated array of size HASH_TAB_SIZE */ +/* The next_entry field is part of the symbolinfo struct. */ /* ------------------------------------------------------------------------- */ /* Initialisation. */ @@ -187,7 +184,8 @@ extern int strcmpcis(char *p, char *q) /* ------------------------------------------------------------------------- */ extern void add_config_symbol_definition(char *symbol, int32 value) -{ +{ char *str; + if (symbol_definitions_count == symbol_definitions_size) { int oldsize = symbol_definitions_size; if (symbol_definitions_size == 0) @@ -198,7 +196,7 @@ extern void add_config_symbol_definition(char *symbol, int32 value) symbol_definitions_size, "symbol definition table"); } - char *str = my_malloc(strlen(symbol)+1, "symbol name"); + str = my_malloc(strlen(symbol)+1, "symbol name"); strcpy(str, symbol); symbol_definitions[symbol_definitions_count].symbol = str; @@ -210,17 +208,48 @@ extern void add_config_symbol_definition(char *symbol, int32 value) /* Symbol finding, creating, and removing. */ /* ------------------------------------------------------------------------- */ +extern int get_symbol_index(char *p) +{ + /* Return the index in the symbols array of symbol "p", or -1 + if it isn't there. Does not create a new symbol or mark the + symbol as used. */ + + int32 new_entry, this; + char *r; + int hashcode = hash_code_from_string(p); + + this = start_of_list[hashcode]; + + do + { if (this == -1) break; + + r = symbols[this].name; + new_entry = strcmpcis(r, p); + if (new_entry == 0) + { + return this; + } + if (new_entry > 0) break; + + this = symbols[this].next_entry; + } while (this != -1); + + return -1; +} + extern int symbol_index(char *p, int hashcode) { - /* Return the index in the symbs/svals/sflags/stypes/... arrays of symbol - "p", creating a new symbol with that name if it isn't already there. + /* Return the index in the symbols array of symbol "p", creating a + new symbol with that name if it isn't already there. New symbols are created with flag UNKNOWN_SFLAG, value 0x100 (a 2-byte quantity in Z-machine terms) and type CONSTANT_T. The string "p" is undamaged. */ - int32 new_entry, this, last; char *r; + int32 new_entry, this, last; + char *r; + int len; if (hashcode == -1) hashcode = hash_code_from_string(p); @@ -229,7 +258,7 @@ extern int symbol_index(char *p, int hashcode) do { if (this == -1) break; - r = (char *)symbs[this]; + r = symbols[this].name; new_entry = strcmpcis(r, p); if (new_entry == 0) { @@ -240,51 +269,55 @@ extern int symbol_index(char *p, int hashcode) if (new_entry > 0) break; last = this; - this = next_entry[this]; + this = symbols[this].next_entry; } while (this != -1); - if (no_symbols >= MAX_SYMBOLS) - memoryerror("MAX_SYMBOLS", MAX_SYMBOLS); + if (symdef_trace_setting) + printf("Encountered symbol %d '%s'\n", no_symbols, p); + + ensure_memory_list_available(&symbols_memlist, no_symbols+1); + if (debugfile_switch) + ensure_memory_list_available(&symbol_debug_info_memlist, no_symbols+1); if (last == -1) - { next_entry[no_symbols]=start_of_list[hashcode]; + { symbols[no_symbols].next_entry=start_of_list[hashcode]; start_of_list[hashcode]=no_symbols; } else - { next_entry[no_symbols]=this; - next_entry[last]=no_symbols; + { symbols[no_symbols].next_entry=this; + symbols[last].next_entry=no_symbols; } - if (symbols_free_space+strlen(p)+1 >= symbols_ceiling) + len = strlen(p); + if (symbols_free_space+len+1 >= symbols_ceiling) { symbols_free_space = my_malloc(SYMBOLS_CHUNK_SIZE, "symbol names chunk"); symbols_ceiling = symbols_free_space + SYMBOLS_CHUNK_SIZE; - /* If we've passed MAX_SYMBOL_CHUNKS chunks, we print an error - message telling the user to increase SYMBOLS_CHUNK_SIZE. - That is the correct cure, even though the error comes out - worded inaccurately. */ - if (no_symbol_name_space_chunks >= MAX_SYMBOL_CHUNKS) - memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE); + ensure_memory_list_available(&symbol_name_space_chunks_memlist, no_symbol_name_space_chunks+1); symbol_name_space_chunks[no_symbol_name_space_chunks++] - = (char *) symbols_free_space; - if (symbols_free_space+strlen(p)+1 >= symbols_ceiling) - memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE); + = symbols_free_space; + if (symbols_free_space+len+1 >= symbols_ceiling) + { + /* This should be impossible, since SYMBOLS_CHUNK_SIZE > MAX_IDENTIFIER_LENGTH. */ + fatalerror("Symbol exceeds the maximum possible length"); + } } - strcpy((char *) symbols_free_space, p); - symbs[no_symbols] = (int32 *) symbols_free_space; - symbols_free_space += strlen((char *)symbols_free_space) + 1; + strcpy(symbols_free_space, p); + symbols[no_symbols].name = symbols_free_space; + symbols_free_space += (len+1); - svals[no_symbols] = 0x100; /* ###-wrong? Would this fix the + symbols[no_symbols].value = 0x100; /* ###-wrong? Would this fix the unbound-symbol-causes-asm-error? */ - sflags[no_symbols] = UNKNOWN_SFLAG; - stypes[no_symbols] = CONSTANT_T; - slines[no_symbols] = get_brief_location(&ErrorReport); + symbols[no_symbols].flags = UNKNOWN_SFLAG; + symbols[no_symbols].marker = 0; + symbols[no_symbols].type = CONSTANT_T; + symbols[no_symbols].line = get_brief_location(&ErrorReport); if (debugfile_switch) { nullify_debug_file_position - (&symbol_debug_backpatch_positions[no_symbols]); + (&symbol_debug_info[no_symbols].backpatch_pos); nullify_debug_file_position - (&replacement_debug_backpatch_positions[no_symbols]); + (&symbol_debug_info[no_symbols].replacement_backpatch_pos); } if (track_unused_routines) @@ -300,19 +333,19 @@ extern void end_symbol_scope(int k) */ int j; - j = hash_code_from_string((char *) symbs[k]); + j = hash_code_from_string(symbols[k].name); if (start_of_list[j] == k) - { start_of_list[j] = next_entry[k]; + { start_of_list[j] = symbols[k].next_entry; return; } j = start_of_list[j]; while (j != -1) { - if (next_entry[j] == k) - { next_entry[j] = next_entry[k]; + if (symbols[j].next_entry == k) + { symbols[j].next_entry = symbols[k].next_entry; return; } - j = next_entry[j]; + j = symbols[j].next_entry; } } @@ -340,6 +373,10 @@ extern char *typename(int type) case OBJECT_T: return("Object"); case CLASS_T: return("Class"); case FAKE_ACTION_T: return("Fake action"); + + /* These are not symbol types, but they get printed in errors. */ + case STRING_REQ_T: return("String"); + case DICT_WORD_REQ_T: return("Dictionary word"); default: return("(Unknown type)"); } @@ -347,40 +384,140 @@ extern char *typename(int type) static void describe_flags(int flags) { if (flags & UNKNOWN_SFLAG) printf("(?) "); - if (flags & USED_SFLAG) printf("(used) "); if (flags & REPLACE_SFLAG) printf("(Replaced) "); + if (flags & USED_SFLAG) printf("(used) "); if (flags & DEFCON_SFLAG) printf("(Defaulted) "); if (flags & STUB_SFLAG) printf("(Stubbed) "); - if (flags & CHANGE_SFLAG) printf("(value will change) "); if (flags & IMPORT_SFLAG) printf("(Imported) "); if (flags & EXPORT_SFLAG) printf("(Exported) "); + if (flags & ALIASED_SFLAG) printf("(aliased) "); + if (flags & CHANGE_SFLAG) printf("(value will change) "); if (flags & SYSTEM_SFLAG) printf("(System) "); if (flags & INSF_SFLAG) printf("(created in sys file) "); if (flags & UERROR_SFLAG) printf("('Unknown' error issued) "); - if (flags & ALIASED_SFLAG) printf("(aliased) "); if (flags & ACTION_SFLAG) printf("(Action name) "); if (flags & REDEFINABLE_SFLAG) printf("(Redefinable) "); + if (flags & STAR_SFLAG) printf("(*) "); } extern void describe_symbol(int k) { printf("%4d %-16s %2d:%04d %04x %s ", - k, (char *) (symbs[k]), - (int)(slines[k].file_index), - (int)(slines[k].line_number), - svals[k], typename(stypes[k])); - describe_flags(sflags[k]); + k, (symbols[k].name), + (int)(symbols[k].line.file_index), + (int)(symbols[k].line.line_number), + symbols[k].value, typename(symbols[k].type)); + describe_flags(symbols[k].flags); } extern void list_symbols(int level) { int k; for (k=0; k=2) || + ((symbols[k].flags & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0)) { describe_symbol(k); printf("\n"); } } } +/* Check that the operand is of the given symbol type (XXX_T). If wanttype2 is nonzero, that's a second allowable type. + Generate a warning if no match. */ +extern void check_warn_symbol_type(const assembly_operand *AO, int wanttype, int wanttype2, char *context) +{ + symbolinfo *sym; + int symtype; + + if (AO->symindex < 0) + { + /* This argument is not a symbol; it's a local variable, a literal, or a computed expression. */ + /* We can recognize and type-check some literals. */ + if (AO->marker == DWORD_MV) { + if (wanttype != DICT_WORD_REQ_T && wanttype2 != DICT_WORD_REQ_T) + symtype_warning(context, NULL, typename(DICT_WORD_REQ_T), typename(wanttype)); + } + if (AO->marker == STRING_MV) { + if (wanttype != STRING_REQ_T && wanttype2 != STRING_REQ_T) + symtype_warning(context, NULL, typename(STRING_REQ_T), typename(wanttype)); + } + return; + } + + sym = &symbols[AO->symindex]; + symtype = sym->type; + + if (symtype == GLOBAL_VARIABLE_T) + { + /* A global variable could have any value. No way to generate a warning. */ + return; + } + if (symtype == CONSTANT_T) + { + /* A constant could also have any value. This case also includes forward-declared constants (UNKNOWN_SFLAG). */ + /* We try inferring its type by looking at the backpatch marker. Sadly, this only works for objects. (And not in Z-code, where object values are not backpatched.) */ + if (sym->marker == OBJECT_MV) { + /* Continue with inferred type. */ + symtype = OBJECT_T; + } + else { + /* Give up. */ + return; + } + } + + if (!( (symtype == wanttype) + || (wanttype2 != 0 && symtype == wanttype2))) + { + symtype_warning(context, sym->name, typename(symtype), typename(wanttype)); + } +} + +/* Similar, but we allow any type that has a metaclass: Object, Class, String, or Routine. + Generate a warning if no match. */ +extern void check_warn_symbol_has_metaclass(const assembly_operand *AO, char *context) +{ + symbolinfo *sym; + int symtype; + + if (AO->symindex < 0) + { + /* This argument is not a symbol; it's a local variable, a literal, or a computed expression. */ + /* We can recognize and type-check some literals. */ + if (AO->marker == DWORD_MV) { + symtype_warning(context, NULL, typename(DICT_WORD_REQ_T), "Object/Class/Routine/String"); + } + if (AO->marker == STRING_MV) { + /* Strings are good here. */ + } + return; + } + + sym = &symbols[AO->symindex]; + symtype = sym->type; + + if (symtype == GLOBAL_VARIABLE_T) + { + /* A global variable could have any value. No way to generate a warning. */ + return; + } + if (symtype == CONSTANT_T) + { + /* A constant could also have any value. This case also includes forward-declared constants (UNKNOWN_SFLAG). */ + /* We try inferring its type by looking at the backpatch marker. Sadly, this only works for objects. (And not in Z-code, where object values are not backpatched.) */ + if (sym->marker == OBJECT_MV) { + /* Continue with inferred type. */ + symtype = OBJECT_T; + } + else { + /* Give up. */ + return; + } + } + + if (!(symtype == ROUTINE_T || symtype == CLASS_T || symtype == OBJECT_T)) + { + symtype_warning(context, sym->name, typename(symtype), "Object/Class/Routine/String"); + } +} + extern void issue_unused_warnings(void) { int32 i; @@ -393,14 +530,25 @@ extern void issue_unused_warnings(void) /* Now back to mark anything necessary as used */ i = symbol_index("Main", -1); - if (!(sflags[i] & UNKNOWN_SFLAG)) sflags[i] |= USED_SFLAG; + if (!(symbols[i].flags & UNKNOWN_SFLAG)) symbols[i].flags |= USED_SFLAG; for (i=0;i= 0 && (symbols[value].flags & USED_SFLAG) && !(symbols[value].flags & UNKNOWN_SFLAG)) { + value = get_symbol_index("debug_flag"); + if (value >= 0 && (symbols[value].flags & USED_SFLAG) && (symbols[value].flags & UNKNOWN_SFLAG)) { + warning("DEBUG mode is on, but this story or library does not appear to support it"); + } } } @@ -431,112 +579,112 @@ extern void write_the_identifier_names(void) for (i=0; i"); debug_file_printf ("##%s", idname_string); - debug_file_printf("%d", svals[i]); + debug_file_printf("%d", symbols[i].value); debug_file_printf(""); } - action_name_strings[svals[i]] + action_name_strings[symbols[i].value] = compile_string(idname_string, STRCTX_SYMBOL); } } for (i=0; iusage |= DF_USAGE_MAIN; } ix = symbol_index("Main", -1); - if (stypes[ix] == ROUTINE_T) { - uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor); + if (symbols[ix].type == ROUTINE_T) { + uint32 addr = symbols[ix].value * (glulx_mode ? 1 : scale_factor); tofunc = df_function_for_address(addr); if (tofunc) tofunc->usage |= DF_USAGE_MAIN; @@ -1120,12 +1265,12 @@ extern void locate_dead_functions(void) for (ent = func->refs; ent; ent=ent->refsnext) { uint32 addr; int symbol = ent->symbol; - if (stypes[symbol] != ROUTINE_T) + if (symbols[symbol].type != ROUTINE_T) continue; - addr = svals[symbol] * (glulx_mode ? 1 : scale_factor); + addr = symbols[symbol].value * (glulx_mode ? 1 : scale_factor); tofunc = df_function_for_address(addr); if (!tofunc) { - error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]); + error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", symbols[symbol].name); continue; } /* A function may be marked here more than once. That's fine. */ @@ -1177,12 +1322,12 @@ extern void locate_dead_functions(void) for (ent = func->refs; ent; ent=ent->refsnext) { uint32 addr; int symbol = ent->symbol; - if (stypes[symbol] != ROUTINE_T) + if (symbols[symbol].type != ROUTINE_T) continue; - addr = svals[symbol] * (glulx_mode ? 1 : scale_factor); + addr = symbols[symbol].value * (glulx_mode ? 1 : scale_factor); tofunc = df_function_for_address(addr); if (!tofunc) { - error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]); + error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", symbols[symbol].name); continue; } if (tofunc->usage) @@ -1391,18 +1536,14 @@ extern uint32 df_next_function_iterate(int *funcused) extern void init_symbols_vars(void) { - symbs = NULL; - svals = NULL; - smarks = NULL; - stypes = NULL; - sflags = NULL; - next_entry = NULL; + symbols = NULL; start_of_list = NULL; + symbol_debug_info = NULL; symbol_name_space_chunks = NULL; no_symbol_name_space_chunks = 0; symbols_free_space=NULL; - symbols_ceiling=symbols_free_space; + symbols_ceiling=NULL; no_symbols = 0; @@ -1433,28 +1574,21 @@ extern void symbols_begin_pass(void) extern void symbols_allocate_arrays(void) { - symbs = my_calloc(sizeof(char *), MAX_SYMBOLS, "symbols"); - svals = my_calloc(sizeof(int32), MAX_SYMBOLS, "symbol values"); - if (glulx_mode) - smarks = my_calloc(sizeof(int), MAX_SYMBOLS, "symbol markers"); - slines = my_calloc(sizeof(brief_location), MAX_SYMBOLS, "symbol lines"); - stypes = my_calloc(sizeof(char), MAX_SYMBOLS, "symbol types"); - sflags = my_calloc(sizeof(int), MAX_SYMBOLS, "symbol flags"); + initialise_memory_list(&symbols_memlist, + sizeof(symbolinfo), 6400, (void**)&symbols, + "symbols"); if (debugfile_switch) - { symbol_debug_backpatch_positions = - my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS, - "symbol debug information backpatch positions"); - replacement_debug_backpatch_positions = - my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS, - "replacement debug information backpatch positions"); + { + initialise_memory_list(&symbol_debug_info_memlist, + sizeof(symboldebuginfo), 6400, (void**)&symbol_debug_info, + "symbol debug backpatch info"); } - next_entry = my_calloc(sizeof(int), MAX_SYMBOLS, - "symbol linked-list forward links"); start_of_list = my_calloc(sizeof(int32), HASH_TAB_SIZE, "hash code list beginnings"); - symbol_name_space_chunks - = my_calloc(sizeof(char *), MAX_SYMBOL_CHUNKS, "symbol names chunk addresses"); + initialise_memory_list(&symbol_name_space_chunks_memlist, + sizeof(char *), 32, (void**)&symbol_name_space_chunks, + "symbol names chunk addresses"); if (track_unused_routines) { df_tables_closed = FALSE; @@ -1495,23 +1629,13 @@ extern void symbols_free_arrays(void) my_free(&(symbol_name_space_chunks[i]), "symbol names chunk"); - my_free(&symbol_name_space_chunks, "symbol names chunk addresses"); + deallocate_memory_list(&symbol_name_space_chunks_memlist); - my_free(&symbs, "symbols"); - my_free(&svals, "symbol values"); - my_free(&smarks, "symbol markers"); - my_free(&slines, "symbol lines"); - my_free(&stypes, "symbol types"); - my_free(&sflags, "symbol flags"); + deallocate_memory_list(&symbols_memlist); if (debugfile_switch) - { my_free - (&symbol_debug_backpatch_positions, - "symbol debug information backpatch positions"); - my_free - (&replacement_debug_backpatch_positions, - "replacement debug information backpatch positions"); + { + deallocate_memory_list(&symbol_debug_info_memlist); } - my_free(&next_entry, "symbol linked-list forward links"); my_free(&start_of_list, "hash code list beginnings"); if (symbol_replacements) diff --git a/src/syntax.c b/src/syntax.c index 982d49f..f99de05 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "syntax" : Syntax analyser and compiler */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -15,7 +15,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -146,9 +146,9 @@ extern void parse_program(char *source) extern int parse_directive(int internal_flag) { - /* Internal_flag is FALSE if the directive is encountered normally, - TRUE if encountered with a # prefix inside a routine or object - definition. + /* Internal_flag is FALSE if the directive is encountered normally + (at the top level of the program); TRUE if encountered with + a # prefix inside a routine or object definition. (Only directives like #ifdef are permitted inside a definition.) @@ -158,6 +158,11 @@ extern int parse_directive(int internal_flag) int is_renamed; begin_syntax_line(FALSE); + if (!internal_flag) { + /* An internal directive can occur in the middle of an expression or + object definition. So we only release for top-level directives. */ + release_token_texts(); + } get_next_token(); if (token_type == EOF_TT) return(FALSE); @@ -184,9 +189,9 @@ extern int parse_directive(int internal_flag) { ebf_error("routine name", token_text); return(FALSE); } - if ((!(sflags[token_value] & UNKNOWN_SFLAG)) - && (!(sflags[token_value] & REPLACE_SFLAG))) - { ebf_symbol_error("routine name", token_text, typename(stypes[token_value]), slines[token_value]); + if ((!(symbols[token_value].flags & UNKNOWN_SFLAG)) + && (!(symbols[token_value].flags & REPLACE_SFLAG))) + { ebf_symbol_error("routine name", token_text, typename(symbols[token_value].type), symbols[token_value].line); return(FALSE); } @@ -195,7 +200,7 @@ extern int parse_directive(int internal_flag) rep_symbol = routine_symbol; is_renamed = find_symbol_replacement(&rep_symbol); - if ((sflags[routine_symbol] & REPLACE_SFLAG) + if ((symbols[routine_symbol].flags & REPLACE_SFLAG) && !is_renamed && (is_systemfile())) { /* The function is definitely being replaced (system_file always loses priority in a replacement) but is not @@ -214,9 +219,9 @@ extern int parse_directive(int internal_flag) { /* Parse the function definition and assign its symbol. */ assign_symbol(routine_symbol, parse_routine(lexical_source, FALSE, - (char *) symbs[routine_symbol], FALSE, routine_symbol), + symbols[routine_symbol].name, FALSE, routine_symbol), ROUTINE_T); - slines[routine_symbol] = routine_starts_line; + symbols[routine_symbol].line = routine_starts_line; } if (is_renamed) { @@ -224,8 +229,8 @@ extern int parse_directive(int internal_flag) The first time we see a definition for symbol X, we copy it to Y -- that's the "original" form of the function. */ - if (svals[rep_symbol] == 0) { - assign_symbol(rep_symbol, svals[routine_symbol], ROUTINE_T); + if (symbols[rep_symbol].value == 0) { + assign_symbol(rep_symbol, symbols[routine_symbol].value, ROUTINE_T); } } @@ -237,13 +242,13 @@ extern int parse_directive(int internal_flag) return TRUE; } - if ((token_type == SYMBOL_TT) && (stypes[token_value] == CLASS_T)) + if ((token_type == SYMBOL_TT) && (symbols[token_value].type == CLASS_T)) { if (internal_flag) { error("It is illegal to nest an object in a routine using '#classname'"); return(TRUE); } - sflags[token_value] |= USED_SFLAG; - make_object(FALSE, NULL, -1, -1, svals[token_value]); + symbols[token_value].flags |= USED_SFLAG; + make_object(FALSE, NULL, -1, -1, symbols[token_value].value); return TRUE; } @@ -261,6 +266,7 @@ extern int parse_directive(int internal_flag) return !(parse_given_directive(internal_flag)); } +/* Check what's coming up after a switch case value. */ static int switch_sign(void) { if ((token_type == SEP_TT)&&(token_value == COLON_SEP)) return 1; @@ -269,8 +275,10 @@ static int switch_sign(void) return 0; } -static assembly_operand spec_stack[32]; -static int spec_type[32]; +/* Info for the current switch statement. Both arrays indexed by spec_sp */ +#define MAX_SPEC_STACK (32) +static assembly_operand spec_stack[MAX_SPEC_STACK]; +static int spec_type[MAX_SPEC_STACK]; static void compile_alternatives_z(assembly_operand switch_value, int n, int stack_level, int label, int flag) @@ -324,7 +332,7 @@ static void parse_switch_spec(assembly_operand switch_value, int label, sequence_point_follows = FALSE; do - { if (spec_sp == 32) + { if (spec_sp >= MAX_SPEC_STACK) { error("At most 32 values can be given in a single 'switch' case"); panic_mode_error_recovery(); return; @@ -444,7 +452,8 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name, no_locals = 0; - for (i=0;i= 512) { k_long++; k_str=""; } - else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; } - if (total_bytes_trans == 0) rate = 0; - else rate=total_bytes_trans*1000/total_chars_trans; - - { printf("In:\ -%3d source code files %6d syntactic lines\n\ -%6d textual lines %8ld characters ", - total_input_files, no_syntax_lines, - total_source_line_count, (long int) total_chars_read); - if (character_set_unicode) printf("(UTF-8)\n"); - else if (character_set_setting == 0) printf("(plain ASCII)\n"); - else - { printf("(ISO 8859-%d %s)\n", character_set_setting, - name_of_iso_set(character_set_setting)); - } - - printf("Allocated:\n\ -%6d symbols (maximum %4d) %8ld bytes of memory\n\ -Out: Version %d \"%s\" %s %d.%c%c%c%c%c%c (%ld%sK long):\n", - no_symbols, MAX_SYMBOLS, - (long int) malloced_bytes, - version_number, - version_name(version_number), - output_called, - release_number, p[18], p[19], p[20], p[21], p[22], p[23], - (long int) k_long, k_str); - - printf("\ -%6d classes (maximum %3d) %6d objects (maximum %3d)\n\ -%6d global vars (maximum 233) %6d variable/array space (maximum %d)\n", - no_classes, MAX_CLASSES, - no_objects, ((version_number==3)?255:(MAX_OBJECTS-1)), - no_globals, - dynamic_array_area_size, MAX_STATIC_DATA); - - printf( -"%6d verbs (maximum %3d) %6d dictionary entries (maximum %d)\n\ -%6d grammar lines (version %d) %6d grammar tokens (unlimited)\n\ -%6d actions (maximum %3d) %6d attributes (maximum %2d)\n\ -%6d common props (maximum %2d) %6d individual props (unlimited)\n", - no_Inform_verbs, MAX_VERBS, - dict_entries, MAX_DICT_ENTRIES, - no_grammar_lines, grammar_version_number, - no_grammar_tokens, - no_actions, MAX_ACTIONS, - no_attributes, ((version_number==3)?32:48), - no_properties-2, ((version_number==3)?30:62), - no_individual_properties - 64); - - if (track_unused_routines) - { - uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping; - printf( -"%6ld bytes of Z-code %6ld unused bytes %s (%.1f%%)\n", - (long int) df_total_size_before_stripping, (long int) diff, - (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"), - 100 * (float)diff / (float)df_total_size_before_stripping); - } - - printf( -"%6ld characters used in text %6ld bytes compressed (rate %d.%3ld)\n\ -%6d abbreviations (maximum %d) %6d routines (unlimited)\n\ -%6ld instructions of Z-code %6d sequence points\n\ -%6ld bytes readable memory used (maximum 65536)\n\ -%6ld bytes used in Z-machine %6ld bytes free in Z-machine\n", - (long int) total_chars_trans, - (long int) total_bytes_trans, - (total_chars_trans>total_bytes_trans)?0:1, - (long int) rate, - no_abbreviations, MAX_ABBREVS, - no_routines, - (long int) no_instructions, no_sequence_points, - (long int) Write_Code_At, - (long int) Out_Size, - (long int) - (((long int) (limit*1024L)) - ((long int) Out_Size))); - - } - } - - if (offsets_switch) - { - { printf( -"\nOffsets in %s:\n\ -%05lx Synonyms %05lx Defaults %05lx Objects %05lx Properties\n\ -%05lx Variables %05lx Parse table %05lx Actions %05lx Preactions\n\ -%05lx Adjectives %05lx Dictionary %05lx Code %05lx Strings\n", - output_called, - (long int) abbrevs_at, - (long int) prop_defaults_at, - (long int) object_tree_at, - (long int) object_props_at, - (long int) globals_at, - (long int) grammar_table_at, - (long int) actions_at, - (long int) preactions_at, - (long int) adjectives_offset, - (long int) dictionary_at, - (long int) Write_Code_At, - (long int) Write_Strings_At); - if (module_switch) - printf("%05lx Linking data\n",(long int) link_table_at); - } - } - if (debugfile_switch) { begin_writing_debug_sections(); write_debug_section("abbreviations", 64); @@ -1100,130 +1003,125 @@ Out: Version %d \"%s\" %s %d.%c%c%c%c%c%c (%ld%sK long):\n", end_writing_debug_sections(Out_Size); } - if (memory_map_switch) + if (memory_map_setting) { + int32 addr; { printf("Dynamic +---------------------+ 00000\n"); -printf("memory | header |\n"); +printf("memory | header | %s\n", + show_percentage(0x40, Out_Size)); printf(" +---------------------+ 00040\n"); -printf(" | abbreviations |\n"); +printf(" | abbreviations | %s\n", + show_percentage(abbrevs_at-0x40, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) abbrevs_at); -printf(" | abbreviations table |\n"); +printf(" | abbreviations table | %s\n", + show_percentage(headerext_at-abbrevs_at, Out_Size)); printf(" +---------------------+ %05lx\n", (long int) headerext_at); -printf(" | header extension |\n"); +addr = (alphabet_modified ? charset_at : (zscii_defn_modified ? unicode_at : prop_defaults_at)); +printf(" | header extension | %s\n", + show_percentage(addr-headerext_at, Out_Size)); if (alphabet_modified) { printf(" + - - - - - - - - - - + %05lx\n", (long int) charset_at); -printf(" | alphabets table |\n"); +addr = (zscii_defn_modified ? unicode_at : prop_defaults_at); +printf(" | alphabets table | %s\n", + show_percentage(addr-charset_at, Out_Size)); } if (zscii_defn_modified) { printf(" + - - - - - - - - - - + %05lx\n", (long int) unicode_at); -printf(" | Unicode table |\n"); +printf(" | Unicode table | %s\n", + show_percentage(prop_defaults_at-unicode_at, Out_Size)); } printf(" +---------------------+ %05lx\n", (long int) prop_defaults_at); -printf(" | property defaults |\n"); +printf(" | property defaults | %s\n", + show_percentage(object_tree_at-prop_defaults_at, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) object_tree_at); -printf(" | objects |\n"); +printf(" | objects | %s\n", + show_percentage(object_props_at-object_tree_at, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) object_props_at); printf(" | object short names, |\n"); -printf(" | common prop values |\n"); +printf(" | common prop values | %s\n", + show_percentage(class_numbers_offset-object_props_at, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) class_numbers_offset); -printf(" | class numbers table |\n"); +printf(" | class numbers table | %s\n", + show_percentage(identifier_names_offset-class_numbers_offset, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) identifier_names_offset); -printf(" | symbol names table |\n"); +printf(" | symbol names table | %s\n", + show_percentage(individuals_offset-identifier_names_offset, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) individuals_offset); -printf(" | indiv prop values |\n"); +printf(" | indiv prop values | %s\n", + show_percentage(globals_at-individuals_offset, Out_Size)); printf(" +---------------------+ %05lx\n", (long int) globals_at); -printf(" | global variables |\n"); +printf(" | global variables | %s\n", + show_percentage(480, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", ((long int) globals_at)+480L); -printf(" | arrays |\n"); +printf(" | arrays | %s\n", + show_percentage(grammar_table_at-(globals_at+480), Out_Size)); printf(" +=====================+ %05lx\n", (long int) grammar_table_at); -printf("Readable| grammar table |\n"); +printf("Readable| grammar table | %s\n", + show_percentage(actions_at-grammar_table_at, Out_Size)); printf("memory + - - - - - - - - - - + %05lx\n", (long int) actions_at); -printf(" | actions |\n"); +printf(" | actions | %s\n", + show_percentage(preactions_at-actions_at, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) preactions_at); -printf(" | parsing routines |\n"); +printf(" | parsing routines | %s\n", + show_percentage(adjectives_offset-preactions_at, Out_Size)); printf(" + - - - - - - - - - - + %05lx\n", (long int) adjectives_offset); -printf(" | adjectives |\n"); +printf(" | adjectives | %s\n", + show_percentage(dictionary_at-adjectives_offset, Out_Size)); printf(" +---------------------+ %05lx\n", (long int) dictionary_at); -printf(" | dictionary |\n"); +addr = (module_switch ? map_of_module : (static_array_area_size ? static_arrays_at : Write_Code_At)); +printf(" | dictionary | %s\n", + show_percentage(addr-dictionary_at, Out_Size)); if (module_switch) { printf(" + - - - - - - - - - - + %05lx\n", (long int) map_of_module); -printf(" | map of module addrs |\n"); +addr = (static_array_area_size ? static_arrays_at : Write_Code_At); +printf(" | map of module addrs | %s\n", + show_percentage(addr-map_of_module, Out_Size)); } if (static_array_area_size) { printf(" +---------------------+ %05lx\n", (long int) static_arrays_at); -printf(" | static arrays |\n"); +printf(" | static arrays | %s\n", + show_percentage(Write_Code_At-static_arrays_at, Out_Size)); } printf(" +=====================+ %05lx\n", (long int) Write_Code_At); -printf("Above | Z-code |\n"); +printf("Above | Z-code | %s\n", + show_percentage(Write_Strings_At-Write_Code_At, Out_Size)); printf("readable+---------------------+ %05lx\n", (long int) Write_Strings_At); -printf("memory | strings |\n"); +addr = (module_switch ? link_table_at : Out_Size); +printf("memory | strings | %s\n", + show_percentage(addr-Write_Strings_At, Out_Size)); if (module_switch) { printf(" +=====================+ %05lx\n", (long int) link_table_at); -printf(" | module linking data |\n"); +printf(" | module linking data | %s\n", + show_percentage(Out_Size-link_table_at, Out_Size)); } printf(" +---------------------+ %05lx\n", (long int) Out_Size); } } - if (percentages_switch) - { printf("Approximate percentage breakdown of %s:\n", - output_called); - percentage("Z-code", code_length,Out_Size); - if (module_switch) - percentage("Linking data", link_data_size,Out_Size); - percentage("Static strings", strings_length,Out_Size); - percentage("Dictionary", Write_Code_At-dictionary_at,Out_Size); - percentage("Objects", globals_at-prop_defaults_at,Out_Size); - percentage("Globals", grammar_table_at-globals_at,Out_Size); - percentage("Parsing tables", dictionary_at-grammar_table_at,Out_Size); - percentage("Header and synonyms", prop_defaults_at,Out_Size); - percentage("Total of save area", grammar_table_at,Out_Size); - percentage("Total of text", total_bytes_trans,Out_Size); - } - if (frequencies_switch) - { - { printf("How frequently abbreviations were used, and roughly\n"); - printf("how many bytes they saved: ('_' denotes spaces)\n"); - for (i=0; i= 512) { k_long++; k_str=""; } - else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; } - if (strings_length == 0) rate = 0; - else rate=strings_length*1000/total_chars_trans; - - { printf("In:\ -%3d source code files %6d syntactic lines\n\ -%6d textual lines %8ld characters ", - total_input_files, no_syntax_lines, - total_source_line_count, (long int) total_chars_read); - if (character_set_unicode) printf("(UTF-8)\n"); - else if (character_set_setting == 0) printf("(plain ASCII)\n"); - else - { printf("(ISO 8859-%d %s)\n", character_set_setting, - name_of_iso_set(character_set_setting)); - } - - {char serialnum[8]; - write_serial_number(serialnum); - printf("Allocated:\n\ -%6d symbols (maximum %4d) %8ld bytes of memory\n\ -Out: %s %s %d.%c%c%c%c%c%c (%ld%sK long):\n", - no_symbols, MAX_SYMBOLS, - (long int) malloced_bytes, - version_name(version_number), - output_called, - release_number, - serialnum[0], serialnum[1], serialnum[2], - serialnum[3], serialnum[4], serialnum[5], - (long int) k_long, k_str); - } - - printf("\ -%6d classes (maximum %3d) %6d objects (maximum %3d)\n\ -%6d global vars (maximum %3d) %6d variable/array space (maximum %d)\n", - no_classes, MAX_CLASSES, - no_objects, MAX_OBJECTS, - no_globals, MAX_GLOBAL_VARIABLES, - dynamic_array_area_size, MAX_STATIC_DATA); - - printf( -"%6d verbs (maximum %3d) %6d dictionary entries (maximum %d)\n\ -%6d grammar lines (version %d) %6d grammar tokens (unlimited)\n\ -%6d actions (maximum %3d) %6d attributes (maximum %2d)\n\ -%6d common props (maximum %3d) %6d individual props (unlimited)\n", - no_Inform_verbs, MAX_VERBS, - dict_entries, MAX_DICT_ENTRIES, - no_grammar_lines, grammar_version_number, - no_grammar_tokens, - no_actions, MAX_ACTIONS, - no_attributes, NUM_ATTR_BYTES*8, - no_properties, INDIV_PROP_START, - no_individual_properties - INDIV_PROP_START); - - if (track_unused_routines) - { - uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping; - printf( -"%6ld bytes of code %6ld unused bytes %s (%.1f%%)\n", - (long int) df_total_size_before_stripping, (long int) diff, - (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"), - 100 * (float)diff / (float)df_total_size_before_stripping); - } - - printf( -"%6ld characters used in text %6ld bytes compressed (rate %d.%3ld)\n\ -%6d abbreviations (maximum %d) %6d routines (unlimited)\n\ -%6ld instructions of code %6d sequence points\n\ -%6ld bytes writable memory used %6ld bytes read-only memory used\n\ -%6ld bytes used in machine %10ld bytes free in machine\n", - (long int) total_chars_trans, - (long int) strings_length, - (total_chars_trans>strings_length)?0:1, - (long int) rate, - no_abbreviations, MAX_ABBREVS, - no_routines, - (long int) no_instructions, no_sequence_points, - (long int) (Out_Size - Write_RAM_At), - (long int) Write_RAM_At, - (long int) Out_Size, - (long int) - (((long int) (limit*1024L)) - ((long int) Out_Size))); - - } - } - - if (offsets_switch) - { - { printf( -"\nOffsets in %s:\n\ -%05lx Synonyms %05lx Defaults %05lx Objects %05lx Properties\n\ -%05lx Variables %05lx Parse table %05lx Actions %05lx Preactions\n\ -%05lx Adjectives %05lx Dictionary %05lx Code %05lx Strings\n", - output_called, - (long int) abbrevs_at, - (long int) prop_defaults_at, - (long int) object_tree_at, - (long int) object_props_at, - (long int) globals_at, - (long int) grammar_table_at, - (long int) actions_at, - (long int) preactions_at, - (long int) adjectives_offset, - (long int) dictionary_at, - (long int) Write_Code_At, - (long int) Write_Strings_At); - } - } - if (debugfile_switch) { begin_writing_debug_sections(); write_debug_section("memory layout id", GLULX_HEADER_SIZE); @@ -1797,73 +1572,79 @@ Out: %s %s %d.%c%c%c%c%c%c (%ld%sK long):\n", end_writing_debug_sections(Out_Size + MEMORY_MAP_EXTENSION); } - if (memory_map_switch) + if (memory_map_setting) { - + int32 addr; { printf(" +---------------------+ 000000\n"); -printf("Read- | header |\n"); +printf("Read- | header | %s\n", + show_percentage(GLULX_HEADER_SIZE, Out_Size)); printf(" only +=====================+ %06lx\n", (long int) GLULX_HEADER_SIZE); -printf("memory | memory layout id |\n"); +printf("memory | memory layout id | %s\n", + show_percentage(Write_Code_At-GLULX_HEADER_SIZE, Out_Size)); printf(" +---------------------+ %06lx\n", (long int) Write_Code_At); -printf(" | code |\n"); +printf(" | code | %s\n", + show_percentage(Write_Strings_At-Write_Code_At, Out_Size)); printf(" +---------------------+ %06lx\n", (long int) Write_Strings_At); -printf(" | string decode table |\n"); +printf(" | string decode table | %s\n", + show_percentage(compression_table_size, Out_Size)); printf(" + - - - - - - - - - - + %06lx\n", (long int) Write_Strings_At + compression_table_size); -printf(" | strings |\n"); +addr = (static_array_area_size ? static_arrays_at : Write_RAM_At+globals_at); +printf(" | strings | %s\n", + show_percentage(addr-(Write_Strings_At + compression_table_size), Out_Size)); if (static_array_area_size) { printf(" +---------------------+ %06lx\n", (long int) (static_arrays_at)); -printf(" | static arrays |\n"); +printf(" | static arrays | %s\n", + show_percentage(Write_RAM_At+globals_at-static_arrays_at, Out_Size)); } printf(" +=====================+ %06lx\n", (long int) (Write_RAM_At+globals_at)); -printf("Dynamic | global variables |\n"); +printf("Dynamic | global variables | %s\n", + show_percentage(arrays_at-globals_at, Out_Size)); printf("memory + - - - - - - - - - - + %06lx\n", (long int) (Write_RAM_At+arrays_at)); -printf(" | arrays |\n"); +printf(" | arrays | %s\n", + show_percentage(abbrevs_at-arrays_at, Out_Size)); printf(" +---------------------+ %06lx\n", (long int) (Write_RAM_At+abbrevs_at)); -printf(" | printing variables |\n"); - if (alphabet_modified) - { -printf(" + - - - - - - - - - - + %06lx\n", - (long int) (Write_RAM_At+charset_at)); -printf(" | alphabets table |\n"); - } - if (zscii_defn_modified) - { -printf(" + - - - - - - - - - - + %06lx\n", - (long int) (Write_RAM_At+unicode_at)); -printf(" | Unicode table |\n"); - } +printf(" | printing variables | %s\n", + show_percentage(object_tree_at-abbrevs_at, Out_Size)); printf(" +---------------------+ %06lx\n", (long int) (Write_RAM_At+object_tree_at)); -printf(" | objects |\n"); +printf(" | objects | %s\n", + show_percentage(object_props_at-object_tree_at, Out_Size)); printf(" + - - - - - - - - - - + %06lx\n", (long int) (Write_RAM_At+object_props_at)); -printf(" | property values |\n"); +printf(" | property values | %s\n", + show_percentage(prop_defaults_at-object_props_at, Out_Size)); printf(" + - - - - - - - - - - + %06lx\n", (long int) (Write_RAM_At+prop_defaults_at)); -printf(" | property defaults |\n"); +printf(" | property defaults | %s\n", + show_percentage(class_numbers_offset-prop_defaults_at, Out_Size)); printf(" + - - - - - - - - - - + %06lx\n", (long int) (Write_RAM_At+class_numbers_offset)); -printf(" | class numbers table |\n"); +printf(" | class numbers table | %s\n", + show_percentage(identifier_names_offset-class_numbers_offset, Out_Size)); printf(" + - - - - - - - - - - + %06lx\n", (long int) (Write_RAM_At+identifier_names_offset)); -printf(" | id names table |\n"); +printf(" | id names table | %s\n", + show_percentage(grammar_table_at-identifier_names_offset, Out_Size)); printf(" +---------------------+ %06lx\n", (long int) (Write_RAM_At+grammar_table_at)); -printf(" | grammar table |\n"); +printf(" | grammar table | %s\n", + show_percentage(actions_at-grammar_table_at, Out_Size)); printf(" + - - - - - - - - - - + %06lx\n", (long int) (Write_RAM_At+actions_at)); -printf(" | actions |\n"); +printf(" | actions | %s\n", + show_percentage(dictionary_offset-(Write_RAM_At+actions_at), Out_Size)); printf(" +---------------------+ %06lx\n", (long int) dictionary_offset); -printf(" | dictionary |\n"); +printf(" | dictionary | %s\n", + show_percentage(Out_Size-dictionary_offset, Out_Size)); if (MEMORY_MAP_EXTENSION == 0) { printf(" +---------------------+ %06lx\n", (long int) Out_Size); @@ -1871,56 +1652,276 @@ printf(" +---------------------+ %06lx\n", (long int) Out_Size); else { printf(" +=====================+ %06lx\n", (long int) Out_Size); -printf("Runtime | (empty) |\n"); +printf("Runtime | (empty) |\n"); /* no percentage */ printf(" extn +---------------------+ %06lx\n", (long int) Out_Size+MEMORY_MAP_EXTENSION); } } } +} +static void display_frequencies() +{ + int i, j; + + printf("How frequently abbreviations were used, and roughly\n"); + printf("how many bytes they saved: ('_' denotes spaces)\n"); + + for (i=0; i= 512) { k_long++; k_str=""; } + else if ((Out_Size-1024*k_long) > 0) { k_str=".5"; } + if (total_bytes_trans == 0) rate = 0; + else rate=total_bytes_trans*1000/total_chars_trans; + + { printf("In:\ +%3d source code files %6d syntactic lines\n\ +%6d textual lines %8ld characters ", + total_input_files, no_syntax_lines, + total_source_line_count, (long int) total_chars_read); + if (character_set_unicode) printf("(UTF-8)\n"); + else if (character_set_setting == 0) printf("(plain ASCII)\n"); + else + { printf("(ISO 8859-%d %s)\n", character_set_setting, + name_of_iso_set(character_set_setting)); } - if ((i%3)!=0) printf("\n"); - if (no_abbreviations==0) printf("None were declared.\n"); - } + + printf("Allocated:\n\ +%6d symbols %8ld bytes of memory\n\ +Out: Version %d \"%s\" %s %d.%c%c%c%c%c%c (%ld%sK long):\n", + no_symbols, + (long int) malloced_bytes, + version_number, + version_name(version_number), + output_called, + release_number, p[18], p[19], p[20], p[21], p[22], p[23], + (long int) k_long, k_str); + + printf("\ +%6d classes %6d objects\n\ +%6d global vars (maximum 233) %6d variable/array space\n", + no_classes, + no_objects, + no_globals, + dynamic_array_area_size); + + printf( + "%6d verbs %6d dictionary entries\n\ +%6d grammar lines (version %d) %6d grammar tokens (unlimited)\n\ +%6d actions %6d attributes (maximum %2d)\n\ +%6d common props (maximum %2d) %6d individual props (unlimited)\n", + no_Inform_verbs, + dict_entries, + no_grammar_lines, grammar_version_number, + no_grammar_tokens, + no_actions, + no_attributes, ((version_number==3)?32:48), + no_properties-3, ((version_number==3)?29:61), + no_individual_properties - 64); + + if (track_unused_routines) + { + uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping; + printf( + "%6ld bytes of Z-code %6ld unused bytes %s (%.1f%%)\n", + (long int) df_total_size_before_stripping, (long int) diff, + (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"), + 100 * (float)diff / (float)df_total_size_before_stripping); + } + + printf( + "%6ld characters used in text %6ld bytes compressed (rate %d.%3ld)\n\ +%6d abbreviations (maximum %d) %6d routines (unlimited)\n\ +%6ld instructions of Z-code %6d sequence points\n\ +%6ld bytes readable memory used (maximum 65536)\n\ +%6ld bytes used in Z-machine %6ld bytes free in Z-machine\n", + (long int) total_chars_trans, + (long int) total_bytes_trans, + (total_chars_trans>total_bytes_trans)?0:1, + (long int) rate, + no_abbreviations, MAX_ABBREVS, + no_routines, + (long int) no_instructions, no_sequence_points, + (long int) Write_Code_At, + (long int) Out_Size, + (long int) + (((long int) (limit*1024L)) - ((long int) Out_Size))); + + } +} + +static void display_statistics_g() +{ + int32 k_long, rate; + char *k_str = ""; + int32 limit = 1024*1024; + int32 strings_length = compression_table_size + compression_string_size; + char *output_called = (module_switch)?"module":"story file"; + + 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"; } + + if (strings_length == 0) rate = 0; + else rate=strings_length*1000/total_chars_trans; + + { printf("In:\ +%3d source code files %6d syntactic lines\n\ +%6d textual lines %8ld characters ", + total_input_files, no_syntax_lines, + total_source_line_count, (long int) total_chars_read); + if (character_set_unicode) printf("(UTF-8)\n"); + else if (character_set_setting == 0) printf("(plain ASCII)\n"); + else + { printf("(ISO 8859-%d %s)\n", character_set_setting, + name_of_iso_set(character_set_setting)); + } + + {char serialnum[8]; + write_serial_number(serialnum); + printf("Allocated:\n\ +%6d symbols %8ld bytes of memory\n\ +Out: %s %s %d.%c%c%c%c%c%c (%ld%sK long):\n", + no_symbols, + (long int) malloced_bytes, + version_name(version_number), + output_called, + release_number, + serialnum[0], serialnum[1], serialnum[2], + serialnum[3], serialnum[4], serialnum[5], + (long int) k_long, k_str); + } + + printf("\ +%6d classes %6d objects\n\ +%6d global vars %6d variable/array space\n", + no_classes, + no_objects, + no_globals, + dynamic_array_area_size); + + printf( + "%6d verbs %6d dictionary entries\n\ +%6d grammar lines (version %d) %6d grammar tokens (unlimited)\n\ +%6d actions %6d attributes (maximum %2d)\n\ +%6d common props (maximum %3d) %6d individual props (unlimited)\n", + no_Inform_verbs, + dict_entries, + no_grammar_lines, grammar_version_number, + no_grammar_tokens, + no_actions, + no_attributes, NUM_ATTR_BYTES*8, + no_properties-3, INDIV_PROP_START-3, + no_individual_properties - INDIV_PROP_START); + + if (track_unused_routines) + { + uint32 diff = df_total_size_before_stripping - df_total_size_after_stripping; + printf( + "%6ld bytes of code %6ld unused bytes %s (%.1f%%)\n", + (long int) df_total_size_before_stripping, (long int) diff, + (OMIT_UNUSED_ROUTINES ? "stripped out" : "detected"), + 100 * (float)diff / (float)df_total_size_before_stripping); + } + + printf( + "%6ld characters used in text %6ld bytes compressed (rate %d.%3ld)\n\ +%6d abbreviations (maximum %d) %6d routines (unlimited)\n\ +%6ld instructions of code %6d sequence points\n\ +%6ld bytes writable memory used %6ld bytes read-only memory used\n\ +%6ld bytes used in machine %10ld bytes free in machine\n", + (long int) total_chars_trans, + (long int) strings_length, + (total_chars_trans>strings_length)?0:1, + (long int) rate, + no_abbreviations, MAX_ABBREVS, + no_routines, + (long int) no_instructions, no_sequence_points, + (long int) (Out_Size - Write_RAM_At), + (long int) Write_RAM_At, + (long int) Out_Size, + (long int) + (((long int) (limit*1024L)) - ((long int) Out_Size))); + } } + extern void construct_storyfile(void) { - if (!glulx_mode) - construct_storyfile_z(); - else - construct_storyfile_g(); + if (!glulx_mode) + construct_storyfile_z(); + else + construct_storyfile_g(); + + /* Display all the trace/stats info that came out of compilation. + + (Except for the memory map, which uses a bunch of local variables + from construct_storyfile_z/g(), so it's easier to do that inside + that function.) + */ + + if (frequencies_setting) + display_frequencies(); + + if (list_symbols_setting) + list_symbols(list_symbols_setting); + + if (list_dict_setting) + show_dictionary(list_dict_setting); + + if (list_verbs_setting) + list_verb_table(); + + if (list_objects_setting) + list_object_tree(); + + if (statistics_switch) { + if (!glulx_mode) + display_statistics_z(); + else + display_statistics_g(); + } } /* ========================================================================= */ diff --git a/src/text.c b/src/text.c index 88ceaec..525ecec 100644 --- a/src/text.c +++ b/src/text.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "text" : Text translation, the abbreviations optimiser, the dictionary */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2021 */ +/* Part of Inform 6.40 */ +/* copyright (c) Graham Nelson 1993 - 2022 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -15,29 +15,30 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ #include "header.h" -uchar *low_strings, *low_strings_top; /* Start and next free byte in the low - strings pool */ +uchar *low_strings; /* Allocated to low_strings_top */ +int32 low_strings_top; +static memory_list low_strings_memlist; int32 static_strings_extent; /* Number of bytes of static strings made so far */ -memory_block static_strings_area; /* Used if (!temporary_files_switch) to - hold the static strings area so far */ +uchar *static_strings_area; /* Used to hold the static strings + area so far + Allocated to static_strings_extent */ +memory_list static_strings_area_memlist; -static uchar *strings_holding_area; /* Area holding translated strings - until they are moved into either - a temporary file, or the - static_strings_area below */ - -char *all_text, *all_text_top; /* Start and next byte free in (large) - text buffer holding the entire text +static char *all_text; /* Text buffer holding the entire text of the game, when it is being - recorded */ + recorded + (Allocated to all_text_top) */ +static memory_list all_text_memlist; +static int32 all_text_top; + int abbrevs_lookup_table_made, /* The abbreviations lookup table is constructed when the first non- abbreviation string is translated: @@ -48,8 +49,6 @@ int abbrevs_lookup_table_made, /* The abbreviations lookup table is with ASCII character n, or -1 if none of the abbreviations do */ int no_abbreviations; /* No of abbreviations defined so far */ -uchar *abbreviations_at; /* Memory to hold the text of any - abbreviation strings declared */ /* ------------------------------------------------------------------------- */ /* Glulx string compression storage */ /* ------------------------------------------------------------------------- */ @@ -62,7 +61,6 @@ int no_dynamic_strings; /* No. of @.. string escapes used int no_unicode_chars; /* Number of distinct Unicode chars used. (Beyond 0xFF.) */ -static int MAX_CHARACTER_SET; /* Number of possible entities */ huffentity_t *huff_entities; /* The list of entities (characters, abbreviations, @.. escapes, and the terminator) */ @@ -87,11 +85,16 @@ int32 compression_string_size; /* Length of the compressed string int32 *compressed_offsets; /* The beginning of every string in the game, relative to the beginning of the Huffman table. (So entry 0 - is equal to compression_table_size)*/ + is equal to compression_table_size). + Allocated to no_strings at + compress_game_text() time. */ +static memory_list compressed_offsets_memlist; + +unicode_usage_t *unicode_usage_entries; /* Allocated to no_unicode_chars */ +static memory_list unicode_usage_entries_memlist; #define UNICODE_HASH_BUCKETS (64) -unicode_usage_t *unicode_usage_entries; -static unicode_usage_t *unicode_usage_hash[UNICODE_HASH_BUCKETS]; +static int unicode_usage_hash[UNICODE_HASH_BUCKETS]; static int unicode_entity_index(int32 unicode); @@ -99,9 +102,20 @@ static int unicode_entity_index(int32 unicode); /* Abbreviation arrays */ /* ------------------------------------------------------------------------- */ -int *abbrev_values; -int *abbrev_quality; -int *abbrev_freqs; +abbreviation *abbreviations; /* Allocated up to no_abbreviations */ +static memory_list abbreviations_memlist; + +/* Memory to hold the text of any abbreviation strings declared. This is + counted in units of MAX_ABBREV_LENGTH bytes. (An abbreviation must fit + in that many bytes, null included.) */ +uchar *abbreviations_at; /* Allocated up to no_abbreviations */ +static memory_list abbreviations_at_memlist; + +static int *abbreviations_optimal_parse_schedule; +static memory_list abbreviations_optimal_parse_schedule_memlist; + +static int *abbreviations_optimal_parse_scores; +static memory_list abbreviations_optimal_parse_scores_memlist; /* ------------------------------------------------------------------------- */ @@ -110,26 +124,29 @@ int32 total_chars_trans, /* Number of ASCII chars of text in */ zchars_trans_in_last_string; /* Number of Z-chars in last string: needed only for abbrev efficiency calculation in "directs.c" */ -static int32 total_zchars_trans, /* Number of Z-chars of text out +static int32 total_zchars_trans; /* Number of Z-chars of text out (only used to calculate the above) */ - no_chars_transcribed; /* Number of ASCII chars written to - the text transcription area (used - for the -r and -u switches) */ static int zchars_out_buffer[3], /* During text translation, a buffer of 3 Z-chars at a time: when it's full these are written as a 2-byte word */ zob_index; /* Index (0 to 2) into it */ -static unsigned char *text_out_pc; /* The "program counter" during text - translation: the next address to +uchar *translated_text; /* Area holding translated strings + until they are moved into the + static_strings_area below */ +static memory_list translated_text_memlist; + +static int32 text_out_pos; /* The "program counter" during text + translation: the next position to write Z-coded text output to */ -static unsigned char *text_out_limit; /* The upper limit of text_out_pc - during text translation */ +static int32 text_out_limit; /* The upper limit of text_out_pos + during text translation (or -1 + for no limit) */ static int text_out_overflow; /* During text translation, becomes - true if text_out_pc tries to pass + true if text_out_pos tries to pass text_out_limit */ /* ------------------------------------------------------------------------- */ @@ -154,10 +171,10 @@ static void make_abbrevs_lookup(void) p2=(char *)abbreviations_at+k*MAX_ABBREV_LENGTH; if (strcmp(p1,p2)<0) { 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; + l=abbreviations[j].value; abbreviations[j].value=abbreviations[k].value; + abbreviations[k].value=l; + l=abbreviations[j].quality; abbreviations[j].quality=abbreviations[k].quality; + abbreviations[k].quality=l; bubble_sort = TRUE; } } @@ -166,7 +183,7 @@ static void make_abbrevs_lookup(void) for (j=no_abbreviations-1; j>=0; j--) { p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH; abbrevs_lookup[(uchar)p1[0]]=j; - abbrev_freqs[j]=0; + abbreviations[j].freq=0; } abbrevs_lookup_table_made = TRUE; } @@ -197,7 +214,7 @@ static int try_abbreviations_from(unsigned char *text, int i, int from) if (!glulx_mode) { for (k=0; p[k]!=0; k++) text[i+k]=1; } - abbrev_freqs[j]++; + abbreviations[j].freq++; return(j); NotMatched: ; } @@ -207,15 +224,25 @@ static int try_abbreviations_from(unsigned char *text, int i, int from) extern void make_abbreviation(char *text) { + ensure_memory_list_available(&abbreviations_memlist, no_abbreviations+1); + ensure_memory_list_available(&abbreviations_at_memlist, no_abbreviations+1); + strcpy((char *)abbreviations_at + no_abbreviations*MAX_ABBREV_LENGTH, text); - abbrev_values[no_abbreviations] = compile_string(text, STRCTX_ABBREV); + abbreviations[no_abbreviations].value = compile_string(text, STRCTX_ABBREV); + abbreviations[no_abbreviations].freq = 0; /* The quality is the number of Z-chars saved by using this */ /* abbreviation: note that it takes 2 Z-chars to print it. */ - abbrev_quality[no_abbreviations++] = zchars_trans_in_last_string - 2; + abbreviations[no_abbreviations].quality = zchars_trans_in_last_string - 2; + + if (abbreviations[no_abbreviations].quality <= 0) { + warning_named("Abbreviation does not save any characters:", text); + } + + no_abbreviations++; } /* ------------------------------------------------------------------------- */ @@ -226,29 +253,47 @@ extern void make_abbreviation(char *text) /* ------------------------------------------------------------------------- */ extern int32 compile_string(char *b, int strctx) -{ int i, j; uchar *c; - +{ int32 i, j, k; + uchar *c; + int in_low_memory; + + if (execution_never_reaches_here) { + /* No need to put strings into gametext.txt or the static/low + strings areas. */ + if (strctx == STRCTX_GAME || strctx == STRCTX_GAMEOPC || strctx == STRCTX_LOWSTRING || strctx == STRCTX_INFIX) { + /* VENEER and VENEEROPC are only used at the translate_text level, + so we don't have to catch them here. */ + return 0; + } + } + /* In Z-code, abbreviations go in the low memory pool (0x100). So do strings explicitly defined with the Lowstring directive. (In Glulx, the in_low_memory flag is ignored.) */ - int in_low_memory = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING); + in_low_memory = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING); if (!glulx_mode && in_low_memory) - { j=subtract_pointers(low_strings_top,low_strings); - low_strings_top=translate_text(low_strings_top, low_strings+MAX_LOW_STRINGS, b, strctx); - if (!low_strings_top) - memoryerror("MAX_LOW_STRINGS", MAX_LOW_STRINGS); + { + k = translate_text(-1, b, strctx); + if (k<0) { + error("text translation failed"); + k = 0; + } + ensure_memory_list_available(&low_strings_memlist, low_strings_top+k); + memcpy(low_strings+low_strings_top, translated_text, k); + j = low_strings_top; + low_strings_top += k; return(0x21+(j/2)); } if (glulx_mode && done_compression) compiler_error("Tried to add a string after compression was done."); - c = translate_text(strings_holding_area, strings_holding_area+MAX_STATIC_STRINGS, b, strctx); - if (!c) - memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS); - - i = subtract_pointers(c, strings_holding_area); + i = translate_text(-1, b, strctx); + if (i < 0) { + error("text translation failed"); + i = 0; + } /* Insert null bytes as needed to ensure that the next static string */ /* also occurs at an address expressible as a packed address */ @@ -261,23 +306,18 @@ extern int32 compile_string(char *b, int strctx) textalign = scale_factor; while ((i%textalign)!=0) { - if (i+2 > MAX_STATIC_STRINGS) - memoryerror("MAX_STATIC_STRINGS",MAX_STATIC_STRINGS); - i+=2; *c++ = 0; *c++ = 0; + ensure_memory_list_available(&translated_text_memlist, i+2); + translated_text[i++] = 0; + translated_text[i++] = 0; } } j = static_strings_extent; - if (temporary_files_switch) - for (c=strings_holding_area; c text_out_limit) { - text_out_overflow = TRUE; - return; + + if (text_out_limit >= 0) { + if (text_out_pos+2 > text_out_limit) { + text_out_overflow = TRUE; + return; + } } - text_out_pc[0] = j/256; text_out_pc[1] = j%256; text_out_pc+=2; + else { + ensure_memory_list_available(&translated_text_memlist, text_out_pos+2); + } + + translated_text[text_out_pos++] = j/256; translated_text[text_out_pos++] = j%256; total_bytes_trans+=2; } @@ -342,57 +389,84 @@ static void write_zscii(int zsc) /* ------------------------------------------------------------------------- */ static void end_z_chars(void) -{ unsigned char *p; +{ zchars_trans_in_last_string=total_zchars_trans-zchars_trans_in_last_string; while (zob_index!=0) write_z_char_z(5); - p=(unsigned char *) text_out_pc; - *(p-2)= *(p-2)+128; + if (text_out_pos < 2) { + /* Something went wrong. */ + text_out_overflow = TRUE; + return; + } + translated_text[text_out_pos-2] += 128; } /* Glulx handles this much more simply -- compression is done elsewhere. */ static void write_z_char_g(int i) { - ASSERT_GLULX(); - if (text_out_pc+1 > text_out_limit) { - text_out_overflow = TRUE; - return; - } - total_zchars_trans++; - text_out_pc[0] = i; - text_out_pc++; - total_bytes_trans++; + ASSERT_GLULX(); + if (text_out_limit >= 0) { + if (text_out_pos+1 > text_out_limit) { + text_out_overflow = TRUE; + return; + } + } + else { + ensure_memory_list_available(&translated_text_memlist, text_out_pos+1); + } + total_zchars_trans++; + translated_text[text_out_pos++] = i; + total_bytes_trans++; +} + +/* Helper routine to compute the weight, in units, of a character handled by the Z-Machine */ +static int zchar_weight(int c) +{ + int lookup = iso_to_alphabet_grid[c]; + if (lookup < 0) return 4; + if (lookup < 26) return 1; + return 2; } /* ------------------------------------------------------------------------- */ /* The main routine "text.c" provides to the rest of Inform: the text */ -/* translator. p is the address to write output to, s_text the source text */ -/* and the return value is the next free address to write output to. */ -/* The return value will not exceed p_limit. If the translation tries to */ -/* overflow this boundary, the return value will be NULL (and you should */ -/* display an error). */ +/* translator. s_text is the source text and the return value is the */ +/* number of bytes translated. */ +/* The translated text will be stored in translated_text. */ +/* */ +/* If p_limit is >= 0, the text length will not exceed that many bytes. */ +/* If the translation tries to overflow this boundary, the return value */ +/* will be -1. (You should display an error and not read translated_text.) */ +/* */ +/* If p_limit is negative, any amount of text is accepted (up to int32 */ +/* anyway). */ +/* */ /* Note that the source text may be corrupted by this routine. */ /* ------------------------------------------------------------------------- */ -extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx) -{ int i, j, k, in_alphabet, lookup_value; +extern int32 translate_text(int32 p_limit, char *s_text, int strctx) +{ int i, j, k, in_alphabet, lookup_value, is_abbreviation; int32 unicode; int zscii; unsigned char *text_in; + if (p_limit >= 0) { + ensure_memory_list_available(&translated_text_memlist, p_limit); + } + /* For STRCTX_ABBREV, the string being translated is itself an abbreviation string, so it can't make use of abbreviations. Set the is_abbreviation flag to indicate this. The compiler has historically set this flag for the Lowstring directive as well -- the in_low_memory and is_abbreviation flag were always the same. I am preserving that convention. */ - int is_abbreviation = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING); + is_abbreviation = (strctx == STRCTX_ABBREV || strctx == STRCTX_LOWSTRING); - /* Cast the input and output streams to unsigned char: text_out_pc will + /* Cast the input and output streams to unsigned char: text_out_pos will advance as bytes of Z-coded text are written, but text_in doesn't */ text_in = (unsigned char *) s_text; - text_out_pc = (unsigned char *) p; - text_out_limit = (unsigned char *) p_limit; + text_out_pos = 0; + text_out_limit = p_limit; text_out_overflow = FALSE; /* Remember the Z-chars total so that later we can subtract to find the @@ -415,14 +489,17 @@ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx) && (!is_abbreviation)) make_abbrevs_lookup(); - /* If we're storing the whole game text to memory, then add this text */ + /* If we're storing the whole game text to memory, then add this text. + We will put two newlines between each text and four at the very end. + (The optimise code does a lot of sloppy text[i+2], so the extra + two newlines past all_text_top are necessary.) */ if ((!is_abbreviation) && (store_the_text)) - { no_chars_transcribed += strlen(s_text)+2; - if (no_chars_transcribed >= MAX_TRANSCRIPT_SIZE) - memoryerror("MAX_TRANSCRIPT_SIZE", MAX_TRANSCRIPT_SIZE); - sprintf(all_text_top, "%s\n\n", s_text); - all_text_top += strlen(all_text_top); + { int addlen = strlen(s_text); + ensure_memory_list_available(&all_text_memlist, all_text_top+addlen+5); + sprintf(all_text+all_text_top, "%s\n\n\n\n", s_text); + /* Advance past two newlines. */ + all_text_top += (addlen+2); } if (transcript_switch) { @@ -439,6 +516,52 @@ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx) } } + /* Computing the optimal way to parse strings to insert abbreviations with dynamic programming */ + /* (ref: R.A. Wagner , "Common phrases and minimum-space text storage", Commun. ACM, 16 (3) (1973)) */ + /* We compute this optimal way here; it's stored in abbreviations_optimal_parse_schedule */ + if (economy_switch) + { + uchar *q, c; + int l, min_score, from; + int text_in_length; + + text_in_length = strlen( (char*) text_in); + ensure_memory_list_available(&abbreviations_optimal_parse_schedule_memlist, text_in_length); + ensure_memory_list_available(&abbreviations_optimal_parse_scores_memlist, text_in_length+1); + + abbreviations_optimal_parse_scores[text_in_length] = 0; + for(j=text_in_length-1; j>=0; j--) + { /* Initial values: empty schedule, score = just write the letter without abbreviating. */ + abbreviations_optimal_parse_schedule[j] = -1; + min_score = zchar_weight(text_in[j]) + abbreviations_optimal_parse_scores[j+1]; + /* If there's an abbreviation starting with that letter... */ + if ( (from = abbrevs_lookup[text_in[j]]) != -1) + { + c = text_in[j]; + /* Loop on all abbreviations starting with what is in c. */ + for (k=from, q=(uchar *)abbreviations_at+from*MAX_ABBREV_LENGTH; + (k 2 + abbreviations_optimal_parse_scores[j+l]) + { /* It is indeed smaller, so let's write it down in our schedule. */ + min_score = 2 + abbreviations_optimal_parse_scores[j+l]; + abbreviations_optimal_parse_schedule[j] = k; + } + NotMatched: ; + } + } + /* We gave it our best, this is the smallest we got. */ + abbreviations_optimal_parse_scores[j] = min_score; + } + } + + + if (!glulx_mode) { /* The empty string of Z-text is illegal, since it can't carry an end @@ -466,16 +589,24 @@ extern uchar *translate_text(uchar *p, uchar *p_limit, char *s_text, int strctx) } } - /* Try abbreviations if the economy switch set */ - - if ((economy_switch) && (!is_abbreviation) - && ((k=abbrevs_lookup[text_in[i]])!=-1)) - { if ((j=try_abbreviations_from(text_in, i, k))!=-1) - { /* abbreviations run from MAX_DYNAMIC_STRINGS to 96 */ - j += MAX_DYNAMIC_STRINGS; - write_z_char_z(j/32+1); write_z_char_z(j%32); - } + /* Try abbreviations if the economy switch set. */ + /* Look at the abbreviation schedule to see if we should abbreviate here. */ + /* Note: Just because the schedule has something doesn't mean we should abbreviate there; */ + /* sometimes you abbreviate before because it's better. If we have already replaced the */ + /* char by a '1', it means we're in the middle of an abbreviation; don't try to abbreviate then. */ + if ((economy_switch) && (!is_abbreviation) && text_in[i] != 1 && + ((j = abbreviations_optimal_parse_schedule[i]) != -1)) + { + /* Fill with 1s, which will get ignored by everyone else. */ + uchar *p = (uchar *)abbreviations_at+j*MAX_ABBREV_LENGTH; + for (k=0; p[k]!=0; k++) text_in[i+k]=1; + /* Actually write the abbreviation in the story file. */ + abbreviations[j].freq++; + /* Abbreviations run from MAX_DYNAMIC_STRINGS to 96. */ + j += MAX_DYNAMIC_STRINGS; + write_z_char_z(j/32+1); write_z_char_z(j%32); } + /* If Unicode switch set, use text_to_unicode to perform UTF-8 decoding */ @@ -495,12 +626,11 @@ advance as part of 'Zcharacter table':", unicode); /* '@' is the escape character in Inform string notation: the various possibilities are: - (printing only) @@decimalnumber : write this ZSCII char (0 to 1023) - @twodigits : write the abbreviation string with this - decimal number - - (any string context) + @twodigits or : write the abbreviation string with this + @(digits) decimal number + @(symbol) : write the abbreviation string with this + (constant) value @accentcode : this accented character: e.g., for @'e write an E-acute @{...} : this Unicode char (in hex) */ @@ -508,7 +638,7 @@ advance as part of 'Zcharacter table':", unicode); if (text_in[i]=='@') { if (text_in[i+1]=='@') { - /* @@... */ + /* @@... (ascii value) */ i+=2; j=atoi((char *) (text_in+i)); switch(j) @@ -526,6 +656,55 @@ advance as part of 'Zcharacter table':", unicode); } while (isdigit(text_in[i])) i++; i--; } + else if (text_in[i+1]=='(') + { + /* @(...) (dynamic string) */ + char dsymbol[MAX_IDENTIFIER_LENGTH+1]; + int len = 0, digits = 0; + i += 2; + /* This accepts "12xyz" as a symbol, which it really isn't, + but that just means it won't be found. */ + while ((text_in[i] == '_' || isalnum(text_in[i])) && len < MAX_IDENTIFIER_LENGTH) { + char ch = text_in[i++]; + if (isdigit(ch)) digits++; + dsymbol[len++] = ch; + } + dsymbol[len] = '\0'; + j = -1; + /* We would like to parse dsymbol as *either* a decimal + number or a constant symbol. */ + if (text_in[i] != ')' || len == 0) { + error("'@(...)' abbreviation must contain a symbol"); + } + else if (digits == len) { + /* all digits; parse as decimal */ + j = atoi(dsymbol); + } + else { + int sym = symbol_index(dsymbol, -1); + if ((symbols[sym].flags & UNKNOWN_SFLAG) || symbols[sym].type != CONSTANT_T || symbols[sym].marker) { + error_named("'@(...)' abbreviation expected a known constant value, but contained", dsymbol); + } + else { + symbols[sym].flags |= USED_SFLAG; + j = symbols[sym].value; + } + } + if (!glulx_mode && j >= 96) { + error_max_dynamic_strings(j); + j = -1; + } + if (j >= MAX_DYNAMIC_STRINGS) { + error_max_dynamic_strings(j); + j = -1; + } + if (j >= 0) { + write_z_char_z(j/32+1); write_z_char_z(j%32); + } + else { + write_z_char_z(' '); /* error fallback */ + } + } else if (isdigit(text_in[i+1])!=0) { int d1, d2; @@ -538,16 +717,22 @@ advance as part of 'Zcharacter table':", unicode); else { j = d1*10 + d2; - if (!glulx_mode && j >= 96) - { error("Z-machine dynamic strings are limited to 96"); - j = 0; + if (!glulx_mode && j >= 96) { + error_max_dynamic_strings(j); + j = -1; } if (j >= MAX_DYNAMIC_STRINGS) { - memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS); - j = 0; + /* Shouldn't get here with two digits */ + error_max_dynamic_strings(j); + j = -1; } i+=2; - write_z_char_z(j/32+1); write_z_char_z(j%32); + if (j >= 0) { + write_z_char_z(j/32+1); write_z_char_z(j%32); + } + else { + write_z_char_z(' '); /* error fallback */ + } } } else @@ -609,7 +794,6 @@ advance as part of 'Zcharacter table':", unicode); /* Flush the Z-characters output buffer and set the "end" bit */ end_z_chars(); - } else { @@ -673,6 +857,56 @@ string."); write_z_char_g(j); while (isdigit(text_in[i])) i++; i--; } + else if (text_in[i+1]=='(') { + char dsymbol[MAX_IDENTIFIER_LENGTH+1]; + int len = 0, digits = 0; + i += 2; + /* This accepts "12xyz" as a symbol, which it really isn't, + but that just means it won't be found. */ + while ((text_in[i] == '_' || isalnum(text_in[i])) && len < MAX_IDENTIFIER_LENGTH) { + char ch = text_in[i++]; + if (isdigit(ch)) digits++; + dsymbol[len++] = ch; + } + dsymbol[len] = '\0'; + j = -1; + /* We would like to parse dsymbol as *either* a decimal + number or a constant symbol. */ + if (text_in[i] != ')' || len == 0) { + error("'@(...)' abbreviation must contain a symbol"); + } + else if (digits == len) { + /* all digits; parse as decimal */ + j = atoi(dsymbol); + } + else { + int sym = symbol_index(dsymbol, -1); + if ((symbols[sym].flags & UNKNOWN_SFLAG) || symbols[sym].type != CONSTANT_T || symbols[sym].marker) { + error_named("'@(...)' abbreviation expected a known constant value, but contained", dsymbol); + } + else { + symbols[sym].flags |= USED_SFLAG; + j = symbols[sym].value; + } + } + if (j >= MAX_DYNAMIC_STRINGS) { + error_max_dynamic_strings(j); + j = -1; + } + if (j+1 >= no_dynamic_strings) + no_dynamic_strings = j+1; + if (j >= 0) { + write_z_char_g('@'); + write_z_char_g('D'); + write_z_char_g('A' + ((j >>12) & 0x0F)); + write_z_char_g('A' + ((j >> 8) & 0x0F)); + write_z_char_g('A' + ((j >> 4) & 0x0F)); + write_z_char_g('A' + ((j ) & 0x0F)); + } + else { + write_z_char_g(' '); /* error fallback */ + } + } else if (isdigit(text_in[i+1])) { int d1, d2; d1 = character_digit_value[text_in[i+1]]; @@ -687,17 +921,22 @@ string; substituting ' '."); i += 2; j = d1*10 + d2; if (j >= MAX_DYNAMIC_STRINGS) { - memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS); - j = 0; + error_max_dynamic_strings(j); + j = -1; } if (j+1 >= no_dynamic_strings) no_dynamic_strings = j+1; - write_z_char_g('@'); - write_z_char_g('D'); - write_z_char_g('A' + ((j >>12) & 0x0F)); - write_z_char_g('A' + ((j >> 8) & 0x0F)); - write_z_char_g('A' + ((j >> 4) & 0x0F)); - write_z_char_g('A' + ((j ) & 0x0F)); + if (j >= 0) { + write_z_char_g('@'); + write_z_char_g('D'); + write_z_char_g('A' + ((j >>12) & 0x0F)); + write_z_char_g('A' + ((j >> 8) & 0x0F)); + write_z_char_g('A' + ((j >> 4) & 0x0F)); + write_z_char_g('A' + ((j ) & 0x0F)); + } + else { + write_z_char_g(' '); /* error fallback */ + } } } else { @@ -784,41 +1023,31 @@ string; substituting '?'."); } } write_z_char_g(0); + zchars_trans_in_last_string=total_zchars_trans-zchars_trans_in_last_string; } if (text_out_overflow) - return NULL; + return -1; else - return((uchar *) text_out_pc); + return text_out_pos; } static int unicode_entity_index(int32 unicode) { - unicode_usage_t *uptr; int j; int buck = unicode % UNICODE_HASH_BUCKETS; - for (uptr = unicode_usage_hash[buck]; uptr; uptr=uptr->next) { - if (uptr->ch == unicode) + for (j = unicode_usage_hash[buck]; j >= 0; j=unicode_usage_entries[j].next) { + if (unicode_usage_entries[j].ch == unicode) break; } - if (uptr) { - j = (uptr - unicode_usage_entries); - } - else { - if (no_unicode_chars >= MAX_UNICODE_CHARS) { - memoryerror("MAX_UNICODE_CHARS", MAX_UNICODE_CHARS); - j = 0; - } - else { - j = no_unicode_chars; - no_unicode_chars++; - uptr = unicode_usage_entries + j; - uptr->ch = unicode; - uptr->next = unicode_usage_hash[buck]; - unicode_usage_hash[buck] = uptr; - } + if (j < 0) { + ensure_memory_list_available(&unicode_usage_entries_memlist, no_unicode_chars+1); + j = no_unicode_chars++; + unicode_usage_entries[j].ch = unicode; + unicode_usage_entries[j].next = unicode_usage_hash[buck]; + unicode_usage_hash[buck] = j; } return j; @@ -841,9 +1070,16 @@ void compress_game_text() int jx; int ch; int32 ix; + int max_char_set; huffbitlist_t bits; if (compression_switch) { + max_char_set = 257 + no_abbreviations + no_dynamic_strings + no_unicode_chars; + + huff_entities = my_calloc(sizeof(huffentity_t), max_char_set*2+1, + "huffman entities"); + hufflist = my_calloc(sizeof(huffentity_t *), max_char_set, + "huffman node list"); /* How many entities have we currently got? Well, 256 plus the string-terminator plus Unicode chars plus abbrevations plus @@ -857,8 +1093,8 @@ void compress_game_text() huff_dynam_start = entities; entities += no_dynamic_strings; - if (entities > MAX_CHARACTER_SET) - memoryerror("MAX_CHARACTER_SET",MAX_CHARACTER_SET); + if (entities > max_char_set) + compiler_error("Too many entities for max_char_set"); /* Characters */ for (jx=0; jx<256; jx++) { @@ -893,17 +1129,10 @@ void compress_game_text() no_huff_entities = 257; huff_unicode_start = 257; huff_abbrev_start = 257; - huff_dynam_start = 257+MAX_ABBREVS; + huff_dynam_start = 257+no_abbreviations; compression_table_size = 0; } - if (temporary_files_switch) { - fclose(Temp1_fp); - Temp1_fp=fopen(Temp1_Name,"rb"); - if (Temp1_fp==NULL) - fatalerror("I/O failure: couldn't reopen temporary file 1"); - } - if (compression_switch) { for (lx=0, ix=0; lx static_strings_extent || ch < 0) compiler_error("Read too much not-yet-compressed text."); @@ -1039,12 +1265,7 @@ void compress_game_text() without actually doing the compression. */ compression_string_size = 0; - if (temporary_files_switch) { - fseek(Temp1_fp, 0, SEEK_SET); - } - - if (no_strings >= MAX_NUM_STATIC_STRINGS) - memoryerror("MAX_NUM_STATIC_STRINGS", MAX_NUM_STATIC_STRINGS); + ensure_memory_list_available(&compressed_offsets_memlist, no_strings); for (lx=0, ix=0; lx static_strings_extent || ch < 0) compiler_error("Read too much not-yet-compressed text."); @@ -1182,11 +1400,16 @@ static void compress_makebits(int entnum, int depth, int prevbit, /* for compatibility with previous releases. */ /* ------------------------------------------------------------------------- */ +/* The complete game text. */ +static char *opttext; +static int32 opttextlen; + typedef struct tlb_s { char text[4]; int32 intab, occurrences; } tlb; -static tlb *tlbtab; +static tlb *tlbtab; /* Three-letter blocks (allocated up to no_occs) */ +static memory_list tlbtab_memlist; static int32 no_occs; static int32 *grandtable; @@ -1198,16 +1421,19 @@ typedef struct optab_s int32 location; char text[MAX_ABBREV_LENGTH]; } optab; -static optab *bestyet, *bestyet2; +static int32 MAX_BESTYET; +static optab *bestyet; /* High-score entries (up to MAX_BESTYET used/allocated) */ +static optab *bestyet2; /* The selected entries (up to selected used; allocated to MAX_ABBREVS) */ static int pass_no; -static char *sub_buffer; - static void optimise_pass(void) -{ int32 i; int t1, t2; +{ + TIMEVALUE t1, t2; + float duration; + int32 i; int32 j, j2, k, nl, matches, noflags, score, min, minat=0, x, scrabble, c; - for (i=0; i<256; i++) bestyet[i].length=0; + for (i=0; i= 2) { + printf("Pass %d, %4ld/%ld '%s' (%ld occurrences) ", + pass_no, (long int) i, (long int) no_occs, tlbtab[i].text, + (long int) tlbtab[i].occurrences); + } + TIMEVALUE_NOW(&t1); for (j=0; j=2)&&(nl<=62)) + while ((noflags>=2)&&(nl-nl)&&(x= 2) { + TIMEVALUE_NOW(&t2); + duration = TIMEVALUE_DIFFERENCE(&t1, &t2); + printf(" (%.4f seconds)\n", duration); + } } } } @@ -1304,22 +1530,35 @@ static int any_overlap(char *s1, char *s2) return(0); } -#define MAX_TLBS 8000 - extern void optimise_abbreviations(void) -{ int32 i, j, t, max=0, MAX_GTABLE; +{ int32 i, j, tcount, max=0, MAX_GTABLE; int32 j2, selected, available, maxat=0, nl; - tlb test; + if (opttext == NULL) + return; + + /* We insist that the first two abbreviations will be ". " and ", ". */ + if (MAX_ABBREVS < 2) + return; + + /* Note that it's safe to access opttext[opttextlen+2]. There are + two newlines and a null beyond opttextlen. */ + printf("Beginning calculation of optimal abbreviations...\n"); pass_no = 0; - tlbtab=my_calloc(sizeof(tlb), MAX_TLBS, "tlb table"); no_occs=0; - sub_buffer=my_calloc(sizeof(char), 4000, "sub_buffer"); - for (i=0; i=2) - { tlbtab[no_occs]=test; - tlbtab[no_occs].intab=t; t+=tlbtab[no_occs].occurrences; + { + ensure_memory_list_available(&tlbtab_memlist, no_occs+1); + tlbtab[no_occs]=test; + tlbtab[no_occs].intab=tcount; + tcount += tlbtab[no_occs].occurrences; if (max= 1) { + printf("Cross-reference table (%ld entries) built...\n", + (long int) no_occs); + } /* for (i=0; i0)&&(selected<64)) - { printf("Pass %d\n", ++pass_no); - + for (i=0; i0)&&(selected= 1) { + printf("Pass %d\n", pass_no); + } + optimise_pass(); available=0; - for (i=0; i<256; i++) + for (i=0; i0) printf("%02d: %4d %4d '%s'\n", i, bestyet[i].score, bestyet[i].popularity, bestyet[i].text); @@ -1438,40 +1685,44 @@ extern void optimise_abbreviations(void) do { max=0; - for (i=0; i<256; i++) + for (i=0; i0) - { bestyet2[selected++]=bestyet[maxat]; - - printf( - "Selection %2ld: '%s' (repeated %ld times, scoring %ld)\n", - (long int) selected,bestyet[maxat].text, - (long int) bestyet[maxat].popularity, - (long int) bestyet[maxat].score); + { + char testtext[4]; + bestyet2[selected++]=bestyet[maxat]; + + if (optabbrevs_trace_setting >= 1) { + printf( + "Selection %2ld: '%s' (repeated %ld times, scoring %ld)\n", + (long int) selected,bestyet[maxat].text, + (long int) bestyet[maxat].popularity, + (long int) bestyet[maxat].score); + } - test.text[0]=bestyet[maxat].text[0]; - test.text[1]=bestyet[maxat].text[1]; - test.text[2]=bestyet[maxat].text[2]; - test.text[3]=0; + testtext[0]=bestyet[maxat].text[0]; + testtext[1]=bestyet[maxat].text[1]; + testtext[2]=bestyet[maxat].text[2]; + testtext[3]=0; for (i=0; i0)&& (any_overlap(bestyet[maxat].text,bestyet[i].text)==1)) { bestyet[i].score=0; @@ -1479,7 +1730,7 @@ extern void optimise_abbreviations(void) bestyet[i].text); */ } } - } while ((max>0)&&(available>0)&&(selected<64)); + } while ((max>0)&&(available>0)&&(selected */ /* 4 or 6 bytes byte byte byte */ /* */ -/* For Glulx, the form is instead: (But see below about Unicode-valued */ -/* dictionaries and my heinie.) */ +/* For Glulx, the form is instead: (See below about Unicode-valued */ +/* dictionaries and DICT_WORD_BYTES.) */ /* */ /* */ -/* $60 DICT_WORD_SIZE short short short */ +/* $60 DICT_WORD_BYTES short short short */ /* */ /* These records are stored in "accession order" (i.e. in order of their */ /* first being received by these routines) and only alphabetically sorted */ @@ -1538,28 +1789,31 @@ extern void optimise_abbreviations(void) /* fields. (The high bytes are $DICT_WORD_SIZE+1/3/5.) */ /* ------------------------------------------------------------------------- */ -uchar *dictionary, /* (These two pointers are externally +uchar *dictionary; /* (These two variables are externally used only in "tables.c" when building the story-file) */ - *dictionary_top; /* Pointer to next free record */ +static memory_list dictionary_memlist; +int32 dictionary_top; /* Position of the next free record + in dictionary (i.e., the current + number of bytes) */ int dict_entries; /* Total number of records entered */ /* ------------------------------------------------------------------------- */ -/* dict_word is a typedef for a struct of 6 unsigned chars (defined in */ -/* "header.h"): it holds the (4 or) 6 bytes of Z-coded text of a word. */ +/* dict_word was originally a typedef for a struct of 6 unsigned chars. */ +/* It held the (4 or) 6 bytes of Z-coded text of a word. */ /* Usefully, because the PAD character 5 is < all alphabetic characters, */ /* alphabetic order corresponds to numeric order. For this reason, the */ /* dict_word is called the "sort code" of the original text word. */ /* */ -/* ###- In modifying the compiler, I've found it easier to discard the */ +/* In modifying the compiler for Glulx, I found it easier to discard the */ /* typedef, and operate directly on uchar arrays of length DICT_WORD_SIZE. */ /* In Z-code, DICT_WORD_SIZE will be 6, so the Z-code compiler will work */ /* as before. In Glulx, it can be any value up to MAX_DICT_WORD_SIZE. */ /* (That limit is defined as 40 in the header; it exists only for a few */ /* static buffers, and can be increased without using significant memory.) */ /* */ -/* ###- Well, that certainly bit me on the butt, didn't it. In further */ +/* ...Well, that certainly bit me on the butt, didn't it. In further */ /* modifying the compiler to generate a Unicode dictionary, I have to */ /* store four-byte values in the uchar array. This is handled by making */ /* the array size DICT_WORD_BYTES (which is DICT_WORD_SIZE*DICT_CHAR_SIZE).*/ @@ -1806,10 +2060,13 @@ typedef struct dict_tree_node_s char colour; /* The colour of the branch to the parent */ } dict_tree_node; -static dict_tree_node *dtree; +static dict_tree_node *dtree; /* Allocated to dict_entries */ +static memory_list dtree_memlist; + +static uchar *dict_sort_codes; /* Allocated to dict_entries*DICT_WORD_BYTES */ +static memory_list dict_sort_codes_memlist; -int *final_dict_order; -static uchar *dict_sort_codes; +int *final_dict_order; /* Allocated at sort_dictionary() time */ static void dictionary_begin_pass(void) { @@ -1817,10 +2074,12 @@ static void dictionary_begin_pass(void) /* Glulx has a 4-byte header instead. */ if (!glulx_mode) - dictionary_top=dictionary+7; + dictionary_top = 7; else - dictionary_top=dictionary+4; + dictionary_top = 4; + ensure_memory_list_available(&dictionary_memlist, dictionary_top); + root = VACANT; dict_entries = 0; } @@ -1836,6 +2095,9 @@ static void recursively_sort(int node) extern void sort_dictionary(void) { int i; + + final_dict_order = my_calloc(sizeof(int), dict_entries, "final dictionary ordering table"); + if (module_switch) { for (i=0; i 3) { p[4]=prepared_sort[4]; p[5]=prepared_sort[5]; } - p[res]=x; p[res+1]=y; p[res+2]=z; + p[res]=x; p[res+1]=y; + if (!ZCODE_LESS_DICT_DATA) p[res+2]=z; if (x & 128) p[res] = (p[res])|number_and_case; - dictionary_top += res+3; + dictionary_top += DICT_ENTRY_BYTE_LENGTH; } else { int i; + ensure_memory_list_available(&dictionary_memlist, dictionary_top + DICT_ENTRY_BYTE_LENGTH); p = dictionary + 4 + DICT_ENTRY_BYTE_LENGTH*dict_entries; p[0] = 0x60; /* type byte -- dict word */ @@ -2052,7 +2319,7 @@ extern void dictionary_set_verb_number(char *dword, int to) if (i!=0) { if (!glulx_mode) { - p=dictionary+7+(i-1)*(3+res)+res; + p=dictionary+7+(i-1)*DICT_ENTRY_BYTE_LENGTH+res; p[1]=to; } else { @@ -2156,6 +2423,10 @@ extern void word_to_ascii(uchar *p, char *results) encoded_word[7] = 8*(((int) p[4])&0x3) + (((int) p[5])&0xe0)/32; encoded_word[8] = ((int) p[5])&0x1f; } + else + { + encoded_word[6] = encoded_word[7] = encoded_word[8] = 0; + } shift = 0; cc = 0; for (i=0; i< ((version_number==3)?6:9); i++) @@ -2185,15 +2456,49 @@ extern void word_to_ascii(uchar *p, char *results) results[cc] = 0; } -static void recursively_show_z(int node) +/* Print a dictionary word to stdout. + (This assumes that d_show_buf is null.) + */ +void print_dict_word(int node) +{ + uchar *p; + int cprinted; + + if (!glulx_mode) { + char textual_form[32]; + p = (uchar *)dictionary + 7 + DICT_ENTRY_BYTE_LENGTH*node; + + word_to_ascii(p, textual_form); + + for (cprinted = 0; textual_form[cprinted]!=0; cprinted++) + show_char(textual_form[cprinted]); + } + else { + p = (uchar *)dictionary + 4 + DICT_ENTRY_BYTE_LENGTH*node; + + for (cprinted = 0; cprinted= 1) + { + if (level >= 2) { + for (i=0; i= 1) + { int flagpos = (DICT_CHAR_SIZE == 1) ? (DICT_WORD_SIZE+1) : (DICT_WORD_BYTES+4); int flags = (p[flagpos+0] << 8) | (p[flagpos+1]); int verbnum = (p[flagpos+2] << 8) | (p[flagpos+3]); + if (level >= 2) { + for (i=0; i= VENEER_ROUTINES) { + return "???"; + } + if (!glulx_mode) { + return VRs_z[code].name; + } + else { + return VRs_g[code].name; + } +} + static void compile_symbol_table_routine(void) { int32 j, nl, arrays_l, routines_l, constants_l; assembly_operand AO, AO2, AO3; /* Assign local var names for the benefit of the debugging information - file. */ - local_variable_texts[0] = "dummy1"; - local_variable_texts[1] = "dummy2"; + file. (We don't set local_variable.keywords because we're not + going to be parsing any code.) */ + strcpy(local_variable_names[0].text, "dummy1"); + strcpy(local_variable_names[1].text, "dummy2"); veneer_mode = TRUE; j = symbol_index("Symb__Tab", -1); assign_symbol(j, assemble_routine_header(2, FALSE, "Symb__Tab", FALSE, j), ROUTINE_T); - sflags[j] |= SYSTEM_SFLAG + USED_SFLAG; - if (trace_fns_setting==3) sflags[j] |= STAR_SFLAG; + symbols[j].flags |= SYSTEM_SFLAG + USED_SFLAG; + if (trace_fns_setting==3) symbols[j].flags |= STAR_SFLAG; if (!glulx_mode) { if (define_INFIX_switch == FALSE) { assemblez_0(rfalse_zc); - variable_usage[1] = TRUE; - variable_usage[2] = TRUE; + variables[1].usage = TRUE; + variables[2].usage = TRUE; assemble_routine_end(FALSE, null_debug_locations); veneer_mode = FALSE; return; @@ -2238,16 +2256,16 @@ static void compile_symbol_table_routine(void) nl = next_label++; sequence_point_follows = FALSE; assemblez_2_branch(je_zc, AO, AO2, nl, FALSE); - AO3.value = array_sizes[j]; + AO3.value = arrays[j].size; AO3.marker = 0; assemblez_store(temp_var2, AO3); - AO3.value = array_types[j]; - if (sflags[array_symbols[j]] & (INSF_SFLAG+SYSTEM_SFLAG)) + AO3.value = arrays[j].type; + if (symbols[arrays[j].symbol].flags & (INSF_SFLAG+SYSTEM_SFLAG)) AO3.value = AO3.value + 16; AO3.marker = 0; assemblez_store(temp_var3, AO3); - AO3.value = svals[array_symbols[j]]; - AO3.marker = (!array_locs[j] ? ARRAY_MV : STATIC_ARRAY_MV); + AO3.value = symbols[arrays[j].symbol].value; + AO3.marker = (!arrays[j].loc ? ARRAY_MV : STATIC_ARRAY_MV); assemblez_1(ret_zc, AO3); assemble_label_no(nl); } @@ -2263,11 +2281,11 @@ static void compile_symbol_table_routine(void) sequence_point_follows = FALSE; assemblez_2_branch(je_zc, AO, AO2, nl, FALSE); AO3.value = 0; - if (sflags[named_routine_symbols[j]] + if (symbols[named_routine_symbols[j]].flags & (INSF_SFLAG+SYSTEM_SFLAG)) AO3.value = 16; AO3.marker = 0; assemblez_store(temp_var3, AO3); - AO3.value = svals[named_routine_symbols[j]]; + AO3.value = symbols[named_routine_symbols[j]].value; AO3.marker = IROUTINE_MV; assemblez_1(ret_zc, AO3); assemble_label_no(nl); @@ -2277,9 +2295,9 @@ static void compile_symbol_table_routine(void) assemble_label_no(constants_l); for (j=0, no_named_constants=0; j= 16 && tok < 48) { + printf(" noun=%d", tok-16); + } + else if (tok >= 48 && tok < 80) { + printf(" routine=%d", tok-48); + } + else if (tok >= 80 && tok < 128) { + printf(" scope=%d", tok-80); + } + else if (tok >= 128 && tok < 160) { + printf(" attr=%d", tok-128); + } + else if (tok >= 160) { + printf(" prep=%d", tok); + } + else { + printf(" ???"); + } + } + } + + printf(" -> "); + actsym = actions[action].symbol; + str = (symbols[actsym].name); + len = strlen(str) - 3; /* remove "__A" */ + for (ix=0; ix> 4) & 0x03; + mark += 1; + tokdat = (grammar_lines[mark] << 8) | (grammar_lines[mark+1]); + mark += 2; + } + else { + toktype = grammar_lines[mark] & 0x0F; + tokalt = (grammar_lines[mark] >> 4) & 0x03; + mark += 1; + tokdat = (grammar_lines[mark] << 24) | (grammar_lines[mark+1] << 16) | (grammar_lines[mark+2] << 8) | (grammar_lines[mark+3]); + mark += 4; + } + + if (tokalt == 3 || tokalt == 1) + printf(" /"); + + switch (toktype) { + case 1: + switch (tokdat) { + case 0: printf(" noun"); break; + case 1: printf(" held"); break; + case 2: printf(" multi"); break; + case 3: printf(" multiheld"); break; + case 4: printf(" multiexcept"); break; + case 5: printf(" multiinside"); break; + case 6: printf(" creature"); break; + case 7: printf(" special"); break; + case 8: printf(" number"); break; + case 9: printf(" topic"); break; + default: printf(" ???"); break; + } + break; + case 2: + printf(" '"); + print_dict_word(tokdat); + printf("'"); + break; + case 3: + printf(" noun=%d", tokdat); + break; + case 4: + printf(" attr=%d", tokdat); + break; + case 5: + printf(" scope=%d", tokdat); + break; + case 6: + printf(" routine=%d", tokdat); + break; + default: + printf(" ???%d:%d", toktype, tokdat); + break; + } + } + printf(" -> "); + actsym = actions[action].symbol; + str = (symbols[actsym].name); + len = strlen(str) - 3; /* remove "__A" */ + for (ix=0; ix"); debug_file_printf("##%s", token_text); - debug_file_printf("%d", svals[i]); + debug_file_printf("%d", symbols[i].value); get_next_token(); write_debug_locations (get_token_location_end(beginning_debug_location)); @@ -193,33 +374,34 @@ extern assembly_operand action_of_name(char *name) snprintf(action_sub, MAX_IDENTIFIER_LENGTH+4, "%s__A", name); j = symbol_index(action_sub, -1); - if (stypes[j] == FAKE_ACTION_T) + if (symbols[j].type == FAKE_ACTION_T) { INITAO(&AO); - AO.value = svals[j]; + AO.value = symbols[j].value; if (!glulx_mode) AO.type = LONG_CONSTANT_OT; else set_constant_ot(&AO); - sflags[j] |= USED_SFLAG; + symbols[j].flags |= USED_SFLAG; return AO; } - if (sflags[j] & UNKNOWN_SFLAG) + if (symbols[j].flags & UNKNOWN_SFLAG) { - if (no_actions>=MAX_ACTIONS) memoryerror("MAX_ACTIONS",MAX_ACTIONS); + ensure_memory_list_available(&actions_memlist, no_actions+1); new_action(name, no_actions); - action_symbol[no_actions] = j; + actions[no_actions].symbol = j; + actions[no_actions].byte_offset = 0; /* fill in later */ assign_symbol(j, no_actions++, CONSTANT_T); - sflags[j] |= ACTION_SFLAG; + symbols[j].flags |= ACTION_SFLAG; } - sflags[j] |= USED_SFLAG; + symbols[j].flags |= USED_SFLAG; INITAO(&AO); - AO.value = svals[j]; + AO.value = symbols[j].value; AO.marker = ACTION_MV; if (!glulx_mode) { AO.type = (module_switch)?LONG_CONSTANT_OT:SHORT_CONSTANT_OT; - if (svals[j] >= 256) AO.type = LONG_CONSTANT_OT; + if (symbols[j].value >= 256) AO.type = LONG_CONSTANT_OT; } else { AO.type = CONSTANT_OT; @@ -233,27 +415,27 @@ extern void find_the_actions(void) char action_sub[MAX_IDENTIFIER_LENGTH+4]; if (module_switch) - for (i=0; i= MAX_ADJECTIVES) - memoryerror("MAX_ADJECTIVES", MAX_ADJECTIVES); + if (no_adjectives >= 255) { + error("Grammar version 1 cannot support more than 255 prepositions"); + return 0; + } + if (ZCODE_LESS_DICT_DATA && !glulx_mode) { + /* We need to use #dict_par3 for the preposition number. */ + error("Grammar version 1 cannot be used with ZCODE_LESS_DICT_DATA"); + return 0; + } + ensure_memory_list_available(&adjectives_memlist, no_adjectives+1); + ensure_memory_list_available(&adjective_sort_code_memlist, (no_adjectives+1) * DICT_WORD_BYTES); dictionary_prepare(English_word, new_sort_code); for (i=0; i MAX_VERB_WORD_SIZE+4) error_numbered("Verb word is too long -- max length is", MAX_VERB_WORD_SIZE); + ensure_memory_list_available(&English_verb_list_memlist, English_verb_list_size + entrysize); + top = English_verb_list + English_verb_list_size; English_verb_list_size += entrysize; - if (English_verb_list_size >= MAX_VERBSPACE) - memoryerror("MAX_VERBSPACE", MAX_VERBSPACE); - - English_verb_list_top[0] = entrysize; - English_verb_list_top[1] = number/256; - English_verb_list_top[2] = number%256; - strcpy(English_verb_list_top+3, English_verb); - English_verb_list_top += entrysize; + + top[0] = entrysize; + top[1] = number/256; + top[2] = number%256; + strcpy(top+3, English_verb); } static int get_verb(void) @@ -395,9 +604,25 @@ static int get_verb(void) /* Grammar lines for Verb/Extend directives. */ /* ------------------------------------------------------------------------- */ +static void ensure_grammar_lines_available(int verbnum, int num) +{ + /* Note that the size field always starts positive. */ + if (num > Inform_verbs[verbnum].size) { + int newsize = 2*num+4; + my_realloc(&Inform_verbs[verbnum].l, sizeof(int) * Inform_verbs[verbnum].size, sizeof(int) * newsize, "grammar lines for one verb"); + Inform_verbs[verbnum].size = newsize; + } +} + static int grammar_line(int verbnum, int line) { - /* Parse a grammar line, to be written into grammar_lines[mark] onward. + /* Parse a grammar line, to be written into grammar_lines[] starting + at grammar_lines_top. grammar_lines_top is left at the end + of the new line. + + This stores the line position in Inform_verbs[verbnum].l[line]. + (It does not increment Inform_verbs[verbnum].lines; the caller + must do that.) Syntax: * ... -> @@ -435,32 +660,9 @@ static int grammar_line(int verbnum, int line) return FALSE; } - /* Have we run out of lines or token space? */ - - if (line >= MAX_LINES_PER_VERB) - { discard_token_location(beginning_debug_location); - error("Too many lines of grammar for verb. This maximum is built \ -into Inform, so suggest rewriting grammar using general parsing routines"); - return(FALSE); - } - - /* Internally, a line can be up to 3*32 + 1 + 2 = 99 bytes long */ - /* In Glulx, that's 5*32 + 4 = 164 bytes */ - mark = grammar_lines_top; - if (!glulx_mode) { - if (mark + 100 >= MAX_LINESPACE) - { discard_token_location(beginning_debug_location); - memoryerror("MAX_LINESPACE", MAX_LINESPACE); - } - } - else { - if (mark + 165 >= MAX_LINESPACE) - { discard_token_location(beginning_debug_location); - memoryerror("MAX_LINESPACE", MAX_LINESPACE); - } - } + ensure_grammar_lines_available(verbnum, line+1); Inform_verbs[verbnum].l[line] = mark; if (!glulx_mode) { @@ -471,6 +673,7 @@ into Inform, so suggest rewriting grammar using general parsing routines"); mark = mark + 3; TOKEN_SIZE = 5; } + ensure_memory_list_available(&grammar_lines_memlist, mark); grammar_token = 0; last_was_slash = TRUE; slash_mode = FALSE; no_grammar_lines++; @@ -522,7 +725,7 @@ into Inform, so suggest rewriting grammar using general parsing routines"); get_next_token(); if ((token_type != SYMBOL_TT) - || (stypes[token_value] != ROUTINE_T)) + || (symbols[token_value].type != ROUTINE_T)) { discard_token_location(beginning_debug_location); ebf_error("routine name after 'noun='", token_text); panic_mode_error_recovery(); @@ -530,12 +733,12 @@ into Inform, so suggest rewriting grammar using general parsing routines"); } if (grammar_version_number == 1) bytecode - = 16 + make_parsing_routine(svals[token_value]); + = 16 + make_parsing_routine(symbols[token_value].value); else { bytecode = 0x83; - wordcode = svals[token_value]; + wordcode = symbols[token_value].value; } - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; } else { put_token_back(); @@ -586,7 +789,7 @@ are using Library 6/3 or later"); get_next_token(); if ((token_type != SYMBOL_TT) - || (stypes[token_value] != ROUTINE_T)) + || (symbols[token_value].type != ROUTINE_T)) { discard_token_location(beginning_debug_location); ebf_error("routine name after 'scope='", token_text); panic_mode_error_recovery(); @@ -595,9 +798,9 @@ are using Library 6/3 or later"); if (grammar_version_number == 1) bytecode = 80 + - make_parsing_routine(svals[token_value]); - else { bytecode = 0x85; wordcode = svals[token_value]; } - sflags[token_value] |= USED_SFLAG; + make_parsing_routine(symbols[token_value].value); + else { bytecode = 0x85; wordcode = symbols[token_value].value; } + symbols[token_value].flags |= USED_SFLAG; } else if ((token_type == SEP_TT) && (token_value == SETEQUALS_SEP)) { discard_token_location(beginning_debug_location); @@ -608,25 +811,25 @@ are using Library 6/3 or later"); else { /* or tokens */ if ((token_type != SYMBOL_TT) - || ((stypes[token_value] != ATTRIBUTE_T) - && (stypes[token_value] != ROUTINE_T))) + || ((symbols[token_value].type != ATTRIBUTE_T) + && (symbols[token_value].type != ROUTINE_T))) { discard_token_location(beginning_debug_location); error_named("No such grammar token as", token_text); panic_mode_error_recovery(); return FALSE; } - if (stypes[token_value]==ATTRIBUTE_T) + if (symbols[token_value].type==ATTRIBUTE_T) { if (grammar_version_number == 1) - bytecode = 128 + svals[token_value]; - else { bytecode = 4; wordcode = svals[token_value]; } + bytecode = 128 + symbols[token_value].value; + else { bytecode = 4; wordcode = symbols[token_value].value; } } else { if (grammar_version_number == 1) bytecode = 48 + - make_parsing_routine(svals[token_value]); - else { bytecode = 0x86; wordcode = svals[token_value]; } + make_parsing_routine(symbols[token_value].value); + else { bytecode = 0x86; wordcode = symbols[token_value].value; } } - sflags[token_value] |= USED_SFLAG; + symbols[token_value].flags |= USED_SFLAG; } grammar_token++; no_grammar_tokens++; @@ -641,6 +844,7 @@ tokens in any line (unless you're compiling with library 6/3 or later)"); error("'/' can only be applied to prepositions"); bytecode |= 0x10; } + ensure_memory_list_available(&grammar_lines_memlist, mark+5); grammar_lines[mark++] = bytecode; if (!glulx_mode) { grammar_lines[mark++] = wordcode/256; @@ -656,6 +860,7 @@ tokens in any line (unless you're compiling with library 6/3 or later)"); } while (TRUE); + ensure_memory_list_available(&grammar_lines_memlist, mark+1); grammar_lines[mark++] = 15; grammar_lines_top = mark; @@ -702,6 +907,7 @@ Library 6/3 or later"); debug_file_printf(""); } + ensure_memory_list_available(&grammar_lines_memlist, mark+3); if (!glulx_mode) { if (reverse_action) j = j + 0x400; @@ -731,8 +937,8 @@ extern void make_verb(void) int Inform_verb, meta_verb_flag=FALSE, verb_equals_form=FALSE; - char *English_verbs_given[MAX_VERB_SYNONYMS]; - int no_given = 0, i; + int no_given = 0, verbs_given_pos = 0; + int i, pos; directive_keywords.enabled = TRUE; @@ -745,11 +951,11 @@ extern void make_verb(void) while ((token_type == DQ_TT) || (token_type == SQ_TT)) { - if (no_given >= MAX_VERB_SYNONYMS) { - error("Too many synonyms in a Verb directive."); - panic_mode_error_recovery(); return; - } - English_verbs_given[no_given++] = token_text; + int len = strlen(token_text) + 1; + ensure_memory_list_available(&English_verbs_given_memlist, verbs_given_pos + len); + strcpy(English_verbs_given+verbs_given_pos, token_text); + verbs_given_pos += len; + no_given++; get_next_token(); } @@ -768,16 +974,25 @@ extern void make_verb(void) ebf_error("';' after English verb", token_text); } else - { Inform_verb = no_Inform_verbs; - if (no_Inform_verbs == MAX_VERBS) - memoryerror("MAX_VERBS",MAX_VERBS); + { verb_equals_form = FALSE; + if (!glulx_mode && no_Inform_verbs >= 255) { + error("Z-code is limited to 255 verbs."); + panic_mode_error_recovery(); return; + } + ensure_memory_list_available(&Inform_verbs_memlist, no_Inform_verbs+1); + Inform_verb = no_Inform_verbs; + Inform_verbs[no_Inform_verbs].lines = 0; + Inform_verbs[no_Inform_verbs].size = 4; + Inform_verbs[no_Inform_verbs].l = my_malloc(sizeof(int) * Inform_verbs[no_Inform_verbs].size, "grammar lines for one verb"); } - for (i=0; i= 255) { + error("Z-code is limited to 255 verbs."); + panic_mode_error_recovery(); return; + } + ensure_memory_list_available(&Inform_verbs_memlist, no_Inform_verbs+1); + l = -1; while (get_next_token(), ((token_type == DQ_TT) || (token_type == SQ_TT))) { Inform_verb = get_verb(); @@ -836,8 +1055,15 @@ extern void extend_verb(void) /* Copy the old Inform-verb into a new one which the list of English-verbs given have had their dictionary entries modified to point to */ + /* (We are copying entry Inform_verb to no_Inform_verbs here.) */ - Inform_verbs[no_Inform_verbs] = Inform_verbs[Inform_verb]; + l = Inform_verbs[Inform_verb].lines; /* number of lines to copy */ + + Inform_verbs[no_Inform_verbs].lines = l; + Inform_verbs[no_Inform_verbs].size = l+4; + Inform_verbs[no_Inform_verbs].l = my_malloc(sizeof(int) * Inform_verbs[no_Inform_verbs].size, "grammar lines for one verb"); + for (k=0; k0; k--) Inform_verbs[Inform_verb].l[k+lines] = Inform_verbs[Inform_verb].l[k-1+lines]; + } } while (grammar_line(Inform_verb, lines++)); if (extend_mode == EXTEND_FIRST) - { Inform_verbs[Inform_verb].lines = l+lines-1; - for (k=0; k