From 56a5292888e1d46fe3033cd1d5c636051692453f Mon Sep 17 00:00:00 2001 From: Jason Self Date: Sat, 17 Feb 2024 07:57:34 -0800 Subject: [PATCH] Update to Inform v6.42 Commit e528e4802e3fdc2364a9a92aa127e7a5091a8d4e dated February 10 2024. These changes are similiarly relicensed to GPL per Section 4(c)(ii) of the Artistic License 2.0. --- configure.ac | 2 +- src/arrays.c | 31 ++-- src/asm.c | 466 ++++++++++++++++++++++++++++++++----------------- src/bpatch.c | 72 +++++++- src/chars.c | 9 +- src/directs.c | 132 +++++++------- src/errors.c | 87 +++++++-- src/expressc.c | 75 ++++++-- src/expressp.c | 268 +++++++++++++++++++++------- src/files.c | 69 ++++---- src/header.h | 158 ++++++++++------- src/inform.c | 47 ++--- src/lexer.c | 270 ++++++++++++++++++++-------- src/memory.c | 91 +++++++--- src/objects.c | 144 ++++++++------- src/states.c | 182 ++++++++++++------- src/symbols.c | 226 +++++++++++++++--------- src/syntax.c | 303 ++++++++++++++++++++------------ src/tables.c | 265 +++++++++++++++++----------- src/text.c | 430 ++++++++++++++++++++++++++++++++------------- src/veneer.c | 87 +++++++-- src/verbs.c | 151 +++++++++++----- 22 files changed, 2414 insertions(+), 1151 deletions(-) diff --git a/configure.ac b/configure.ac index 0ff8ced..971edc4 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with Inform. If not, see https://gnu.org/licenses/ -AC_INIT([inform], [6.41], [j@jxself.org]) +AC_INIT([inform], [6.42], [j@jxself.org]) AM_INIT_AUTOMAKE([foreign]) AC_OUTPUT(Makefile src/Makefile) diff --git a/src/arrays.c b/src/arrays.c index c48cc65..82caa7c 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.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -292,7 +292,7 @@ extern void make_global() int name_length; assembly_operand AO; - int32 globalnum; + uint32 globalnum; int32 global_symbol; debug_location_beginning beginning_debug_location = get_token_location_beginning(); @@ -322,7 +322,7 @@ extern void make_global() if (token_type != SYMBOL_TT) { discard_token_location(beginning_debug_location); - ebf_error("new global variable name", token_text); + ebf_curtoken_error("new global variable name"); panic_mode_error_recovery(); return; } @@ -413,7 +413,7 @@ extern void make_global() 4*globalnum); } - if (globalnum < 0 || globalnum >= global_initial_value_memlist.count) + if (globalnum >= global_initial_value_memlist.count) compiler_error("Globalnum out of range"); global_initial_value[globalnum] = AO.value; @@ -456,7 +456,7 @@ extern void make_array() if (token_type != SYMBOL_TT) { discard_token_location(beginning_debug_location); - ebf_error("new array name", token_text); + ebf_curtoken_error("new array name"); panic_mode_error_recovery(); return; } @@ -492,7 +492,7 @@ extern void make_array() if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) { discard_token_location(beginning_debug_location); - ebf_error("array definition", token_text); + ebf_curtoken_error("array definition"); put_token_back(); return; } @@ -516,8 +516,7 @@ extern void make_array() array_type = BUFFER_ARRAY; else { discard_token_location(beginning_debug_location); - ebf_error - ("'->', '-->', 'string', 'table' or 'buffer'", token_text); + ebf_curtoken_error("'->', '-->', 'string', 'table' or 'buffer'"); panic_mode_error_recovery(); return; } @@ -632,6 +631,8 @@ extern void make_array() put_token_back(); AO = parse_expression(ARRAY_CONTEXT); + if (AO.marker == ERROR_MV) + break; if (i == 0) { get_next_token(); @@ -656,7 +657,7 @@ extern void make_array() get_next_token(); if (token_type != DQ_TT) - { ebf_error("literal text in double-quotes", token_text); + { ebf_curtoken_error("literal text in double-quotes"); token_text = "error"; } @@ -705,6 +706,7 @@ advance as part of 'Zcharacter table':", unicode); i = 0; while (TRUE) { + assembly_operand AO; /* 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. */ @@ -719,11 +721,14 @@ advance as part of 'Zcharacter table':", unicode); been missed, and the programmer is now starting a new routine */ - ebf_error("']'", token_text); + ebf_curtoken_error("']'"); put_token_back(); break; } put_token_back(); - array_entry(i, is_static, parse_expression(ARRAY_CONTEXT)); + AO = parse_expression(ARRAY_CONTEXT); + if (AO.marker == ERROR_MV) + break; + array_entry(i, is_static, AO); i++; } } @@ -864,7 +869,7 @@ extern void arrays_allocate_arrays(void) "global variable values"); initialise_memory_list(¤t_array_name, - sizeof(char), MAX_IDENTIFIER_LENGTH+1, NULL, + sizeof(char), 32, NULL, "array name currently being defined"); } diff --git a/src/asm.c b/src/asm.c index 2736ad1..858ff9d 100644 --- a/src/asm.c +++ b/src/asm.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "asm" : The Inform assembler */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -92,11 +92,9 @@ static char opcode_syntax_string[128]; /* Text buffer holding the correct static int routine_symbol; /* The symbol index of the routine currently being compiled */ static memory_list current_routine_name; /* The name of the routine currently - being compiled. (This may be longer - than MAX_IDENTIFIER_LENGTH, e.g. for - an "obj.prop" property routine.) */ -static int routine_locals; /* The number of local variables used by - the routine currently being compiled */ + being compiled. (This may not be a + simple symbol, e.g. for an "obj.prop" + property routine.) */ static int32 routine_start_pc; @@ -322,7 +320,7 @@ extern int is_variable_ot(int otval) extern char *variable_name(int32 i) { if (i==0) return("sp"); - if (ioperand[1]; AMO_2 = AI->operand[2]; if ((AMO_0.type == LOCALVAR_OT) && (AMO_0.value == 0)) { - // addr is on the stack + /* addr is on the stack */ assembleg_store(temp_var3, stack_pointer); assembleg_3(aload_gc, temp_var3, one_operand, AMO_1); assembleg_3(aload_gc, temp_var3, zero_operand, AMO_2); @@ -1333,7 +1340,7 @@ static void assembleg_macro(const assembly_instruction *AI) AMO_1 = AI->operand[1]; AMO_2 = AI->operand[2]; if ((AMO_0.type == LOCALVAR_OT) && (AMO_0.value == 0)) { - // addr is on the stack + /* addr is on the stack */ assembleg_store(temp_var3, stack_pointer); assembleg_3(astore_gc, temp_var3, zero_operand, AMO_1); assembleg_3(astore_gc, temp_var3, one_operand, AMO_2); @@ -1647,9 +1654,9 @@ extern void assembleg_instruction(const assembly_instruction *AI) printf("%02x ", zcode_holding_area[start_pc]); } else { - printf("%02x", zcode_holding_area[start_pc]); if (zcode_markers[start_pc]) - printf("{%02x}", zcode_markers[start_pc]); + printf("{%s}", describe_mv_short(zcode_markers[start_pc])); + printf("%02x", zcode_holding_area[start_pc]); printf(" "); } } @@ -1722,22 +1729,22 @@ extern void define_symbol_label(int symbol) labels[label].symbol = symbol; } -extern int32 assemble_routine_header(int no_locals, - int routine_asterisked, char *name, int embedded_flag, int the_symbol) +/* The local variables must already be set up; no_locals indicates + how many exist. */ +extern int32 assemble_routine_header(int routine_asterisked, char *name, + int embedded_flag, int the_symbol) { int i, rv; int stackargs = FALSE; int name_length; execution_never_reaches_here = EXECSTATE_REACHABLE; - routine_locals = no_locals; - ensure_memory_list_available(&variables_memlist, MAX_LOCAL_VARIABLES); for (i=0; i= 1 - && strcmpcis(local_variable_names[0].text, "_vararg_count")==0) { - stackargs = TRUE; + && strcmpcis(get_local_variable_name(0), "_vararg_count")==0) { + stackargs = TRUE; } if (veneer_mode) routine_starts_line = blank_brief_location; @@ -1800,7 +1807,8 @@ extern int32 assemble_routine_header(int no_locals, if ((routine_asterisked) || (define_INFIX_switch)) { char fnt[256]; assembly_operand PV, RFA, CON, STP, SLF; int ln, ln2; - + /* TODO: fnt[256] is unsafe */ + ln = next_label++; ln2 = next_label++; @@ -2044,7 +2052,7 @@ void assemble_routine_end(int embedded_flag, debug_locations locations) debug_file_printf ("%d", zmachine_pc - routine_start_pc); write_debug_locations(locations); - for (i = 1; i <= routine_locals; ++i) + for (i = 1; i <= no_locals; ++i) { debug_file_printf(""); debug_file_printf("%s", variable_name(i)); if (glulx_mode) @@ -2070,7 +2078,7 @@ void assemble_routine_end(int embedded_flag, debug_locations locations) /* Issue warnings about any local variables not used in the routine. */ - for (i=1; i<=routine_locals; i++) + for (i=1; i<=no_locals; i++) if (!(variables[i].usage)) dbnu_warning("Local variable", variable_name(i), routine_starts_line); @@ -2216,7 +2224,7 @@ static void transfer_routine_z(void) addr = labels[j].offset - offset_of_next + 2; } if (addr<-0x2000 || addr>0x1fff) - fatalerror("Branch out of range: divide the routine up?"); + error_fmt("Branch out of range: routine \"%s\" is too large", current_routine_name.data); if (addr<0) addr+=(int32) 0x10000L; addr=addr&0x3fff; @@ -2247,7 +2255,7 @@ static void transfer_routine_z(void) addr = labels[j].offset - new_pc; } if (addr<-0x8000 || addr>0x7fff) - fatalerror("Jump out of range: divide the routine up?"); + error_fmt("Jump out of range: routine \"%s\" is too large", current_routine_name.data); if (addr<0) addr += (int32) 0x10000L; zcode_holding_area[i] = addr/256; zcode_holding_area[i+1] = addr%256; @@ -2260,6 +2268,7 @@ static void transfer_routine_z(void) default: switch(zcode_markers[i] & 0x7f) { case NULL_MV: break; + case ERROR_MV: break; case VARIABLE_MV: case OBJECT_MV: case ACTION_MV: @@ -2485,6 +2494,8 @@ static void transfer_routine_g(void) switch(zcode_markers[i] & 0x7f) { case NULL_MV: break; + case ERROR_MV: + break; case ACTION_MV: case IDENT_MV: break; @@ -3061,7 +3072,9 @@ static assembly_operand parse_operand_z(void) } static void parse_assembly_z(void) -{ int n, min, max, indirect_addressed, error_flag = FALSE; +{ int n, min, max; + int indirect_addressed, jumplabel_args; + int error_flag = FALSE; opcodez O; AI.operand_count = 0; @@ -3107,8 +3120,7 @@ static void parse_assembly_z(void) if (i>0) token_text[i-1] = ':'; if (n==-1) - { ebf_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG", - token_text); + { ebf_curtoken_error("Expected 0OP, 1OP, 2OP, VAR, EXT, VAR_LONG or EXT_LONG"); n = EXT; } custom_opcode_z.no = n; @@ -3124,10 +3136,9 @@ static void parse_assembly_z(void) case TWO: max = 32; break; } if ((custom_opcode_z.code < min) || (custom_opcode_z.code >= max)) - { char range[32]; - sprintf(range, "%d to %d", min, max-1); - error_named("For this operand type, opcode number must be in range", - range); + { + error_fmt("For this operand type, opcode number must be in range %d to %d", + min, max-1); custom_opcode_z.code = min; } } @@ -3148,9 +3159,74 @@ T (text), I (indirect addressing), F** (set this Flags 2 bit)"); } O = custom_opcode_z; } + else if ((token_type == SEP_TT) && (token_value == ARROW_SEP || token_value == DARROW_SEP)) + { + int32 start_pc = zcode_ha_size; + int bytecount = 0; + int isword = (token_value == DARROW_SEP); + while (1) { + assembly_operand AO; + /* This isn't the start of a statement, but it's safe to + release token texts anyway. */ + release_token_texts(); + get_next_token(); + if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break; + put_token_back(); + AO = parse_expression(ARRAY_CONTEXT); + if (AO.marker == ERROR_MV) { + break; + } + if (!isword) { + if (AO.marker != 0) + error("Entries in code byte arrays must be known constants"); + if (AO.value >= 256) + warning("Entry in code byte array not in range 0 to 255"); + } + if (execution_never_reaches_here) { + continue; + } + if (bytecount == 0 && asm_trace_level > 0) { + printf("%5d +%05lx %3s %-12s", ErrorReport.line_number, + ((long int) zmachine_pc), " ", + isword?"":""); + } + if (!isword) { + byteout((AO.value & 0xFF), 0); + bytecount++; + if (asm_trace_level > 0) { + printf(" %02x", (AO.value & 0xFF)); + } + } + else { + byteout(((AO.value >> 8) & 0xFF), AO.marker); + byteout((AO.value & 0xFF), 0); + bytecount += 2; + if (asm_trace_level > 0) { + printf(" "); + print_operand(&AO, TRUE); + } + } + } + if (bytecount > 0 && asm_trace_level > 0) { + printf("\n"); + } + if (asm_trace_level>=2) + { + int j; + for (j=0;start_pc= 256) + warning("Entry in code byte array not in range 0 to 255"); + } + if (execution_never_reaches_here) { + continue; + } + if (bytecount == 0 && asm_trace_level > 0) { + printf("%5d +%05lx %3s %-12s", ErrorReport.line_number, + ((long int) zmachine_pc), " ", + isword?"":""); + } + if (!isword) { + byteout((AO.value & 0xFF), 0); + bytecount++; + if (asm_trace_level > 0) { + printf(" %02x", (AO.value & 0xFF)); + } + } + else { + byteout(((AO.value >> 24) & 0xFF), AO.marker); + byteout(((AO.value >> 16) & 0xFF), 0); + byteout(((AO.value >> 8) & 0xFF), 0); + byteout((AO.value & 0xFF), 0); + bytecount += 4; + if (asm_trace_level > 0) { + printf(" "); + print_operand(&AO, TRUE); + } + } + } + if (bytecount > 0 && asm_trace_level > 0) { + printf("\n"); + } + if (asm_trace_level>=2) + { + int j; + for (j=0;start_pc= BRANCH_MV && mval < BRANCHMAX_MV) return "br"; + + return("???"); +} + /* ------------------------------------------------------------------------- */ /* The mending operation */ /* ------------------------------------------------------------------------- */ @@ -143,9 +187,17 @@ static int32 backpatch_value_z(int32 value) value += individuals_offset; break; case MAIN_MV: - value = symbol_index("Main", -1); - if (symbols[value].type != ROUTINE_T) + value = get_symbol_index("Main"); + if (value < 0 || (symbols[value].flags & UNKNOWN_SFLAG)) { error("No 'Main' routine has been defined"); + value = 0; + break; + } + if (symbols[value].type != ROUTINE_T) { + ebf_symbol_error("'Main' routine", symbols[value].name, typename(symbols[value].type), symbols[value].line); + value = 0; + break; + } symbols[value].flags |= USED_SFLAG; value = symbols[value].value; if (OMIT_UNUSED_ROUTINES) @@ -290,9 +342,17 @@ static int32 backpatch_value_g(int32 value) value += individuals_offset; break; case MAIN_MV: - value = symbol_index("Main", -1); - if (symbols[value].type != ROUTINE_T) + value = get_symbol_index("Main"); + if (value < 0 || (symbols[value].flags & UNKNOWN_SFLAG)) { error("No 'Main' routine has been defined"); + value = 0; + break; + } + if (symbols[value].type != ROUTINE_T) { + ebf_symbol_error("'Main' routine", symbols[value].name, typename(symbols[value].type), symbols[value].line); + value = 0; + break; + } symbols[value].flags |= USED_SFLAG; value = symbols[value].value; if (OMIT_UNUSED_ROUTINES) diff --git a/src/chars.c b/src/chars.c index 3fdf10d..2d4e21b 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.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -335,8 +335,9 @@ static void read_source_to_iso_file(uchar *uccg) /* */ /* 00 remains 0 (meaning "end of file") */ /* TAB becomes SPACE */ +/* 0a remains '\n' */ /* 0c ("form feed") becomes '\n' */ -/* 0d becomes '\n' */ +/* 0d remains '\r' */ /* other control characters become '?' */ /* 7f becomes '?' */ /* 80 to 9f become '?' */ @@ -359,7 +360,7 @@ static void make_source_to_iso_grid(void) for (n=1; n<32; n++) source_to_iso_grid[n] = '?'; source_to_iso_grid[10] = '\n'; source_to_iso_grid[12] = '\n'; - source_to_iso_grid[13] = '\n'; + source_to_iso_grid[13] = '\r'; source_to_iso_grid[127] = '?'; source_to_iso_grid[TAB_CHARACTER] = ' '; diff --git a/src/directs.c b/src/directs.c index d8374c3..b9d26d2 100644 --- a/src/directs.c +++ b/src/directs.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "directs" : Directives (# commands) */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -23,7 +23,6 @@ int no_routines, /* Number of routines compiled so far */ no_named_routines, /* Number not embedded in objects */ - no_locals, /* Number of locals in current routine */ no_termcs; /* Number of terminating characters */ int terminating_characters[32]; @@ -39,23 +38,23 @@ static int ifdef_stack[MAX_IFDEF_STACK], ifdef_sp; /* ------------------------------------------------------------------------- */ -static int ebf_error_recover(char *s1, char *s2) +static int ebf_error_recover(char *s1) { - /* Display an "expected... but found..." error, then skim forward - to the next semicolon and return FALSE. This is such a common - case in parse_given_directive() that it's worth a utility - function. You will see many error paths that look like: + /* Display an "expected... but found (current token)" error, then + skim forward to the next semicolon and return FALSE. This is + such a common case in parse_given_directive() that it's worth a + utility function. You will see many error paths that look like: return ebf_error_recover(...); */ - ebf_error(s1, s2); + ebf_curtoken_error(s1); panic_mode_error_recovery(); return FALSE; } -static int ebf_symbol_error_recover(char *s1, char *name, char *type, brief_location report_line) +static int ebf_symbol_error_recover(char *s1, char *type, brief_location report_line) { /* Same for ebf_symbol_error(). */ - ebf_symbol_error(s1, name, type, report_line); + ebf_symbol_error(s1, token_text, type, report_line); panic_mode_error_recovery(); return FALSE; } @@ -122,13 +121,7 @@ extern int parse_given_directive(int internal_flag) panic_mode_error_recovery(); return FALSE; } if (token_type != DQ_TT) - { return ebf_error_recover("abbreviation string", token_text); - } - /* Abbreviation string with null must fit in a MAX_ABBREV_LENGTH - array. */ - if (strlen(token_text)>=MAX_ABBREV_LENGTH) - { error_named("Abbreviation too long", token_text); - continue; + { return ebf_error_recover("abbreviation string"); } make_abbreviation(token_text); } while (TRUE); @@ -167,12 +160,12 @@ extern int parse_given_directive(int internal_flag) if (token_type != SYMBOL_TT) { discard_token_location(beginning_debug_location); - return ebf_error_recover("new constant name", token_text); + return ebf_error_recover("new constant name"); } 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(symbols[i].type), symbols[i].line); + return ebf_symbol_error_recover("new constant name", typename(symbols[i].type), symbols[i].line); } assign_symbol(i, 0, CONSTANT_T); @@ -250,7 +243,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); case DEFAULT_CODE: get_next_token(); if (token_type != SYMBOL_TT) - return ebf_error_recover("name", token_text); + return ebf_error_recover("name"); i = -1; if (symbols[token_value].flags & UNKNOWN_SFLAG) @@ -296,7 +289,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); */ get_next_token(); if (token_type != SQ_TT && token_type != DQ_TT) - return ebf_error_recover("dictionary word", token_text); + return ebf_error_recover("dictionary word"); { char *wd = token_text; @@ -400,7 +393,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); DefCondition: get_next_token(); if (token_type != SYMBOL_TT) - return ebf_error_recover("symbol name", token_text); + return ebf_error_recover("symbol name"); /* Special case: a symbol of the form "VN_nnnn" is considered defined if the compiler version number is at least nnnn. @@ -506,7 +499,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); HashIfCondition: get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) - return ebf_error_recover("semicolon after 'If...' condition", token_text); + return ebf_error_recover("semicolon after 'If...' condition"); if (ifdef_sp >= MAX_IFDEF_STACK) { error("'If' directives nested too deeply"); @@ -570,13 +563,13 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); case INCLUDE_CODE: get_next_token(); if (token_type != DQ_TT) - return ebf_error_recover("filename in double-quotes", token_text); + return ebf_error_recover("filename in double-quotes"); { char *name = token_text; get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) - ebf_error("semicolon ';' after Include filename", token_text); + ebf_curtoken_error("semicolon ';' after Include filename"); if (strcmp(name, "language__") == 0) load_sourcefile(Language_Name, 0); @@ -610,13 +603,13 @@ 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); + return ebf_error_recover("new low string name"); if (!(symbols[i].flags & UNKNOWN_SFLAG)) - return ebf_symbol_error_recover("new low string name", token_text, typename(symbols[i].type), symbols[i].line); + return ebf_symbol_error_recover("new low string name", typename(symbols[i].type), symbols[i].line); get_next_token(); if (token_type != DQ_TT) - return ebf_error_recover("literal string in double-quotes", token_text); + return ebf_error_recover("literal string in double-quotes"); assign_symbol(i, compile_string(token_text, STRCTX_LOWSTRING), CONSTANT_T); break; @@ -647,26 +640,25 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); if ((token_type == DIR_KEYWORD_TT) && (token_value == ERROR_DK)) { get_next_token(); if (token_type != DQ_TT) - { return ebf_error_recover("error message in double-quotes", token_text); + { return ebf_error_recover("error message in double-quotes"); } error(token_text); break; } if ((token_type == DIR_KEYWORD_TT) && (token_value == FATALERROR_DK)) { get_next_token(); if (token_type != DQ_TT) - { return ebf_error_recover("fatal error message in double-quotes", token_text); + { return ebf_error_recover("fatal error message in double-quotes"); } fatalerror(token_text); break; } if ((token_type == DIR_KEYWORD_TT) && (token_value == WARNING_DK)) { get_next_token(); if (token_type != DQ_TT) - { return ebf_error_recover("warning message in double-quotes", token_text); + { return ebf_error_recover("warning message in double-quotes"); } warning(token_text); break; } - return ebf_error_recover("a message in double-quotes, 'error', 'fatalerror' or 'warning'", - token_text); + return ebf_error_recover("a message in double-quotes, 'error', 'fatalerror' or 'warning'"); break; /* --------------------------------------------------------------------- */ @@ -715,16 +707,14 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) { if (token_type != DQ_TT) { - return ebf_error_recover("a file name in double-quotes", - token_text); + return ebf_error_recover("a file name in double-quotes"); } origsource_file = token_text; get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) { if (token_type != NUMBER_TT) { - return ebf_error_recover("a file line number", - token_text); + return ebf_error_recover("a file line number"); } origsource_line = token_value; if (origsource_line < 0) @@ -733,8 +723,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) { if (token_type != NUMBER_TT) { - return ebf_error_recover("a file line number", - token_text); + return ebf_error_recover("a file line number"); } origsource_char = token_value; if (origsource_char < 0) @@ -805,9 +794,9 @@ 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); + return ebf_error_recover("name of routine to replace"); if (!(symbols[token_value].flags & UNKNOWN_SFLAG)) - return ebf_error_recover("name of routine not yet defined", token_text); + return ebf_error_recover("name of routine not yet defined"); symbols[token_value].flags |= REPLACE_SFLAG; @@ -824,7 +813,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); } if (token_type != SYMBOL_TT || !(symbols[token_value].flags & UNKNOWN_SFLAG)) - return ebf_error_recover("semicolon ';' or new routine name", token_text); + return ebf_error_recover("semicolon ';' or new routine name"); /* Define the original-form symbol as a zero constant. Its value will be overwritten later, when we define the @@ -862,7 +851,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); directive_keywords.enabled = FALSE; if ((token_type != DIR_KEYWORD_TT) || ((token_value != SCORE_DK) && (token_value != TIME_DK))) - return ebf_error_recover("'score' or 'time' after 'statusline'", token_text); + return ebf_error_recover("'score' or 'time' after 'statusline'"); if (token_value == SCORE_DK) statusline_flag = SCORE_STYLE; else statusline_flag = TIME_STYLE; break; @@ -878,7 +867,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); get_next_token(); df_dont_note_global_symbols = FALSE; if (token_type != SYMBOL_TT) - return ebf_error_recover("routine name to stub", token_text); + return ebf_error_recover("routine name to stub"); i = token_value; flag = FALSE; @@ -889,7 +878,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); get_next_token(); k = token_value; if (token_type != NUMBER_TT) - return ebf_error_recover("number of local variables", token_text); + return ebf_error_recover("number of local variables"); if ((k>4) || (k<0)) { error("Must specify 0 to 4 local variables for 'Stub' routine"); k = 0; @@ -903,13 +892,14 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); (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"); - strcpy(local_variable_names[2].text, "dummy3"); - strcpy(local_variable_names[3].text, "dummy4"); + clear_local_variables(); + if (k >= 1) add_local_variable("dummy1"); + if (k >= 2) add_local_variable("dummy2"); + if (k >= 3) add_local_variable("dummy3"); + if (k >= 4) add_local_variable("dummy4"); assign_symbol(i, - assemble_routine_header(k, FALSE, symbols[i].name, FALSE, i), + assemble_routine_header(FALSE, symbols[i].name, FALSE, i), ROUTINE_T); /* Ensure the return value of a stubbed routine is false, @@ -937,8 +927,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); dont_enter_into_symbol_table = TRUE; get_next_token(); dont_enter_into_symbol_table = FALSE; - if (token_type != DQ_TT) - return ebf_error_recover("string of switches", token_text); + if (token_type != UQ_TT) + return ebf_error_recover("string of switches"); if (!ignore_switches_switch) { if (constant_made_yet) { @@ -1010,7 +1000,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); 'on' and 'off' are trace keywords. */ if (token_type != TRACE_KEYWORD_TT) - return ebf_error_recover("debugging keyword", token_text); + return ebf_error_recover("debugging keyword"); trace_keywords.enabled = TRUE; @@ -1100,7 +1090,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); case UNDEF_CODE: get_next_token(); if (token_type != SYMBOL_TT) - return ebf_error_recover("symbol name", token_text); + return ebf_error_recover("symbol name"); if (symbols[token_value].flags & UNKNOWN_SFLAG) { break; /* undef'ing an undefined constant is okay */ @@ -1114,7 +1104,10 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); if (debugfile_switch) { write_debug_undef(token_value); } - end_symbol_scope(token_value); + /* We remove it from the symbol table. But previous uses of the symbol + were valid, so we don't set neverused true. We also mark it + USED so that it can't trigger "symbol not used" warnings. */ + end_symbol_scope(token_value, FALSE); symbols[token_value].flags |= USED_SFLAG; break; @@ -1172,8 +1165,8 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); 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)) + debtok = get_symbol_index("DICT_ENTRY_BYTES"); + if (debtok >= 0 && !(symbols[debtok].flags & UNKNOWN_SFLAG)) { if (!(symbols[debtok].flags & REDEFINABLE_SFLAG)) { @@ -1209,18 +1202,18 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); new_alphabet(token_text, 0); get_next_token(); if (token_type != DQ_TT) - return ebf_error_recover("double-quoted alphabet string", token_text); + return ebf_error_recover("double-quoted alphabet string"); new_alphabet(token_text, 1); get_next_token(); if (token_type != DQ_TT) - return ebf_error_recover("double-quoted alphabet string", token_text); + return ebf_error_recover("double-quoted alphabet string"); new_alphabet(token_text, 2); break; case SQ_TT: map_new_zchar(text_to_unicode(token_text)); if (token_text[textual_form_length] != 0) - return ebf_error_recover("single character value", token_text); + return ebf_error_recover("single character value"); break; case DIR_KEYWORD_TT: @@ -1241,13 +1234,11 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); new_zscii_character(text_to_unicode(token_text), plus_flag); if (token_text[textual_form_length] != 0) - return ebf_error_recover("single character value", - token_text); + return ebf_error_recover("single character value"); plus_flag = TRUE; break; default: - return ebf_error_recover("character or Unicode number", - token_text); + return ebf_error_recover("character or Unicode number"); } get_next_token(); } @@ -1264,8 +1255,7 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); = token_value; break; default: - return ebf_error_recover("ZSCII number", - token_text); + return ebf_error_recover("ZSCII number"); } get_next_token(); } @@ -1273,13 +1263,12 @@ Fake_Action directives to a point after the inclusion of \"Parser\".)"); break; default: return ebf_error_recover("'table', 'terminating', \ -a string or a constant", - token_text); +a string or a constant"); } break; default: return ebf_error_recover("three alphabet strings, \ -a 'table' or 'terminating' command or a single character", token_text); +a 'table' or 'terminating' command or a single character"); } break; @@ -1292,7 +1281,7 @@ a 'table' or 'terminating' command or a single character", token_text); get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); + { ebf_curtoken_error("';'"); /* Put the non-semicolon back. We will continue parsing from that point, in hope that it's the start of a new directive. (This recovers cleanly from a missing semicolon at the end @@ -1314,7 +1303,6 @@ extern void init_directs_vars(void) extern void directs_begin_pass(void) { no_routines = 0; no_named_routines = 0; - no_locals = 0; no_termcs = 0; constant_made_yet = FALSE; ifdef_sp = 0; diff --git a/src/errors.c b/src/errors.c index a71447e..c7ec13d 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.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -171,6 +171,14 @@ static char *location_text(brief_location report_line) return other_pos_buff; } +char *current_location_text(void) +{ + /* Convert the current lexer location to a brief string. + (Called by some trace messages.) + This uses the static buffer other_pos_buff. */ + return location_text(get_brief_location(&ErrorReport)); +} + static void ellipsize_error_message_buff(void) { /* If the error buffer was actually filled up by a message, it was @@ -204,13 +212,23 @@ extern void fatalerror(char *s) exit(1); } +extern void fatalerror_fmt(const char *format, ...) +{ + va_list argument_pointer; + va_start(argument_pointer, format); + vsnprintf(error_message_buff, ERROR_BUFLEN, format, argument_pointer); + va_end(argument_pointer); + ellipsize_error_message_buff(); + fatalerror(error_message_buff); +} + extern void fatalerror_named(char *m, char *fn) { snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"", m, fn); ellipsize_error_message_buff(); fatalerror(error_message_buff); } -extern void memory_out_error(int32 size, int32 howmany, char *name) +extern void fatalerror_memory_out(int32 size, int32 howmany, char *name) { if (howmany == 1) snprintf(error_message_buff, ERROR_BUFLEN, "Run out of memory allocating %d bytes for %s", size, name); @@ -278,15 +296,18 @@ extern void error(char *s) message(1,s); } -extern void error_named(char *s1, char *s2) -{ snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2); +extern void error_fmt(const char *format, ...) +{ + va_list argument_pointer; + va_start(argument_pointer, format); + vsnprintf(error_message_buff, ERROR_BUFLEN, format, argument_pointer); + va_end(argument_pointer); ellipsize_error_message_buff(); error(error_message_buff); } -extern void error_numbered(char *s1, int val) -{ - snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val); +extern void error_named(char *s1, char *s2) +{ snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2); ellipsize_error_message_buff(); error(error_message_buff); } @@ -305,16 +326,35 @@ extern void error_named_at(char *s1, char *s2, brief_location report_line) ErrorReport = E; concise_switch = i; } -extern void no_such_label(char *lname) -{ error_named("No such label as",lname); -} - extern void ebf_error(char *s1, char *s2) { snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s1, s2); ellipsize_error_message_buff(); error(error_message_buff); } +extern void ebf_curtoken_error(char *s) +{ + /* This is "Expected (s) but found (the current token_text)". We use + token_type as a hint for how to display token_text. */ + + if (token_type == DQ_TT) { + snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found string \"%s\"", s, token_text); + } + else if (token_type == SQ_TT && strlen(token_text)==1) { + snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found char '%s'", s, token_text); + } + else if (token_type == SQ_TT) { + snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found dict word '%s'", s, token_text); + } + else { + /* Symbols, unquoted strings, and numbers can be printed directly. EOF will have "" in token_text. */ + snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s, token_text); + } + + ellipsize_error_message_buff(); + error(error_message_buff); +} + extern void ebf_symbol_error(char *s1, char *name, char *type, brief_location report_line) { snprintf(error_message_buff, ERROR_BUFLEN, "\"%s\" is a name already in use and may not be used as a %s (%s \"%s\" was defined at %s)", name, s1, type, name, location_text(report_line)); ellipsize_error_message_buff(); @@ -407,9 +447,13 @@ extern void warning(char *s1) message(2,s1); } -extern void warning_numbered(char *s1, int val) -{ if (nowarnings_switch) { no_suppressed_warnings++; return; } - snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.", s1, val); +extern void warning_fmt(const char *format, ...) +{ + va_list argument_pointer; + if (nowarnings_switch) { no_suppressed_warnings++; return; } + va_start(argument_pointer, format); + vsnprintf(error_message_buff, ERROR_BUFLEN, format, argument_pointer); + va_end(argument_pointer); ellipsize_error_message_buff(); message(2,error_message_buff); } @@ -422,6 +466,19 @@ extern void warning_named(char *s1, char *s2) message(2,error_message_buff); } +extern void warning_at(char *name, brief_location report_line) +{ int i; + ErrorPosition E = ErrorReport; + if (nowarnings_switch) { no_suppressed_warnings++; return; } + export_brief_location(report_line, &ErrorReport); + snprintf(error_message_buff, ERROR_BUFLEN, "%s", name); + ellipsize_error_message_buff(); + i = concise_switch; concise_switch = TRUE; + message(2,error_message_buff); + concise_switch = i; + ErrorReport = E; +} + extern void symtype_warning(char *context, char *name, char *type, char *wanttype) { if (nowarnings_switch) { no_suppressed_warnings++; return; } diff --git a/src/expressc.c b/src/expressc.c index 067cab3..61a5d5a 100644 --- a/src/expressc.c +++ b/src/expressc.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "expressc" : The expression code generator */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -1084,10 +1084,16 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, /* Test if inside the "Class" object... */ INITAOTV(&AO3, BYTECONSTANT_OT, GOBJFIELD_PARENT()); assembleg_3(aload_gc, AO, AO3, stack_pointer); - ln = symbol_index("Class", -1); - AO3.value = symbols[ln].value; - AO3.marker = OBJECT_MV; - AO3.type = CONSTANT_OT; + ln = get_symbol_index("Class"); + if (ln < 0) { + error("No 'Class' object found"); + AO3 = zero_operand; + } + else { + AO3.value = symbols[ln].value; + AO3.marker = OBJECT_MV; + AO3.type = CONSTANT_OT; + } assembleg_2_branch(jne_gc, stack_pointer, AO3, passed_label); } @@ -1105,10 +1111,16 @@ static assembly_operand check_nonzero_at_runtime_g(assembly_operand AO1, } else { /* Build the symbol for "Object" */ - ln = symbol_index("Object", -1); - AO2.value = symbols[ln].value; - AO2.marker = OBJECT_MV; - AO2.type = CONSTANT_OT; + ln = get_symbol_index("Object"); + if (ln < 0) { + error("No 'Object' object found"); + AO2 = zero_operand; + } + else { + AO2.value = symbols[ln].value; + AO2.marker = OBJECT_MV; + AO2.type = CONSTANT_OT; + } if (check_sp) { /* Push "Object" */ assembleg_store(AO1, AO2); @@ -2643,11 +2655,48 @@ static void generate_code_from(int n, int void_flag) assembleg_2(random_gc, AO, stack_pointer); assembleg_3(aload_gc, AO2, stack_pointer, Result); } + else if (is_constant_ot(ET[ET[below].right].value.type) && ET[ET[below].right].value.marker == 0) { + /* One argument, value known at compile time */ + int32 arg = ET[ET[below].right].value.value; /* signed */ + if (arg > 0) { + assembly_operand AO; + INITAO(&AO); + AO.value = arg; + set_constant_ot(&AO); + assembleg_2(random_gc, + AO, stack_pointer); + assembleg_3(add_gc, stack_pointer, one_operand, + Result); + } + else { + /* This handles zero or negative */ + assembly_operand AO; + INITAO(&AO); + AO.value = -arg; + set_constant_ot(&AO); + assembleg_1(setrandom_gc, + AO); + assembleg_store(Result, zero_operand); + } + } else { + /* One argument, not known at compile time */ + int ln, ln2; + assembleg_store(temp_var1, ET[ET[below].right].value); + ln = next_label++; + ln2 = next_label++; + assembleg_2_branch(jle_gc, temp_var1, zero_operand, ln); assembleg_2(random_gc, - ET[ET[below].right].value, stack_pointer); + temp_var1, stack_pointer); assembleg_3(add_gc, stack_pointer, one_operand, Result); + assembleg_0_branch(jump_gc, ln2); + assemble_label_no(ln); + assembleg_2(neg_gc, temp_var1, stack_pointer); + assembleg_1(setrandom_gc, + stack_pointer); + assembleg_store(Result, zero_operand); + assemble_label_no(ln2); } break; @@ -3001,7 +3050,7 @@ assembly_operand code_generate(assembly_operand AO, int context, int label) } if (expr_trace_level >= 2) - { printf("Raw parse tree:\n"); show_tree(AO, FALSE); + { printf("Raw parse tree:\n"); show_tree(&AO, FALSE); } if (context == CONDITION_CONTEXT) @@ -3021,7 +3070,7 @@ assembly_operand code_generate(assembly_operand AO, int context, int label) default: printf("* ILLEGAL *"); break; } printf(" context with annotated tree:\n"); - show_tree(AO, TRUE); + show_tree(&AO, TRUE); } generate_code_from(AO.value, (context==VOID_CONTEXT)); diff --git a/src/expressp.c b/src/expressp.c index c93337a..906eed1 100644 --- a/src/expressp.c +++ b/src/expressp.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "expressp" : The expression parser */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -67,6 +67,8 @@ static int comma_allowed, arrow_allowed, superclass_allowed, int system_function_usage[NUMBER_SYSTEM_FUNCTIONS]; +static void check_system_constant_available(int); + static int get_next_etoken(void) { int v, symbol = 0, mark_symbol_as_used = FALSE, initial_bracket_level = bracket_level; @@ -324,8 +326,8 @@ but not used as a value:", unicode); current_token.text += 3; current_token.type = SYMBOL_TT; - symbol = symbol_index(current_token.text, -1); - if (symbols[symbol].type != GLOBAL_VARIABLE_T) { + symbol = get_symbol_index(current_token.text); + if (symbol < 0 || symbols[symbol].type != GLOBAL_VARIABLE_T) { ebf_error( "global variable name after '#g$'", current_token.text); @@ -376,7 +378,7 @@ but not used as a value:", unicode); "'#r$Routine' can now be written just 'Routine'"); current_token.text += 3; current_token.type = SYMBOL_TT; - current_token.value = symbol_index(current_token.text, -1); + current_token.value = symbol_index(current_token.text, -1, NULL); goto ReceiveSymbol; case HASHWDOLLAR_SEP: @@ -388,13 +390,14 @@ but not used as a value:", unicode); get_next_token(); system_constants.enabled = FALSE; if (token_type != SYSTEM_CONSTANT_TT) - { ebf_error( - "'r$', 'n$', 'g$' or internal Inform constant name after '#'", - token_text); + { ebf_curtoken_error( + "'r$', 'n$', 'g$' or internal Inform constant name after '#'"); break; } else - { current_token.type = token_type; + { + check_system_constant_available(token_value); + current_token.type = token_type; current_token.value = token_value; current_token.text = token_text; current_token.marker = INCON_MV; @@ -472,27 +475,31 @@ but not used as a value:", unicode); return TRUE; } -/* --- Operator precedences ------------------------------------------------ */ +/* --- Operator precedences and error values-------------------------------- */ #define LOWER_P 101 #define EQUAL_P 102 #define GREATER_P 103 -#define e1 1 /* Missing operand error */ -#define e2 2 /* Unexpected close bracket */ -#define e3 3 /* Missing operator error */ -#define e4 4 /* Expression ends with an open bracket */ -#define e5 5 /* Associativity illegal error */ +#define BYPREC -1 /* Compare the precedence of two operators */ + +#define NOVAL_E 1 /* Missing operand error */ +#define CLOSEB_E 2 /* Unexpected close bracket */ +#define NOOP_E 3 /* Missing operator error */ +#define OPENB_E 4 /* Expression ends with an open bracket */ +#define ASSOC_E 5 /* Associativity illegal error */ -const int prec_table[] = { +const int prec_table[49] = { -/* a .......... ( ) end op term */ +/* a ....... ( ) end op:pre op:bin op:post term */ -/* b ( */ LOWER_P, e3, LOWER_P, LOWER_P, e3, -/* . ) */ EQUAL_P, GREATER_P, e2, GREATER_P, GREATER_P, -/* . end */ e4, GREATER_P, e1, GREATER_P, GREATER_P, -/* . op */ LOWER_P, GREATER_P, LOWER_P, -1, GREATER_P, -/* . term */ LOWER_P, e3, LOWER_P, LOWER_P, e3 +/* b ( */ LOWER_P, NOOP_E, LOWER_P, LOWER_P, LOWER_P, NOOP_E, NOOP_E, +/* . ) */ EQUAL_P, GREATER_P, CLOSEB_E, GREATER_P, GREATER_P, GREATER_P, GREATER_P, +/* . end */ OPENB_E, GREATER_P, NOVAL_E, GREATER_P, GREATER_P, GREATER_P, GREATER_P, +/* . op:pre */ LOWER_P, NOOP_E, LOWER_P, BYPREC, BYPREC, NOOP_E, NOOP_E, +/* . op:bin */ LOWER_P, GREATER_P, LOWER_P, BYPREC, BYPREC, BYPREC, GREATER_P, +/* . op:post */ LOWER_P, GREATER_P, LOWER_P, BYPREC, BYPREC, BYPREC, GREATER_P, +/* . term */ LOWER_P, NOOP_E, LOWER_P, LOWER_P, LOWER_P, NOOP_E, NOOP_E }; @@ -501,7 +508,7 @@ 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, the only possible values are GREATER_P, LOWER_P or EQUAL_P; - if it is malformed then one of e1 to e5 results. + if it is malformed then one of the *_E results. Note that this routine is not symmetrical and that the relation is not trichotomous. @@ -512,25 +519,50 @@ static int find_prec(const token_data *a, const token_data *b) a GREATER_P a if a left-associative */ - int i, j, l1, l2; + int ai, bi, j, l1, l2; + /* Select a column and row in prec_table, based on the type of + a and b. If a/b is an operator, we have to distinguish three + columns/rows depending on whether the operator is prefix, + postfix, or neither. + */ + 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; + { case SUBOPEN_TT: ai=0; break; + case SUBCLOSE_TT: ai=1; break; + case ENDEXP_TT: ai=2; break; + case OP_TT: + if (operators[a->value].usage == PRE_U) + ai=3; + else if (operators[a->value].usage == POST_U) + ai=5; + else + ai=4; + break; + default: ai=6; break; } switch(b->type) - { case SUBOPEN_TT: i+=0; break; - case SUBCLOSE_TT: i+=5; break; - case ENDEXP_TT: i+=10; break; - case OP_TT: i+=15; break; - default: i+=20; break; + { case SUBOPEN_TT: bi=0; break; + case SUBCLOSE_TT: bi=1; break; + case ENDEXP_TT: bi=2; break; + case OP_TT: + if (operators[b->value].usage == PRE_U) + bi=3; + else if (operators[b->value].usage == POST_U) + bi=5; + else + bi=4; + break; + default: bi=6; break; } + + j = prec_table[ai+7*bi]; + if (j != BYPREC) return j; - j = prec_table[i]; if (j != -1) return j; - + /* BYPREC is the (a=OP, b=OP) cases. We must compare the precedence of the + two operators. + (We've already eliminated invalid cases like (a++ --b).) + */ l1 = operators[a->value].precedence; l2 = operators[b->value].precedence; if (operators[b->value].usage == PRE_U) return LOWER_P; @@ -550,7 +582,7 @@ static int find_prec(const token_data *a, const token_data *b) switch(operators[a->value].associativity) { case L_A: return GREATER_P; case R_A: return LOWER_P; - case 0: return e5; + case 0: return ASSOC_E; } return GREATER_P; } @@ -606,8 +638,32 @@ int z_system_constant_list[] = grammar_table_SC, -1 }; +static void check_system_constant_available(int t) +{ + if (OMIT_SYMBOL_TABLE) { + /* Certain system constants refer to the symbol table, which + is meaningless if OMIT_SYMBOL_TABLE is set. */ + switch(t) + { + case identifiers_table_SC: + case attribute_names_array_SC: + case property_names_array_SC: + case action_names_array_SC: + case fake_action_names_array_SC: + case array_names_offset_SC: + case global_names_array_SC: + case routine_names_array_SC: + case constant_names_array_SC: + error_named("OMIT_SYMBOL_TABLE omits system constant", system_constants.keywords[t]); + default: + break; + } + } +} + static int32 value_of_system_constant_z(int t) -{ switch(t) +{ + switch(t) { case adjectives_table_SC: return adjectives_offset; case actions_table_SC: @@ -1042,7 +1098,7 @@ static void add_bracket_layer_to_emitter_stack(int depth) { /* There's no point in tracking bracket layers that don't fence off any values. */ if (emitter_sp < depth + 1) return; if (expr_trace_level >= 2) - printf("Adding bracket layer\n"); + printf("Adding bracket layer (depth %d)\n", depth); ++emitter_stack[emitter_sp-depth-1].bracket_count; } @@ -1215,7 +1271,7 @@ static void emit_token(const token_data *t) default: warning("Property name in expression is not qualified by object"); } - } /* if (is_property_t */ + } } switch(arity) @@ -1223,7 +1279,12 @@ static void emit_token(const token_data *t) o1 = emitter_stack[emitter_sp - 1].op; if ((o1.marker == 0) && is_constant_ot(o1.type)) { switch(t->value) - { case UNARY_MINUS_OP: x = -o1.value; goto FoldConstant; + { case UNARY_MINUS_OP: + if ((uint32)o1.value == 0x80000000) + x = 0x80000000; + else + x = -o1.value; + goto FoldConstant; case ARTNOT_OP: if (!glulx_mode) x = (~o1.value) & 0xffff; @@ -1390,23 +1451,24 @@ static void emit_token(const token_data *t) for 32-bit arithmetic. */ if (!glulx_mode && ((x<-32768) || (x > 32767))) - { char folding_error[40]; + { int32 ov1 = (o1.value >= 0x8000) ? (o1.value - 0x10000) : o1.value; int32 ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value; + char op = '?'; switch(t->value) { case PLUS_OP: - sprintf(folding_error, "%d + %d = %d", ov1, ov2, x); + op = '+'; break; case MINUS_OP: - sprintf(folding_error, "%d - %d = %d", ov1, ov2, x); + op = '-'; break; case TIMES_OP: - sprintf(folding_error, "%d * %d = %d", ov1, ov2, x); + op = '*'; break; } - error_named("Signed arithmetic on compile-time constants overflowed \ -the range -32768 to +32767:", folding_error); + error_fmt("Signed arithmetic on compile-time constants overflowed \ +the range -32768 to +32767 (%d %c %d = %d)", ov1, op, ov2, x); } FoldConstant: @@ -1479,10 +1541,10 @@ static void show_node(int n, int depth, int annotate) if (ET[n].right != -1) show_node(ET[n].right, depth, annotate); } -extern void show_tree(assembly_operand AO, int annotate) -{ if (AO.type == EXPRESSION_OT) show_node(AO.value, 0, annotate); +extern void show_tree(const assembly_operand *AO, int annotate) +{ if (AO->type == EXPRESSION_OT) show_node(AO->value, 0, annotate); else - { printf("Constant: "); print_operand(&AO, annotate); + { printf("Constant: "); print_operand(AO, annotate); printf("\n"); } } @@ -1882,8 +1944,11 @@ extern assembly_operand parse_expression(int context) is constant and thus known at compile time. If an error has occurred in the expression, which recovery from was - not possible, then the return is (short constant) 0. This should - minimise the chance of a cascade of further error messages. + not possible, then the return is (short constant) 0 with marker + value ERROR_MV. The caller may check for this marker value to + decide whether to (e.g.) stop reading array values. Otherwise, it + will just be treated as a zero, which should minimise the chance + of a cascade of further error messages. */ token_data a, b, pop; int i; @@ -1925,7 +1990,8 @@ extern assembly_operand parse_expression(int context) directives.enabled = FALSE; if (get_next_etoken() == FALSE) - { ebf_error("expression", token_text); + { ebf_curtoken_error("expression"); + AO.marker = ERROR_MV; return AO; } @@ -1939,6 +2005,7 @@ extern assembly_operand parse_expression(int context) if (sr_sp == 0) { compiler_error("SR error: stack empty"); + AO.marker = ERROR_MV; return(AO); } @@ -1948,10 +2015,12 @@ extern assembly_operand parse_expression(int context) { if (emitter_sp == 0) { error("No expression between brackets '(' and ')'"); put_token_back(); + AO.marker = ERROR_MV; return AO; } if (emitter_sp > 1) { compiler_error("SR error: emitter stack overfull"); + AO.marker = ERROR_MV; return AO; } @@ -1959,7 +2028,7 @@ extern assembly_operand parse_expression(int context) if (AO.type == EXPRESSION_OT) { if (expr_trace_level >= 3) { printf("Tree before lvalue checking:\n"); - show_tree(AO, FALSE); + show_tree(&AO, FALSE); } if (!glulx_mode) check_property_operator(AO.value); @@ -1979,6 +2048,7 @@ extern assembly_operand parse_expression(int context) if (context == CONSTANT_CONTEXT) if (!is_constant_ot(AO.type)) { AO = zero_operand; + AO.marker = ERROR_MV; ebf_error("constant", ""); } put_token_back(); @@ -1988,7 +2058,7 @@ extern assembly_operand parse_expression(int context) switch(find_prec(&a,&b)) { - case e5: /* Associativity error */ + case ASSOC_E: /* Associativity error */ error_named("Brackets mandatory to clarify order of:", a.text); @@ -2048,8 +2118,10 @@ extern assembly_operand parse_expression(int context) } while (find_prec(&sr_stack[sr_sp-1], &pop) != LOWER_P); break; - case e1: /* Missing operand error */ + case NOVAL_E: /* Missing operand error */ error_named("Missing operand after", a.text); + /* We insert a "0" token so that the rest of the expression + can be compiled. */ put_token_back(); current_token.type = NUMBER_TT; current_token.value = 0; @@ -2057,13 +2129,15 @@ extern assembly_operand parse_expression(int context) current_token.text = "0"; break; - case e2: /* Unexpected close bracket */ + case CLOSEB_E: /* Unexpected close bracket */ error("Found '(' without matching ')'"); get_next_etoken(); break; - case e3: /* Missing operator error */ - error("Missing operator: inserting '+'"); + case NOOP_E: /* Missing operator error */ + error_named("Missing operator after", a.text); + /* We insert a "+" token so that the rest of the expression + can be compiled. */ put_token_back(); current_token.type = OP_TT; current_token.value = PLUS_OP; @@ -2071,7 +2145,7 @@ extern assembly_operand parse_expression(int context) current_token.text = "+"; break; - case e4: /* Expression ends with an open bracket */ + case OPENB_E: /* Expression ends with an open bracket */ error("Found '(' without matching ')'"); sr_sp--; break; @@ -2099,6 +2173,80 @@ extern int test_for_incdec(assembly_operand AO) return s*(ET[ET[AO.value].down].value.value); } + +/* Determine if the operand (a parsed expression) is a constant (as + per is_constant_ot()) or a comma-separated list of such constants. + + "(1)" and "(1,2,3)" both count, and even "((1,2),3)", but + not "(1,(2,3))"; the list must be left-associated. + + Backpatched constants (function names, etc) are acceptable, as are + folded constant expressions. Variables are right out. + + The constants are stored in the ops_found array, up to a maximum of + max_ops_found. For Inform parsing reasons, the array list is backwards + from the order found. + + Returns the number of constants found. If the expression is not a list of + constants, returns zero. + + (The return value may be more than max_ops_found, in which case we weren't + able to return them all in the array.) +*/ +extern int test_constant_op_list(const assembly_operand *AO, assembly_operand *ops_found, int max_ops_found) +{ + int count = 0; + int n; + + if (AO->type != EXPRESSION_OT) { + if (!is_constant_ot(AO->type)) + return 0; + + if (ops_found && max_ops_found > 0) + ops_found[0] = *AO; + return 1; + } + + n = AO->value; + + /* For some reason the top node is always a COMMA with no .right, + just a .down. Should we rely on this? For now yes. */ + + if (operators[ET[n].operator_number].token_value != COMMA_SEP) + return 0; + if (ET[n].right != -1) + return 0; + n = ET[n].down; + + while (TRUE) { + if (ET[n].right != -1) { + if (ET[ET[n].right].down != -1) + return 0; + if (!is_constant_ot(ET[ET[n].right].value.type)) + return 0; + + if (ops_found && max_ops_found > count) + ops_found[count] = ET[ET[n].right].value; + count++; + } + + if (ET[n].down == -1) { + if (!is_constant_ot(ET[n].value.type)) + return 0; + + if (ops_found && max_ops_found > count) + ops_found[count] = ET[n].value; + count++; + return count; + } + + if (operators[ET[n].operator_number].token_value != COMMA_SEP) + return 0; + + n = ET[n].down; + } +} + /* ========================================================================= */ /* Data structure management routines */ /* ------------------------------------------------------------------------- */ diff --git a/src/files.c b/src/files.c index 50fdf5a..68d1c55 100644 --- a/src/files.c +++ b/src/files.c @@ -7,8 +7,8 @@ /* routines in "inform.c", since they are tied up with ICL */ /* settings and are very host OS-dependent. */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -40,7 +40,7 @@ int32 total_chars_read; /* Characters read in (from all static int checksum_low_byte, /* For calculating the Z-machine's */ checksum_high_byte; /* "verify" checksum */ -static int32 checksum_long; /* For the Glulx checksum, */ +static uint32 checksum_long; /* For the Glulx checksum, */ static int checksum_count; /* similarly */ /* ------------------------------------------------------------------------- */ @@ -115,7 +115,7 @@ extern void load_sourcefile(char *filename_given, int same_directory_flag) do { x = translate_in_filename(x, name, filename_given, same_directory_flag, (total_files==0)?1:0); - handle = fopen(name,"r"); + handle = fopen(name,"rb"); } while ((handle == NULL) && (x != 0)); InputFiles[total_files].filename = my_malloc(strlen(name)+1, "filename storage"); @@ -300,16 +300,16 @@ static void sf_put(int c) switch (checksum_count) { case 0: - checksum_long += (((int32)(c & 0xFF)) << 24); + checksum_long += (((uint32)(c & 0xFF)) << 24); break; case 1: - checksum_long += (((int32)(c & 0xFF)) << 16); + checksum_long += (((uint32)(c & 0xFF)) << 16); break; case 2: - checksum_long += (((int32)(c & 0xFF)) << 8); + checksum_long += (((uint32)(c & 0xFF)) << 8); break; case 3: - checksum_long += ((int32)(c & 0xFF)); + checksum_long += ((uint32)(c & 0xFF)); break; } @@ -357,7 +357,7 @@ static void output_compression(int entnum, int32 *size, int *count) (*size) += 1; break; case 3: - cx = (char *)abbreviations_at + ent->u.val*MAX_ABBREV_LENGTH; + cx = abbreviation_text(ent->u.val); while (*cx) { sf_put(*cx); cx++; @@ -619,7 +619,6 @@ static void output_file_z(void) static void output_file_g(void) { char new_name[PATHLEN]; int32 size, i, j, offset; - int32 VersionNum; uint32 code_length, size_before_code, next_cons_check; int use_function; int first_byte_of_triple, second_byte_of_triple, third_byte_of_triple; @@ -646,35 +645,33 @@ static void output_file_g(void) /* Determine the version number. */ - VersionNum = 0x00020000; + final_glulx_version = 0x00020000; /* Increase for various features the game may have used. */ if (no_unicode_chars != 0 || (uses_unicode_features)) { - VersionNum = 0x00030000; + final_glulx_version = 0x00030000; } if (uses_memheap_features) { - VersionNum = 0x00030100; + final_glulx_version = 0x00030100; } if (uses_acceleration_features) { - VersionNum = 0x00030101; + final_glulx_version = 0x00030101; } if (uses_float_features) { - VersionNum = 0x00030102; + final_glulx_version = 0x00030102; } if (uses_double_features || uses_extundo_features) { - VersionNum = 0x00030103; + final_glulx_version = 0x00030103; } /* And check if the user has requested a specific version. */ if (requested_glulx_version) { - if (requested_glulx_version < VersionNum) { - static char error_message_buff[256]; - sprintf(error_message_buff, "Version 0x%08lx requested, but \ -game features require version 0x%08lx", (long)requested_glulx_version, (long)VersionNum); - warning(error_message_buff); + if (requested_glulx_version < final_glulx_version) { + warning_fmt("Version 0x%08lx requested, but game features require version 0x%08lx", + (long)requested_glulx_version, (long)final_glulx_version); } else { - VersionNum = requested_glulx_version; + final_glulx_version = requested_glulx_version; } } @@ -687,10 +684,10 @@ game features require version 0x%08lx", (long)requested_glulx_version, (long)Ver sf_put('u'); sf_put('l'); /* Version number. */ - sf_put((VersionNum >> 24)); - sf_put((VersionNum >> 16)); - sf_put((VersionNum >> 8)); - sf_put((VersionNum)); + sf_put((final_glulx_version >> 24)); + sf_put((final_glulx_version >> 16)); + sf_put((final_glulx_version >> 8)); + sf_put((final_glulx_version)); /* RAMSTART */ sf_put((Write_RAM_At >> 24)); sf_put((Write_RAM_At >> 16)); @@ -1220,9 +1217,9 @@ extern void open_transcript_file(char *what_of) transcript_open = TRUE; - sprintf(topline_buffer, "Transcript of the text of \"%s\"", what_of); + snprintf(topline_buffer, 256, "Transcript of the text of \"%s\"", what_of); write_to_transcript_file(topline_buffer, STRCTX_INFO); - sprintf(topline_buffer, "[From %s]", banner_line); + snprintf(topline_buffer, 256, "[From %s]", banner_line); write_to_transcript_file(topline_buffer, STRCTX_INFO); if (TRANSCRIPT_FORMAT == 1) { write_to_transcript_file("[I:info, G:game text, V:veneer text, L:lowmem string, A:abbreviation, D:dict word, O:object name, S:symbol, X:infix]", STRCTX_INFO); @@ -1242,10 +1239,22 @@ extern void close_transcript_file(void) { char botline_buffer[256]; char sn_buffer[7]; + write_to_transcript_file("", STRCTX_INFO); + + if (!glulx_mode) { + snprintf(botline_buffer, 256, "[Compiled Z-machine version %d]", version_number); + } + else { + int32 major = (final_glulx_version >> 16) & 0xFFFF; + int32 minor = (final_glulx_version >> 8) & 0xFF; + int32 patch = final_glulx_version & 0xFF; + snprintf(botline_buffer, 256, "[Compiled Glulx version %d.%d.%d]", major, minor, patch); + } + write_to_transcript_file(botline_buffer, STRCTX_INFO); + write_serial_number(sn_buffer); - sprintf(botline_buffer, "[End of transcript: release %d, serial %s]", + snprintf(botline_buffer, 256, "[End of transcript: release %d, serial %s]", release_number, sn_buffer); - write_to_transcript_file("", STRCTX_INFO); write_to_transcript_file(botline_buffer, STRCTX_INFO); write_to_transcript_file("", STRCTX_INFO); diff --git a/src/header.h b/src/header.h index 5094c81..80bfa1e 100644 --- a/src/header.h +++ b/src/header.h @@ -1,10 +1,10 @@ /* ------------------------------------------------------------------------- */ /* Header file for Inform: Z-machine ("Infocom" format) compiler */ /* */ -/* Inform 6.41 */ +/* Inform 6.42 */ /* */ /* This header file and the others making up the Inform source code are */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -34,8 +34,8 @@ /* ------------------------------------------------------------------------- */ /* For releases, set to the release date in the form "1st January 2000" */ -#define RELEASE_DATE "22nd July 2022" -#define RELEASE_NUMBER 1641 +#define RELEASE_DATE "10th February 2024" +#define RELEASE_NUMBER 1642 #define GLULX_RELEASE_NUMBER 38 #define VNUMBER RELEASE_NUMBER @@ -570,14 +570,14 @@ #define ReadInt32(ptr) \ - ( (((int32)(((uchar *)(ptr))[0])) << 24) \ - | (((int32)(((uchar *)(ptr))[1])) << 16) \ - | (((int32)(((uchar *)(ptr))[2])) << 8) \ - | (((int32)(((uchar *)(ptr))[3])) ) ) + ( (((uint32)(((uchar *)(ptr))[0])) << 24) \ + | (((uint32)(((uchar *)(ptr))[1])) << 16) \ + | (((uint32)(((uchar *)(ptr))[2])) << 8) \ + | (((uint32)(((uchar *)(ptr))[3])) ) ) #define ReadInt16(ptr) \ - ( (((int32)(((uchar *)(ptr))[0])) << 8) \ - | (((int32)(((uchar *)(ptr))[1])) ) ) + ( (((uint32)(((uchar *)(ptr))[0])) << 8) \ + | (((uint32)(((uchar *)(ptr))[1])) ) ) #define WriteInt32(ptr, val) \ ((ptr)[0] = (uchar)(((int32)(val)) >> 24), \ @@ -602,10 +602,6 @@ /* ------------------------------------------------------------------------- */ #define MAX_ERRORS 100 -#define MAX_IDENTIFIER_LENGTH 32 -#define MAX_ABBREV_LENGTH 64 -#define MAX_DICT_WORD_SIZE 40 -#define MAX_DICT_WORD_BYTES (40*4) #define MAX_NUM_ATTR_BYTES 39 #define MAX_VERB_WORD_SIZE 120 @@ -650,10 +646,12 @@ typedef struct memory_list_s size_t count; /* number of items allocated */ } memory_list; -typedef struct identstruct_s -{ - char text[MAX_IDENTIFIER_LENGTH+1]; -} identstruct; +typedef struct brief_location_s +{ int32 file_index; + int32 line_number; + int32 orig_file_index; + int32 orig_line_number; +} brief_location; typedef struct assembly_operand_t { int type; /* ?_OT value */ @@ -673,8 +671,11 @@ typedef struct variableinfo_s { typedef struct verbt { int lines; - int *l; /* alloced array */ + int *l; /* alloced array of grammar line indexes + (positions in grammar_lines[]) */ int size; /* allocated size of l */ + brief_location line; /* originally defined at */ + int used; /* only set at locate_dead_grammar_lines() time */ } verbt; typedef struct actioninfo_s { @@ -765,6 +766,8 @@ typedef struct abbreviation_s { int value; int quality; int freq; + int textpos; /* in abbreviations_text */ + int textlen; } abbreviation; typedef struct maybe_file_position_S @@ -792,13 +795,6 @@ typedef struct debug_locations_s int reference_count; } debug_locations; -typedef struct brief_location_s -{ int32 file_index; - int32 line_number; - int32 orig_file_index; - int32 orig_line_number; -} brief_location; - typedef struct debug_location_beginning_s { debug_locations *head; int32 beginning_byte_index; @@ -822,6 +818,7 @@ typedef struct lexeme_data_s { char *text; /* points at lextexts array */ int32 value; int type; /* a *_TT value */ + int newsymbol; /* (for SYMBOL_TT) this token created the symbol */ debug_location location; int lextext; /* index of text string in lextexts */ int context; /* lexical context used to interpret this token */ @@ -1118,6 +1115,8 @@ typedef struct operator_s #define picture_table_zc 115 #define print_unicode_zc 116 #define check_unicode_zc 117 +#define set_true_colour_zc 118 +#define buffer_screen_zc 119 /* ------------------------------------------------------------------------- */ @@ -1226,12 +1225,23 @@ typedef struct operator_s #define dstore_gm 3 -#define SYMBOL_TT 0 /* value = index in symbol table */ -#define NUMBER_TT 1 /* value = the number */ -#define DQ_TT 2 /* no value */ -#define SQ_TT 3 /* no value */ -#define SEP_TT 4 /* value = the _SEP code */ -#define EOF_TT 5 /* no value */ +#define SYMBOL_TT 0 /* symbol. + value = index in symbol table */ +#define NUMBER_TT 1 /* number (including hex, float, + etc). + value = the number */ +#define DQ_TT 2 /* double-quoted string. + no value; look at the text */ +#define SQ_TT 3 /* single-quoted string. + no value */ +#define UQ_TT 4 /* unquoted string; only when + dont_enter_into_symbol_table + is true. + no value */ +#define SEP_TT 5 /* separator (punctuation). + value = the _SEP code */ +#define EOF_TT 6 /* end of file. + no value */ #define STATEMENT_TT 100 /* a statement keyword */ #define SEGMENT_MARKER_TT 101 /* with/has/class etc. */ @@ -1278,22 +1288,25 @@ typedef struct operator_s /* Symbol flag definitions (in no significant order) */ /* ------------------------------------------------------------------------- */ -#define UNKNOWN_SFLAG 1 -#define REPLACE_SFLAG 2 -#define USED_SFLAG 4 -#define DEFCON_SFLAG 8 -#define STUB_SFLAG 16 -#define IMPORT_SFLAG 32 -#define EXPORT_SFLAG 64 -#define ALIASED_SFLAG 128 +#define UNKNOWN_SFLAG 1 /* no definition known */ +#define REPLACE_SFLAG 2 /* routine marked for Replace */ +#define USED_SFLAG 4 /* referred to in code */ +#define DEFCON_SFLAG 8 /* defined by Default */ +#define STUB_SFLAG 16 /* defined by Stub */ +#define UNHASHED_SFLAG 32 /* removed from hash chain */ +#define DISCARDED_SFLAG 64 /* removed and should never have been used */ +#define ALIASED_SFLAG 128 /* defined as property/attribute alias name */ -#define CHANGE_SFLAG 256 -#define SYSTEM_SFLAG 512 -#define INSF_SFLAG 1024 -#define UERROR_SFLAG 2048 -#define ACTION_SFLAG 4096 -#define REDEFINABLE_SFLAG 8192 -#define STAR_SFLAG 16384 +#define CHANGE_SFLAG 256 /* defined by Default with a value, + or symbol has a backpatchable value */ +#define SYSTEM_SFLAG 512 /* created by compiler */ +#define INSF_SFLAG 1024 /* created in System_File */ +#define UERROR_SFLAG 2048 /* "No such constant" error issued */ +#define ACTION_SFLAG 4096 /* action name constant (Foo_A) */ +#define REDEFINABLE_SFLAG 8192 /* built-in symbol that can be redefined + by the user */ +#define STAR_SFLAG 16384 /* function defined with "*" or property named + "foo_to" */ /* ------------------------------------------------------------------------- */ /* Symbol type definitions */ @@ -1920,7 +1933,9 @@ typedef struct operator_s #define OBJECT_MV 16 /* Ref to internal object number */ #define STATIC_ARRAY_MV 17 /* Ref to internal static array address */ -#define LARGEST_BPATCH_MV 17 /* Larger marker values are never written +#define ERROR_MV 18 /* An error was reported while + generating this value */ +#define LARGEST_BPATCH_MV 18 /* Larger marker values are never written to backpatch tables */ /* Values 32-35 were used only for module import/export. */ @@ -2142,7 +2157,7 @@ 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, +extern int32 assemble_routine_header(int debug_flag, char *name, int embedded_flag, int the_symbol); extern void assemble_routine_end(int embedded_flag, debug_locations locations); @@ -2248,6 +2263,7 @@ extern int32 zcode_backpatch_size, staticarray_backpatch_size, extern int backpatch_marker, backpatch_error_flag; extern char *describe_mv(int mval); +extern char *describe_mv_short(int mval); extern int32 backpatch_value(int32 value); extern void backpatch_zmachine_image_z(void); @@ -2290,7 +2306,7 @@ extern void make_upper_case(char *str); extern brief_location routine_starts_line; -extern int no_routines, no_named_routines, no_locals, no_termcs; +extern int no_routines, no_named_routines, no_termcs; extern int terminating_characters[]; extern int parse_given_directive(int internal_flag); @@ -2307,29 +2323,35 @@ extern int no_errors, no_warnings, no_suppressed_warnings, no_compiler_errors; extern ErrorPosition ErrorReport; extern void fatalerror(char *s) NORETURN; +extern void fatalerror_fmt(const char *format, ...) NORETURN; extern void fatalerror_named(char *s1, char *s2) NORETURN; -extern void memory_out_error(int32 size, int32 howmany, char *name) NORETURN; -extern void error_max_dynamic_strings(int index); -extern void error_max_abbreviations(int index); +extern void fatalerror_memory_out(int32 size, int32 howmany, char *name) NORETURN; + extern void error(char *s); +extern void error_fmt(const char *format, ...); extern void error_named(char *s1, char *s2); -extern void error_numbered(char *s1, int val); extern void error_named_at(char *s1, char *s2, brief_location report_line); extern void ebf_error(char *s1, char *s2); +extern void ebf_curtoken_error(char *s); extern void ebf_symbol_error(char *s1, char *name, char *type, brief_location report_line); extern void char_error(char *s, int ch); extern void unicode_char_error(char *s, int32 uni); -extern void no_such_label(char *lname); +extern void error_max_dynamic_strings(int index); +extern void error_max_abbreviations(int index); + extern void warning(char *s); -extern void warning_numbered(char *s1, int val); +extern void warning_fmt(const char *format, ...); extern void warning_named(char *s1, char *s2); +extern void warning_at(char *name, brief_location report_line); 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); + extern int compiler_error(char *s); extern int compiler_error_named(char *s1, char *s2); extern void print_sorry_message(void); +extern char *current_location_text(void); #ifdef ARC_THROWBACK extern int throwback_switch; @@ -2367,9 +2389,10 @@ 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 void show_tree(const assembly_operand *AO, int annotate); extern assembly_operand parse_expression(int context); extern int test_for_incdec(assembly_operand AO); +extern int test_constant_op_list(const assembly_operand *AO, assembly_operand *ops_found, int max_ops_found); /* ------------------------------------------------------------------------- */ /* Extern definitions for "files" */ @@ -2461,7 +2484,7 @@ extern int extern int oddeven_packing_switch; extern int glulx_mode, compression_switch; -extern int32 requested_glulx_version; +extern int32 requested_glulx_version, final_glulx_version; extern int error_format, store_the_text, asm_trace_setting, expr_trace_setting, tokens_trace_setting, @@ -2501,7 +2524,8 @@ 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 identstruct *local_variable_names; +extern int no_locals; +extern int *local_variable_name_offsets; extern int32 token_value; extern int token_type; @@ -2514,10 +2538,15 @@ extern void discard_token_location(debug_location_beginning beginning); extern debug_locations get_token_location_end(debug_location_beginning beginning); extern void describe_token_triple(const char *text, int32 value, int type); +#define describe_current_token() describe_token_triple(token_text, token_value, token_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 clear_local_variables(void); +extern void add_local_variable(char *name); +extern char *get_local_variable_name(int index); + extern void declare_systemfile(void); extern int is_systemfile(void); extern void report_errors_at_current_line(void); @@ -2555,9 +2584,12 @@ 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 ZCODE_MAX_INLINE_STRING; extern int NUM_ATTR_BYTES, GLULX_OBJECT_EXT_BYTES; extern int WARN_UNUSED_ROUTINES, OMIT_UNUSED_ROUTINES; extern int STRIP_UNREACHABLE_LABELS; +extern int OMIT_SYMBOL_TABLE; +extern int LONG_DICT_FLAG_BUG; extern int TRANSCRIPT_FORMAT; /* These macros define offsets that depend on the value of NUM_ATTR_BYTES. @@ -2640,8 +2672,8 @@ 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 int symbol_index(char *lexeme_text, int hashcode, int *created); +extern void end_symbol_scope(int k, int neveruse); 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); @@ -2684,6 +2716,7 @@ extern void parse_code_block(int break_label, int continue_label, extern void match_close_bracket(void); extern void parse_statement(int break_label, int continue_label); +extern void parse_statement_singleexpr(assembly_operand AO); extern int parse_label(void); /* ------------------------------------------------------------------------- */ @@ -2728,7 +2761,6 @@ extern int32 low_strings_top; extern int no_abbreviations; extern int abbrevs_lookup_table_made, is_abbreviation; -extern uchar *abbreviations_at; extern abbreviation *abbreviations; extern int32 total_chars_trans, total_bytes_trans, @@ -2796,6 +2828,7 @@ extern int32 compile_string(char *b, 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 char *abbreviation_text(int num); extern void show_dictionary(int level); extern void word_to_ascii(uchar *p, char *result); extern void print_dict_word(int node); @@ -2838,6 +2871,7 @@ extern int32 *grammar_token_routine, extern void find_the_actions(void); extern void make_fake_action(void); extern assembly_operand action_of_name(char *name); +extern void locate_dead_grammar_lines(void); extern void make_verb(void); extern void extend_verb(void); extern void list_verb_table(void); diff --git a/src/inform.c b/src/inform.c index 1890d58..25da037 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.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -47,7 +47,9 @@ int version_number, /* 3 to 8 (Z-code) */ int32 scale_factor, /* packed address multiplier */ length_scale_factor; /* length-in-header multiplier */ -int32 requested_glulx_version; +int32 requested_glulx_version; /* version requested via -v switch */ +int32 final_glulx_version; /* requested version combined with game + feature requirements */ extern void select_version(int vn) { version_number = vn; @@ -154,17 +156,17 @@ static void select_target(int targ) 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); + warning_fmt("INDIV_PROP_START should be at least 256 in Glulx; setting to %d", INDIV_PROP_START); } if (NUM_ATTR_BYTES % 4 != 3) { NUM_ATTR_BYTES += (3 - (NUM_ATTR_BYTES % 4)); - warning_numbered("NUM_ATTR_BYTES must be a multiple of four, plus three. Increasing to", NUM_ATTR_BYTES); + warning_fmt("NUM_ATTR_BYTES must be a multiple of four, plus three; increasing to %d", NUM_ATTR_BYTES); } if (DICT_CHAR_SIZE != 1 && DICT_CHAR_SIZE != 4) { DICT_CHAR_SIZE = 4; - warning_numbered("DICT_CHAR_SIZE must be either 1 or 4. Setting to", DICT_CHAR_SIZE); + warning_fmt("DICT_CHAR_SIZE must be either 1 or 4; setting to %d", DICT_CHAR_SIZE); } } @@ -173,17 +175,10 @@ static void select_target(int targ) MAX_LOCAL_VARIABLES = MAX_KEYWORD_GROUP_SIZE; } - if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) { - DICT_WORD_SIZE = MAX_DICT_WORD_SIZE; - warning_numbered( - "DICT_WORD_SIZE cannot exceed MAX_DICT_WORD_SIZE; resetting", - MAX_DICT_WORD_SIZE); - /* MAX_DICT_WORD_SIZE can be increased in header.h without fear. */ - } if (NUM_ATTR_BYTES > MAX_NUM_ATTR_BYTES) { NUM_ATTR_BYTES = MAX_NUM_ATTR_BYTES; - warning_numbered( - "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting", + warning_fmt( + "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting to %d", MAX_NUM_ATTR_BYTES); /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */ } @@ -354,6 +349,7 @@ static void reset_switch_settings(void) compression_switch = TRUE; glulx_mode = FALSE; requested_glulx_version = 0; + final_glulx_version = 0; /* These aren't switches, but for clarity we reset them too. */ asm_trace_level = 0; @@ -1041,6 +1037,7 @@ static void run_pass(void) sort_dictionary(); if (track_unused_routines) locate_dead_functions(); + locate_dead_grammar_lines(); construct_storyfile(); } @@ -1128,14 +1125,14 @@ disabling -X switch\n"); run_pass(); + if (no_errors==0) { output_file(); output_has_occurred = TRUE; } + else { output_has_occurred = FALSE; } + if (transcript_switch) { write_dictionary_to_transcript(); close_transcript_file(); } - if (no_errors==0) { output_file(); output_has_occurred = TRUE; } - else { output_has_occurred = FALSE; } - if (debugfile_switch) { end_debug_file(); } @@ -1169,7 +1166,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 - 2022.\n\n"); +Copyright (c) Graham Nelson 1993 - 2024.\n\n"); /* For people typing just "inform", a summary only: */ @@ -1539,6 +1536,16 @@ static int strcpyupper(char *to, char *from, int max) static void execute_icl_command(char *p); static int execute_dashdash_command(char *p, char *p2); +/* Open a file and see whether the initial lines match the "!% ..." format + used for ICL commands. Stop when we reach a line that doesn't. + + This does not do line break conversion. It just reads to the next + \n (and ignores \r as whitespace). Therefore it will work on Unix and + DOS source files, but fail to cope with Mac-Classic (\r) source files. + I am not going to worry about this, because files from the Mac-Classic + era shouldn't have "!%" lines; that convention was invented well after + Mac switched over to \n format. + */ static int execute_icl_header(char *argname) { FILE *command_file; @@ -1551,7 +1558,7 @@ static int execute_icl_header(char *argname) do { x = translate_in_filename(x, filename, argname, 0, 1); - command_file = fopen(filename,"r"); + command_file = fopen(filename,"rb"); } while ((command_file == NULL) && (x != 0)); if (!command_file) { /* Fail silently. The regular compiler will try to open the file diff --git a/src/lexer.c b/src/lexer.c index 63cafbc..1843b0a 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "lexer" : Lexical analyser */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -29,10 +29,9 @@ int total_source_line_count, /* Number of source lines so far */ (generally as a result of an error message or the start of pass) */ dont_enter_into_symbol_table, /* Return names as text (with - token type DQ_TT, i.e., as if - they had double-quotes around) - and not as entries in the symbol - table, when TRUE. If -2, only the + token type UQ_TT) and not as + entries in the symbol table, + when TRUE. If -2, only the keyword table is searched. */ return_sp_as_variable; /* When TRUE, the word "sp" denotes the stack pointer variable @@ -269,8 +268,7 @@ static lexeme_data circle[CIRCLE_SIZE]; typedef struct lextext_s { char *text; - size_t size; /* Allocated size (including terminal null) - This is always at least MAX_IDENTIFIER_LENGTH+1 */ + size_t size; /* Allocated size (including terminal null) */ } lextext; static lextext *lextexts; /* Allocated to no_lextexts */ @@ -286,12 +284,19 @@ static int lex_pos; /* Current write position in that lextext */ /* ------------------------------------------------------------------------- */ /* The lexer itself needs up to 3 characters of lookahead (it uses an */ /* LR(3) grammar to translate characters into tokens). */ +/* */ +/* Past the end of the stream, we fill in zeros. This has the awkward */ +/* side effect that a zero byte in a source file will silently terminate */ +/* it, rather than producing an "illegal source character" error. */ +/* On the up side, we can compile veneer routines (which are null- */ +/* terminated strings) with no extra work. */ /* ------------------------------------------------------------------------- */ #define LOOKAHEAD_SIZE 3 static int current, lookahead, /* The latest character read, and */ lookahead2, lookahead3; /* the three characters following it */ + /* (zero means end-of-stream) */ static int pipeline_made; /* Whether or not the pipeline of characters has been constructed @@ -337,6 +342,8 @@ extern void describe_token_triple(const char *text, int32 value, int type) break; case SQ_TT: printf("string '%s'", text); break; + case UQ_TT: printf("barestring %s", text); + break; case SEP_TT: printf("separator '%s'", text); break; case EOF_TT: printf("end of file"); @@ -440,6 +447,7 @@ static char *opcode_list_z[] = { "get_wind_prop", "scroll_window", "pop_stack", "read_mouse", "mouse_window", "push_stack", "put_wind_prop", "print_form", "make_menu", "picture_table", "print_unicode", "check_unicode", + "set_true_colour", "buffer_screen", "" }; @@ -605,11 +613,8 @@ static int lexical_context(void) always translate to the same output tokens whenever the context is the same. - In fact, for efficiency reasons this number omits the bit of - information held in the variable "dont_enter_into_symbol_table". - Inform never needs to backtrack through tokens parsed in that - way (thankfully, as it would be expensive indeed to check - the tokens). */ + (For many years, the "dont_enter_into_symbol_table" variable + was omitted from this number. But now we can include it.) */ int c = 0; if (opcode_names.enabled) c |= 1; @@ -625,11 +630,17 @@ static int lexical_context(void) if (local_variables.enabled) c |= 1024; if (return_sp_as_variable) c |= 2048; + if (dont_enter_into_symbol_table) c |= 4096; + return(c); } static void print_context(int c) { + if (c < 0) { + printf("??? "); + return; + } if ((c & 1) != 0) printf("OPC "); if ((c & 2) != 0) printf("DIR "); if ((c & 4) != 0) printf("TK "); @@ -642,6 +653,7 @@ static void print_context(int c) if ((c & 512) != 0) printf("SCON "); if ((c & 1024) != 0) printf("LV "); if ((c & 2048) != 0) printf("sp "); + if ((c & 4096) != 0) printf("dontent "); } static int *keywords_hash_table; @@ -657,14 +669,22 @@ static int *local_variable_hash_codes; 119 for Glulx. */ +/* The number of local variables in the current routine. */ +int no_locals; + /* Names of local variables in the current routine. + The values are positions in local_variable_names_memlist. 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; +int *local_variable_name_offsets; + +static memory_list local_variable_names_memlist; +/* How much of local_variable_names_memlist is used by the no_local locals. */ +static int local_variable_names_usage; static char one_letter_locals[128]; @@ -729,9 +749,42 @@ static void make_keywords_tables(void) } } +extern void clear_local_variables(void) +{ + no_locals = 0; + local_variable_names_usage = 0; +} + +extern void add_local_variable(char *name) +{ + int len; + + if (no_locals >= MAX_LOCAL_VARIABLES-1) { + /* This should have been caught before we got here */ + error("too many local variables"); + return; + } + + len = strlen(name)+1; + ensure_memory_list_available(&local_variable_names_memlist, local_variable_names_usage + len); + local_variable_name_offsets[no_locals++] = local_variable_names_usage; + strcpy((char *)local_variable_names_memlist.data+local_variable_names_usage, name); + local_variable_names_usage += len; +} + +extern char *get_local_variable_name(int index) +{ + if (index < 0 || index >= no_locals) + return "???"; /* shouldn't happen */ + + return (char *)local_variable_names_memlist.data + local_variable_name_offsets[index]; +} + /* 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. */ + hash tables. + This must be called after add_local_variable(), but before we start + compiling function code. */ extern void construct_local_variable_tables(void) { int i, h; for (i=0; i= 0) + { int *i = keywords_data_table + 3*index; + keyword_group *kg = keyword_groups[*i]; + if (kg == &directives) + { char *q = kg->keywords[*(i+1)]; + if (((kg->case_sensitive) && (strcmp(p, q)==0)) + || ((!(kg->case_sensitive)) && (strcmpcis(p, q)==0))) + { circle[pos].type = kg->change_token_type; + circle[pos].value = *(i+1); + return; + } + } + index = *(i+2); + } + } + + circle[pos].type = UQ_TT; + circle[pos].value = 0; + return; + } + /* If this is assembly language, perhaps it is "sp"? */ if (return_sp_as_variable && (p[0]=='s') && (p[1]=='p') && (p[2]==0)) @@ -790,7 +876,9 @@ static void interpret_identifier(char *p, int pos, int dirs_only_flag) if (index >= 0) { for (;index= 0) { int *i = keywords_data_table + 3*index; keyword_group *kg = keyword_groups[*i]; - if (((!dirs_only_flag) && (kg->enabled)) - || (dirs_only_flag && (kg == &directives))) + if (kg->enabled) { char *q = kg->keywords[*(i+1)]; if (((kg->case_sensitive) && (strcmp(p, q)==0)) || ((!(kg->case_sensitive)) && (strcmpcis(p, q)==0))) @@ -824,11 +910,9 @@ static void interpret_identifier(char *p, int pos, int dirs_only_flag) index = *(i+2); } - if (dirs_only_flag) return; - /* Search for the name; create it if necessary. */ - circle[pos].value = symbol_index(p, hashcode); + circle[pos].value = symbol_index(p, hashcode, &circle[pos].newsymbol); circle[pos].type = SYMBOL_TT; } @@ -901,6 +985,7 @@ static void make_tokeniser_grid(void) tokeniser_grid[0] = EOF_CODE; tokeniser_grid[' '] = WHITESPACE_CODE; tokeniser_grid['\n'] = WHITESPACE_CODE; + tokeniser_grid['\r'] = WHITESPACE_CODE; tokeniser_grid['$'] = RADIX_CODE; tokeniser_grid['!'] = COMMENT_CODE; @@ -1378,7 +1463,7 @@ static int32 construct_double(int wanthigh, int signbit, double intv, double fra /* */ /* Note that file_load_chars(p, size) loads "size" bytes into buffer "p" */ /* from the current input file. If the file runs out, then if it was */ -/* the last source file 4 EOF characters are placed in the buffer: if it */ +/* the last source file 4 null characters are placed in the buffer: if it */ /* was only an Include file ending, then a '\n' character is placed there */ /* (essentially to force termination of any comment line) followed by */ /* three harmless spaces. */ @@ -1541,12 +1626,33 @@ static int get_next_char_from_pipeline(void) CurrentLB->chars_read++; if (forerrors_pointer < FORERRORS_SIZE-1) forerrors_buff[forerrors_pointer++] = current; - if (current == '\n') reached_new_line(); + + /* The file is open in binary mode, so we have to do our own newline + conversion. (We want to do it consistently across all platforms.) + + The strategy is to convert all \r (CR) characters to \n (LF), but + *don't* advance the line counter for \r if it's followed by \n. + The rest of the lexer treats multiple \n characters the same as + one, so the simple conversion will work out okay. + + (Note that, for historical reasons, a ctrl-L (formfeed) is also + treated as \r. This conversion has already been handled by + source_to_iso_grid[].) + */ + if (current == '\n') { + reached_new_line(); + } + else if (current == '\r') { + current = '\n'; + if (lookahead != '\n') + reached_new_line(); + } + return(current); } /* ------------------------------------------------------------------------- */ -/* Source 2: from a string */ +/* Source 2: from a (null-terminated) string */ /* ------------------------------------------------------------------------- */ static int source_to_analyse_pointer; /* Current read position */ @@ -1565,7 +1671,12 @@ static int get_next_char_from_string(void) CurrentLB->chars_read++; if (forerrors_pointer < FORERRORS_SIZE-1) forerrors_buff[forerrors_pointer++] = current; + + /* We shouldn't have \r when compiling from string (veneer function). + If we do, just shove it under the carpet. */ + if (current == '\r') current = '\n'; if (current == '\n') reached_new_line(); + return(current); } @@ -1586,7 +1697,8 @@ static int get_next_char_from_string(void) /* */ /* restart_lexer(source, name) if source is NULL, initialise the lexer */ /* to read from source files; */ -/* otherwise, to read from this string. */ +/* otherwise, to read from this null- */ +/* terminated string. */ /* ------------------------------------------------------------------------- */ extern void release_token_texts(void) @@ -1632,11 +1744,28 @@ extern void release_token_texts(void) extern void put_token_back(void) { tokens_put_back++; + int pos = circle_position - tokens_put_back + 1; + if (pos<0) pos += CIRCLE_SIZE; + if (tokens_trace_level > 0) - { if (tokens_trace_level == 1) printf("<- "); - else printf("<-\n"); + { + printf("<- "); + if (tokens_trace_level > 1) { + describe_token(&circle[pos]); + printf("\n"); + } } + if (circle[pos].type == SYMBOL_TT && circle[pos].newsymbol) { + /* Remove the symbol from the symbol table. (Or mark it as unreachable + anyhow.) */ + end_symbol_scope(circle[pos].value, TRUE); + /* Remove new-symbol flag, and force reinterpretation next time + we see the symbol. */ + circle[pos].newsymbol = FALSE; + circle[pos].context = -1; + } + /* The following error, of course, should never happen! */ if (tokens_put_back == CIRCLE_SIZE) @@ -1695,7 +1824,9 @@ static void lexadds(char *str) } extern void get_next_token(void) -{ int d, i, j, k, quoted_size, e, radix, context; int32 n; char *r; +{ int d, i, j, k, quoted_size, e, radix, context; + uint32 n; + char *r; int floatend; int returning_a_put_back_token = TRUE; @@ -1708,7 +1839,7 @@ extern void get_next_token(void) if (context != circle[i].context) { j = circle[i].type; if ((j==0) || ((j>=100) && (j<200))) - interpret_identifier(circle[i].text, i, FALSE); + interpret_identifier(circle[i].text, i); circle[i].context = context; } goto ReturnBack; @@ -1723,7 +1854,7 @@ extern void get_next_token(void) /* 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].size = 64; /* this can grow */ lextexts[lex_index].text = my_malloc(lextexts[lex_index].size, "one lexeme text"); } lex_pos = 0; @@ -1733,6 +1864,7 @@ extern void get_next_token(void) circle[circle_position].text = NULL; /* will fill in later */ circle[circle_position].value = 0; circle[circle_position].type = 0; + circle[circle_position].newsymbol = FALSE; circle[circle_position].context = context; StartTokenAgain: @@ -1758,7 +1890,7 @@ extern void get_next_token(void) goto StartTokenAgain; case COMMENT_CODE: - while ((lookahead != '\n') && (lookahead != 0)) + while ((lookahead != '\n') && (lookahead != '\r') && (lookahead != 0)) (*get_next_char)(); goto StartTokenAgain; @@ -1779,7 +1911,7 @@ extern void get_next_token(void) lexaddc(0); circle[circle_position].type = NUMBER_TT; - circle[circle_position].value = n; + circle[circle_position].value = (int32)n; break; FloatNumber: @@ -1869,11 +2001,7 @@ extern void get_next_token(void) quoted_size=0; do { e = d; d = (*get_next_char)(); lexaddc(d); - if (quoted_size++==64) - { error( - "Too much text for one pair of quotations '...' to hold"); - lexaddc('\''); break; - } + quoted_size++; if ((d == '\'') && (e != '@')) { if (quoted_size == 1) { d = (*get_next_char)(); lexaddc(d); @@ -1882,28 +2010,27 @@ extern void get_next_token(void) } break; } - } while (d != EOF); - if (d==EOF) ebf_error("'\''", "end of file"); + } while (d != 0); + if (d==0) ebf_error("'\''", "end of file"); 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)(); lexaddc(d); if (d == '\n') { lex_pos--; while (lexlastc() == ' ') lex_pos--; if (lexlastc() != '^') lexaddc(' '); - while ((lookahead != EOF) && + while ((lookahead != 0) && (tokeniser_grid[lookahead] == WHITESPACE_CODE)) (*get_next_char)(); } else if (d == '\\') { int newline_passed = FALSE; lex_pos--; - while ((lookahead != EOF) && + while ((lookahead != 0) && (tokeniser_grid[lookahead] == WHITESPACE_CODE)) if ((d = (*get_next_char)()) == '\n') newline_passed = TRUE; @@ -1915,8 +2042,8 @@ extern void get_next_token(void) chb); } } - } while ((d != EOF) && (d!='\"')); - if (d==EOF) ebf_error("'\"'", "end of file"); + } while ((d != 0) && (d!='\"')); + if (d==0) ebf_error("'\"'", "end of file"); lexdelc(); circle[circle_position].type = DQ_TT; break; @@ -1924,37 +2051,13 @@ extern void get_next_token(void) case IDENTIFIER_CODE: /* Letter or underscore: an identifier */ lexaddc(d); n=1; - while ((n<=MAX_IDENTIFIER_LENGTH) - && ((tokeniser_grid[lookahead] == IDENTIFIER_CODE) + while (((tokeniser_grid[lookahead] == IDENTIFIER_CODE) || (tokeniser_grid[lookahead] == DIGIT_CODE))) n++, lexaddc((*get_next_char)()); 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, 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 */ - 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(lextexts[lex_index].text, circle_position, TRUE); - break; - } - - interpret_identifier(lextexts[lex_index].text, circle_position, FALSE); + interpret_identifier(lextexts[lex_index].text, circle_position); break; default: @@ -2059,7 +2162,10 @@ extern void get_next_token(void) else { printf("-> "); describe_token(&circle[i]); printf(" "); - if (tokens_trace_level > 2) print_context(circle[i].context); + if (tokens_trace_level > 2) { + if (circle[i].newsymbol) printf("newsym "); + print_context(circle[i].context); + } printf("\n"); } } @@ -2073,6 +2179,7 @@ extern void restart_lexer(char *lexical_source, char *name) for (i=0; i 1 || STRIP_UNREACHABLE_LABELS < 0) STRIP_UNREACHABLE_LABELS = 1; } + if (strcmp(command,"OMIT_SYMBOL_TABLE")==0) + { + OMIT_SYMBOL_TABLE=j, flag=1; + if (OMIT_SYMBOL_TABLE > 1 || OMIT_SYMBOL_TABLE < 0) + OMIT_SYMBOL_TABLE = 1; + } + if (strcmp(command,"LONG_DICT_FLAG_BUG")==0) + { + LONG_DICT_FLAG_BUG=j, flag=1; + if (LONG_DICT_FLAG_BUG > 1 || LONG_DICT_FLAG_BUG < 0) + LONG_DICT_FLAG_BUG = 1; + } if (strcmp(command,"SERIAL")==0) { if (j >= 0 && j <= 999999) diff --git a/src/objects.c b/src/objects.c index f122c3e..88715bb 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.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -50,9 +50,11 @@ static fproptg full_object_g; /* Equivalent for Glulx. This object are allocated dynamically as memory-lists */ -static char shortname_buffer[766]; /* Text buffer to hold the short name +static char *shortname_buffer; /* Text buffer to hold the short name (which is read in first, but written almost last) */ +static memory_list shortname_buffer_memlist; + static int parent_of_this_obj; static memory_list current_object_name; /* The name of the object currently @@ -99,8 +101,8 @@ int no_attributes, /* Number of attributes defined so far */ /* 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; - char *stype = ""; +{ char *stype = ""; + if (!printprops_switch) return; if (f == 0) stype = "Attr"; else if (f == 1) stype = "Prop"; else if (f == 2) stype = "Indiv"; @@ -108,7 +110,7 @@ static void trace_s(char *name, int32 number, int f) if (f != 1) printf(" "); else printf("%s%s",(commonprops[number].is_long)?"L":" ", (commonprops[number].is_additive)?"A":" "); - printf(" %s\n", name); + printf(" %-24s (%s)\n", name, current_location_text()); } extern void make_attribute(void) @@ -132,9 +134,9 @@ game to get an extra 16)"); else { if (no_attributes==NUM_ATTR_BYTES*8) { discard_token_location(beginning_debug_location); - error_numbered( - "All attributes already declared -- increase NUM_ATTR_BYTES to use \ -more than", + error_fmt( + "All %d attributes already declared -- increase NUM_ATTR_BYTES to use \ +more", NUM_ATTR_BYTES*8); panic_mode_error_recovery(); put_token_back(); @@ -147,7 +149,7 @@ more than", /* 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); + ebf_curtoken_error("new attribute name"); panic_mode_error_recovery(); put_token_back(); return; @@ -169,8 +171,7 @@ more than", if (!((token_type == SYMBOL_TT) && (symbols[token_value].type == ATTRIBUTE_T))) { discard_token_location(beginning_debug_location); - ebf_error("an existing attribute name after 'alias'", - token_text); + ebf_curtoken_error("an existing attribute name after 'alias'"); panic_mode_error_recovery(); put_token_back(); return; @@ -265,7 +266,7 @@ extern void make_property(void) /* 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); + ebf_curtoken_error("new property name"); panic_mode_error_recovery(); put_token_back(); return; @@ -326,8 +327,7 @@ extern void make_property(void) get_next_token(); if (!((token_type == SYMBOL_TT) && (symbols[token_value].type == PROPERTY_T))) - { ebf_error("an existing property name after 'alias'", - token_text); + { ebf_curtoken_error("an existing property name after 'alias'"); panic_mode_error_recovery(); put_token_back(); return; @@ -360,12 +360,10 @@ Advanced game to get 32 more)"); } else { if (no_properties==INDIV_PROP_START) { - char error_b[128]; discard_token_location(beginning_debug_location); - sprintf(error_b, + error_fmt( "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; @@ -589,11 +587,17 @@ static void property_inheritance_z(void) for (i=full_object.pp[k].l; i= 32) + { + if (i >= 32) { error("An additive property has inherited \ so many values that the list has overflowed the maximum 32 entries"); break; } + if ((version_number==3) && i >= 4) + { error("An additive property has inherited \ +so many values that the list has overflowed the maximum 4 entries"); + break; + } INITAOTV(&full_object.pp[k].ao[i], LONG_CONSTANT_OT, mark + j); j += 2; full_object.pp[k].ao[i].marker = INHERIT_MV; @@ -863,7 +867,13 @@ static int write_properties_between(int mark, int from, int to) } for (k=0; k= 32) { + /* We catch this earlier, but we'll check again to avoid overflowing ao[] */ + error("Too many values for Z-machine property"); + break; + } + if (full_object.pp[j].ao[k].marker != 0) backpatch_zmachine(full_object.pp[j].ao[k].marker, PROP_ZA, mark); properties_table[mark++] = full_object.pp[j].ao[k].value/256; @@ -893,6 +903,7 @@ static int write_property_block_z(char *shortname) if (shortname != NULL) { + /* The limit of 510 bytes, or 765 Z-characters, is a Z-spec limit. */ i = translate_text(510,shortname,STRCTX_OBJNAME); if (i < 0) { error ("Short name of object exceeded 765 Z-characters"); @@ -1161,7 +1172,7 @@ static void properties_segment_z(int this_segment) } if (token_type != SYMBOL_TT) - { ebf_error("property name", token_text); + { ebf_curtoken_error("property name"); return; } @@ -1242,13 +1253,12 @@ not 'private':", token_text); } else if (symbols[defined_this_segment[i]].value == symbols[token_value].value) - { char error_b[128+2*MAX_IDENTIFIER_LENGTH]; - sprintf(error_b, + { + error_fmt( "Property given twice in the same declaration, because \ -the names '%s' and '%s' actually refer to the same property", +the names \"%s\" and \"%s\" actually refer to the same property", symbols[defined_this_segment[i]].name, symbols[token_value].name); - error(error_b); } property_name_symbol = token_value; @@ -1339,12 +1349,20 @@ the names '%s' and '%s' actually refer to the same property", AO = parse_expression(ARRAY_CONTEXT); } + /* length is in bytes here, but we report the limit in words. */ + if (length == 64) { error_named("Limit (of 32 values) exceeded for property", symbols[property_name_symbol].name); break; } + if ((version_number==3) && (!individual_property) && length == 8) + { error_named("Limit (of 4 values) exceeded for property", + symbols[property_name_symbol].name); + break; + } + if (individual_property) { if (AO.marker != 0) backpatch_zmachine(AO.marker, INDIVIDUAL_PROP_ZA, @@ -1382,16 +1400,6 @@ the names '%s' and '%s' actually refer to the same property", } } - if ((version_number==3) && (!individual_property)) - { if (length > 8) - { - warning_named("Version 3 limit of 4 values per property exceeded \ -(use -v5 to get 32), so truncating property", - symbols[property_name_symbol].name); - length = 8; - } - } - if (individual_property) { ensure_memory_list_available(&individuals_table_memlist, individuals_length+length+3); @@ -1434,7 +1442,7 @@ static void properties_segment_g(int this_segment) } if (token_type != SYMBOL_TT) - { ebf_error("property name", token_text); + { ebf_curtoken_error("property name"); return; } @@ -1510,13 +1518,12 @@ not 'private':", token_text); } else if (symbols[defined_this_segment[i]].value == symbols[token_value].value) - { char error_b[128+2*MAX_IDENTIFIER_LENGTH]; - sprintf(error_b, + { + error_fmt( "Property given twice in the same declaration, because \ -the names '%s' and '%s' actually refer to the same property", +the names \"%s\" and \"%s\" actually refer to the same property", symbols[defined_this_segment[i]].name, symbols[token_value].name); - error(error_b); } property_name_symbol = token_value; @@ -1677,7 +1684,7 @@ static void attributes_segment(void) || (token_type == EOF_TT) || ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) { if (!truth_state) - ebf_error("attribute name after '~'", token_text); + ebf_curtoken_error("attribute name after '~'"); put_token_back(); return; } if ((token_type == SEP_TT) && (token_value == COMMA_SEP)) return; @@ -1688,7 +1695,7 @@ static void attributes_segment(void) if ((token_type != SYMBOL_TT) || (symbols[token_value].type != ATTRIBUTE_T)) - { ebf_error("name of an already-declared attribute", token_text); + { ebf_curtoken_error("name of an already-declared attribute"); return; } @@ -1771,7 +1778,7 @@ static void classes_segment(void) if ((token_type != SYMBOL_TT) || (symbols[token_value].type != CLASS_T)) - { ebf_error("name of an already-declared class", token_text); + { ebf_curtoken_error("name of an already-declared class"); return; } if (current_defn_is_class && token_value == current_classname_symbol) @@ -1884,14 +1891,14 @@ inconvenience, please contact the maintainers."); if (metaclass_flag) { token_text = metaclass_name; - token_value = symbol_index(token_text, -1); + token_value = symbol_index(token_text, -1, NULL); token_type = SYMBOL_TT; } else { get_next_token(); if (token_type != SYMBOL_TT) { discard_token_location(beginning_debug_location); - ebf_error("new class name", token_text); + ebf_curtoken_error("new class name"); panic_mode_error_recovery(); return; } @@ -1905,6 +1912,7 @@ inconvenience, please contact the maintainers."); /* Each class also creates a modest object representing itself: */ + ensure_memory_list_available(&shortname_buffer_memlist, strlen(token_text)+1); strcpy(shortname_buffer, token_text); assign_symbol(token_value, class_number, CLASS_T); @@ -2085,6 +2093,7 @@ extern void make_object(int nearby_flag, } } + ensure_memory_list_available(&shortname_buffer_memlist, 2); sprintf(shortname_buffer, "?"); segment_markers.enabled = TRUE; @@ -2097,8 +2106,7 @@ extern void make_object(int nearby_flag, if (token_type == DQ_TT) textual_name = token_text; else { if (token_type != SYMBOL_TT) { - ebf_error("name for new object or its textual short name", - token_text); + ebf_curtoken_error("name for new object or its textual short name"); } else if (!(symbols[token_value].flags & UNKNOWN_SFLAG)) { ebf_symbol_error("new object", token_text, typename(symbols[token_value].type), symbols[token_value].line); @@ -2126,10 +2134,9 @@ extern void make_object(int nearby_flag, { if ((token_type != SYMBOL_TT) || (symbols[token_value].flags & UNKNOWN_SFLAG)) { if (textual_name == NULL) - ebf_error("parent object or the object's textual short name", - token_text); + ebf_curtoken_error("parent object or the object's textual short name"); else - ebf_error("parent object", token_text); + ebf_curtoken_error("parent object"); } else goto SpecParent; } @@ -2140,7 +2147,7 @@ extern void make_object(int nearby_flag, if (end_of_header()) goto HeaderPassed; if (specified_parent != -1) - ebf_error("body of object definition", token_text); + ebf_curtoken_error("body of object definition"); else { SpecParent: if ((symbols[token_value].type == OBJECT_T) @@ -2148,7 +2155,7 @@ extern void make_object(int nearby_flag, { specified_parent = symbols[token_value].value; symbols[token_value].flags |= USED_SFLAG; } - else ebf_error("name of (the parent) object", token_text); + else ebf_curtoken_error("name of (the parent) object"); } /* Now it really has to be the body of the definition. */ @@ -2156,7 +2163,7 @@ extern void make_object(int nearby_flag, get_next_token_with_directives(); if (end_of_header()) goto HeaderPassed; - ebf_error("body of object definition", token_text); + ebf_curtoken_error("body of object definition"); HeaderPassed: if (specified_class == -1) put_token_back(); @@ -2165,16 +2172,30 @@ extern void make_object(int nearby_flag, assign_symbol(internal_name_symbol, no_objects + 1, OBJECT_T); if (textual_name == NULL) - { if (internal_name_symbol > 0) + { + if (internal_name_symbol > 0) { + ensure_memory_list_available(&shortname_buffer_memlist, strlen(symbols[internal_name_symbol].name)+4); sprintf(shortname_buffer, "(%s)", symbols[internal_name_symbol].name); - else + } + else { + ensure_memory_list_available(&shortname_buffer_memlist, 32); sprintf(shortname_buffer, "(%d)", no_objects+1); + } } else - { if (strlen(textual_name)>765) - error("Short name of object (in quotes) exceeded 765 characters"); - strncpy(shortname_buffer, textual_name, 765); + { + if (!glulx_mode) { + /* This check is only advisory. It's possible that a string of less than 765 characters will encode to more than 510 bytes. We'll double-check in write_property_block_z(). */ + if (strlen(textual_name)>765) + error("Short name of object (in quotes) exceeded 765 Z-characters"); + ensure_memory_list_available(&shortname_buffer_memlist, 766); + strncpy(shortname_buffer, textual_name, 765); + } + else { + ensure_memory_list_available(&shortname_buffer_memlist, strlen(textual_name)+1); + strcpy(shortname_buffer, textual_name); + } } if (specified_parent != -1) @@ -2271,7 +2292,8 @@ extern void init_objects_vars(void) properties_table = NULL; individuals_table = NULL; commonprops = NULL; - + shortname_buffer = NULL; + objectsz = NULL; objectsg = NULL; objectatts = NULL; @@ -2379,6 +2401,9 @@ extern void objects_allocate_arrays(void) initialise_memory_list(¤t_object_name, sizeof(char), 32, NULL, "object name currently being defined"); + initialise_memory_list(&shortname_buffer_memlist, + sizeof(char), 768, (void**)&shortname_buffer, + "textual name of object currently being defined"); initialise_memory_list(&embedded_function_name, sizeof(char), 32, NULL, "temporary storage for inline function name"); @@ -2409,6 +2434,7 @@ extern void objects_free_arrays(void) my_free(&commonprops, "common property info"); deallocate_memory_list(¤t_object_name); + deallocate_memory_list(&shortname_buffer_memlist); deallocate_memory_list(&embedded_function_name); deallocate_memory_list(&objectsz_memlist); deallocate_memory_list(&objectsg_memlist); diff --git a/src/states.c b/src/states.c index 56572ac..b0695e9 100644 --- a/src/states.c +++ b/src/states.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "states" : Statement translator */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -29,13 +29,13 @@ static int match_colon(void) of a 'for' loop specification: replacing ';' with ':'"); else if (token_value != COLON_SEP) - { ebf_error("':'", token_text); + { ebf_curtoken_error("':'"); panic_mode_error_recovery(); return(FALSE); } } else - { ebf_error("':'", token_text); + { ebf_curtoken_error("':'"); panic_mode_error_recovery(); return(FALSE); } @@ -46,14 +46,14 @@ static void match_open_bracket(void) { get_next_token(); if ((token_type == SEP_TT) && (token_value == OPENB_SEP)) return; put_token_back(); - ebf_error("'('", token_text); + ebf_curtoken_error("'('"); } extern void match_close_bracket(void) { get_next_token(); if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) return; put_token_back(); - ebf_error("')'", token_text); + ebf_curtoken_error("')'"); } static void parse_action(void) @@ -98,7 +98,11 @@ static void parse_action(void) codegen_action = TRUE; } else - { codegen_action = FALSE; + { + if (token_type != UQ_TT) { + ebf_curtoken_error("name of action"); + } + codegen_action = FALSE; AO2 = action_of_name(token_text); } @@ -121,7 +125,7 @@ static void parse_action(void) } if (!((token_type == SEP_TT) && (token_value == GREATER_SEP || token_value == COMMA_SEP))) { - ebf_error("',' or '>'", token_text); + ebf_curtoken_error("',' or '>'"); } if ((token_type == SEP_TT) && (token_value == COMMA_SEP)) @@ -135,7 +139,7 @@ static void parse_action(void) get_next_token(); if (!((token_type == SEP_TT) && (token_value == GREATER_SEP))) { - ebf_error("'>'", token_text); + ebf_curtoken_error("'>'"); } } @@ -143,7 +147,7 @@ static void parse_action(void) { get_next_token(); if (!((token_type == SEP_TT) && (token_value == GREATER_SEP))) { put_token_back(); - ebf_error("'>>'", token_text); + ebf_curtoken_error("'>>'"); } } @@ -272,7 +276,7 @@ extern int parse_label(void) return(symbols[token_value].value); } - ebf_error("label name", token_text); + ebf_curtoken_error("label name"); return 0; } @@ -305,7 +309,12 @@ static void parse_print_z(int finally_return) if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break; switch(token_type) { case DQ_TT: - if (strlen(token_text) > 32) + if (token_text[0] == '^' && token_text[1] == '\0') { + /* The string "^" is always a simple newline. */ + assemblez_0(new_line_zc); + break; + } + if ((int)strlen(token_text) > ZCODE_MAX_INLINE_STRING) { INITAOT(&AO, LONG_CONSTANT_OT); AO.marker = STRING_MV; AO.value = compile_string(token_text, STRCTX_GAME); @@ -441,7 +450,7 @@ static void parse_print_z(int finally_return) AO.marker = IROUTINE_MV; AO.symindex = token_value; if (symbols[token_value].type != ROUTINE_T) - ebf_error("printing routine name", token_text); + ebf_curtoken_error("printing routine name"); } symbols[token_value].flags |= USED_SFLAG; @@ -462,7 +471,7 @@ static void parse_print_z(int finally_return) QUANTITY_CONTEXT, -1), temp_var1); goto PrintTermDone; - default: ebf_error("print specification", token_text); + default: ebf_curtoken_error("print specification"); get_next_token(); assemblez_1(print_num_zc, code_generate(parse_expression(QUANTITY_CONTEXT), @@ -492,13 +501,13 @@ static void parse_print_z(int finally_return) get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break; if ((token_type != SEP_TT) || (token_value != COMMA_SEP)) - { ebf_error("comma", token_text); + { ebf_curtoken_error("comma"); panic_mode_error_recovery(); return; } else get_next_token(); } while(TRUE); - if (count == 0) ebf_error("something to print", token_text); + if (count == 0) ebf_curtoken_error("something to print"); if (finally_return) { assemblez_0(new_line_zc); assemblez_0(rtrue_zc); @@ -535,6 +544,12 @@ static void parse_print_g(int finally_return) if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break; switch(token_type) { case DQ_TT: + if (token_text[0] == '^' && token_text[1] == '\0') { + /* The string "^" is always a simple newline. */ + INITAOTV(&AO, BYTECONSTANT_OT, 0x0A); + assembleg_1(streamchar_gc, AO); + break; + } /* We can't compile a string into the instruction, so this always goes into the string area. */ { INITAOT(&AO, CONSTANT_OT); @@ -564,7 +579,6 @@ static void parse_print_g(int finally_return) get_next_token(); if ((token_type == SEP_TT) && (token_value == CLOSEB_SEP)) { assembly_operand AO1; - int ln, ln2; put_token_back(); put_token_back(); local_variables.enabled = FALSE; @@ -591,19 +605,15 @@ static void parse_print_g(int finally_return) AO1 = code_generate( parse_expression(QUANTITY_CONTEXT), QUANTITY_CONTEXT, -1); - if ((AO1.type == LOCALVAR_OT) && (AO1.value == 0)) - { assembleg_2(stkpeek_gc, zero_operand, - stack_pointer); + if (is_constant_ot(AO1.type) && AO1.marker == 0) { + if (AO1.value >= 0 && AO1.value < 0x100) + assembleg_1(streamchar_gc, AO1); + else + assembleg_1(streamunichar_gc, AO1); + } + else { + assembleg_1(streamunichar_gc, AO1); } - INITAOTV(&AO2, HALFCONSTANT_OT, 0x100); - assembleg_2_branch(jgeu_gc, AO1, AO2, - ln = next_label++); - ln2 = next_label++; - assembleg_1(streamchar_gc, AO1); - assembleg_jump(ln2); - assemble_label_no(ln); - assembleg_1(streamunichar_gc, AO1); - assemble_label_no(ln2); goto PrintTermDone; case ADDRESS_MK: if (runtime_error_checking_switch) @@ -678,7 +688,7 @@ static void parse_print_g(int finally_return) AO.marker = IROUTINE_MV; AO.symindex = token_value; if (symbols[token_value].type != ROUTINE_T) - ebf_error("printing routine name", token_text); + ebf_curtoken_error("printing routine name"); } symbols[token_value].flags |= USED_SFLAG; @@ -692,7 +702,7 @@ static void parse_print_g(int finally_return) AO2); goto PrintTermDone; - default: ebf_error("print specification", token_text); + default: ebf_curtoken_error("print specification"); get_next_token(); assembleg_1(streamnum_gc, code_generate(parse_expression(QUANTITY_CONTEXT), @@ -722,13 +732,13 @@ static void parse_print_g(int finally_return) get_next_token(); if ((token_type == SEP_TT) && (token_value == SEMICOLON_SEP)) break; if ((token_type != SEP_TT) || (token_value != COMMA_SEP)) - { ebf_error("comma", token_text); + { ebf_curtoken_error("comma"); panic_mode_error_recovery(); return; } else get_next_token(); } while(TRUE); - if (count == 0) ebf_error("something to print", token_text); + if (count == 0) ebf_curtoken_error("something to print"); if (finally_return) { INITAOTV(&AO, BYTECONSTANT_OT, 0x0A); @@ -748,7 +758,7 @@ static int parse_named_label_statements() get_next_token(); if (token_type != SYMBOL_TT) { - ebf_error("label name", token_text); + ebf_curtoken_error("label name"); return TRUE; } @@ -761,7 +771,7 @@ static int parse_named_label_statements() } else { if (symbols[token_value].type != LABEL_T) { - ebf_error("label name", token_text); + ebf_curtoken_error("label name"); return TRUE; } if (symbols[token_value].flags & CHANGE_SFLAG) @@ -774,7 +784,7 @@ static int parse_named_label_statements() get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); + { ebf_curtoken_error("';'"); put_token_back(); return FALSE; } @@ -824,8 +834,10 @@ static void parse_statement_z(int break_label, int continue_label) { parse_action(); goto StatementTerminator; } if (token_type == EOF_TT) - { ebf_error("statement", token_text); return; } + { ebf_curtoken_error("statement"); return; } + /* If we don't see a keyword, this must be a function call or + other expression-with-side-effects. */ if (token_type != STATEMENT_TT) { put_token_back(); AO = parse_expression(VOID_CONTEXT); @@ -854,8 +866,7 @@ static void parse_statement_z(int break_label, int continue_label) if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP)) break; if (token_type != DQ_TT) - ebf_error("text of box line in double-quotes", - token_text); + ebf_curtoken_error("text of box line in double-quotes"); { int i, j; for (i=0, j=0; token_text[i] != 0; j++) if (token_text[i] == '@') @@ -943,7 +954,7 @@ static void parse_statement_z(int break_label, int continue_label) if ((token_type != MISC_KEYWORD_TT) || ((token_value != ON_MK) && (token_value != OFF_MK))) - { ebf_error("'on' or 'off'", token_text); + { ebf_curtoken_error("'on' or 'off'"); panic_mode_error_recovery(); break; } @@ -1207,7 +1218,7 @@ static void parse_statement_z(int break_label, int continue_label) { get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); + { ebf_curtoken_error("';'"); put_token_back(); } } @@ -1318,7 +1329,7 @@ static void parse_statement_z(int break_label, int continue_label) misc_keywords.enabled = FALSE; if ((token_type != MISC_KEYWORD_TT) || (token_value != TO_MK)) - { ebf_error("'to'", token_text); + { ebf_curtoken_error("'to'"); panic_mode_error_recovery(); return; } @@ -1363,7 +1374,7 @@ static void parse_statement_z(int break_label, int continue_label) (symbols[token_value].type == GLOBAL_VARIABLE_T)) AO.value = symbols[token_value].value; else - { ebf_error("'objectloop' variable", token_text); + { ebf_curtoken_error("'objectloop' variable"); panic_mode_error_recovery(); break; } misc_keywords.enabled = TRUE; @@ -1695,9 +1706,8 @@ static void parse_statement_z(int break_label, int continue_label) && (token_value != BOLD_MK) && (token_value != UNDERLINE_MK) && (token_value != FIXED_MK))) - { ebf_error( -"'roman', 'bold', 'underline', 'reverse' or 'fixed'", - token_text); + { ebf_curtoken_error( +"'roman', 'bold', 'underline', 'reverse' or 'fixed'"); panic_mode_error_recovery(); break; } @@ -1762,7 +1772,7 @@ static void parse_statement_z(int break_label, int continue_label) get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); + { ebf_curtoken_error("';'"); put_token_back(); } } @@ -1794,8 +1804,10 @@ static void parse_statement_g(int break_label, int continue_label) { parse_action(); goto StatementTerminator; } if (token_type == EOF_TT) - { ebf_error("statement", token_text); return; } + { ebf_curtoken_error("statement"); return; } + /* If we don't see a keyword, this must be a function call or + other expression-with-side-effects. */ if (token_type != STATEMENT_TT) { put_token_back(); AO = parse_expression(VOID_CONTEXT); @@ -1823,8 +1835,7 @@ static void parse_statement_g(int break_label, int continue_label) if ((token_type==SEP_TT)&&(token_value==SEMICOLON_SEP)) break; if (token_type != DQ_TT) - ebf_error("text of box line in double-quotes", - token_text); + ebf_curtoken_error("text of box line in double-quotes"); { int i, j; for (i=0, j=0; token_text[i] != 0; j++) if (token_text[i] == '@') @@ -1910,7 +1921,7 @@ static void parse_statement_g(int break_label, int continue_label) if ((token_type != MISC_KEYWORD_TT) || ((token_value != ON_MK) && (token_value != OFF_MK))) - { ebf_error("'on' or 'off'", token_text); + { ebf_curtoken_error("'on' or 'off'"); panic_mode_error_recovery(); break; } @@ -2195,7 +2206,7 @@ static void parse_statement_g(int break_label, int continue_label) { get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); + { ebf_curtoken_error("';'"); put_token_back(); } } @@ -2332,7 +2343,7 @@ static void parse_statement_g(int break_label, int continue_label) misc_keywords.enabled = FALSE; if ((token_type != MISC_KEYWORD_TT) || (token_value != TO_MK)) - { ebf_error("'to'", token_text); + { ebf_curtoken_error("'to'"); panic_mode_error_recovery(); return; } @@ -2375,7 +2386,7 @@ static void parse_statement_g(int break_label, int continue_label) INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value); } else { - ebf_error("'objectloop' variable", token_text); + ebf_curtoken_error("'objectloop' variable"); panic_mode_error_recovery(); break; } @@ -2472,10 +2483,16 @@ 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 = symbols[ln].value; - AO2.marker = OBJECT_MV; + ln = get_symbol_index("Class"); + if (ln < 0) { + error("No 'Class' object found"); + AO2 = zero_operand; + } + else { + INITAOT(&AO2, CONSTANT_OT); + AO2.value = symbols[ln].value; + AO2.marker = OBJECT_MV; + } assembleg_store(AO, AO2); assemble_label_no(ln = next_label++); @@ -2631,9 +2648,8 @@ static void parse_statement_g(int break_label, int continue_label) && (token_value != BOLD_MK) && (token_value != UNDERLINE_MK) && (token_value != FIXED_MK))) - { ebf_error( -"'roman', 'bold', 'underline', 'reverse' or 'fixed'", - token_text); + { ebf_curtoken_error( +"'roman', 'bold', 'underline', 'reverse' or 'fixed'"); panic_mode_error_recovery(); break; } @@ -2727,7 +2743,7 @@ static void parse_statement_g(int break_label, int continue_label) get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';'", token_text); + { ebf_curtoken_error("';'"); put_token_back(); } } @@ -2756,6 +2772,48 @@ extern void parse_statement(int break_label, int continue_label) execution_never_reaches_here &= ~EXECSTATE_ENTIRE; } +/* This does the same work as parse_statement(), but it's called if you've + already parsed an expression (in void context) and you want to generate + it as a statement. Essentially it's a copy of parse_statement() and + parse_statement_z/g(), except we skip straight to the "expression-with- + side-effects" bit and omit everything else. + + The caller doesn't need to pass break_label/continue_label; they're + not used for this code path. +*/ +extern void parse_statement_singleexpr(assembly_operand AO) +{ + 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; + + code_generate(AO, VOID_CONTEXT, -1); + + if (vivc_flag) { + panic_mode_error_recovery(); + } + else { + /* StatementTerminator... */ + get_next_token(); + if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) + { ebf_curtoken_error("';'"); + put_token_back(); + } + } + + if (saved_entire_flag) + execution_never_reaches_here |= EXECSTATE_ENTIRE; + else + execution_never_reaches_here &= ~EXECSTATE_ENTIRE; +} + /* ========================================================================= */ /* Data structure management routines */ /* ------------------------------------------------------------------------- */ diff --git a/src/symbols.c b/src/symbols.c index e8ac5b6..dc4eb53 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.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -69,10 +69,13 @@ 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; +static char *temp_symbol_buf; /* used in write_the_identifier_names() */ +static memory_list temp_symbol_buf_memlist; /* ------------------------------------------------------------------------- */ /* Memory to hold the text of symbol names: note that this memory is */ -/* allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE. */ +/* allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE. (Or */ +/* larger, if needed for a particularly enormous symbol.) */ /* ------------------------------------------------------------------------- */ #define SYMBOLS_CHUNK_SIZE (4096) @@ -237,10 +240,16 @@ extern int get_symbol_index(char *p) return -1; } -extern int symbol_index(char *p, int hashcode) +extern int symbol_index(char *p, int hashcode, int *created) { /* Return the index in the symbols array of symbol "p", creating a - new symbol with that name if it isn't already there. + new symbol with that name if it isn't already there. This + always returns a valid symbol index. + + The optional created argument receives TRUE if the symbol + was newly created. + + Pass in the hashcode of p if you know it, or -1 if you don't. New symbols are created with flag UNKNOWN_SFLAG, value 0x100 (a 2-byte quantity in Z-machine terms) and type CONSTANT_T. @@ -264,6 +273,7 @@ extern int symbol_index(char *p, int hashcode) { if (track_unused_routines) df_note_function_symbol(this); + if (created) *created = FALSE; return this; } if (new_entry > 0) break; @@ -273,7 +283,7 @@ extern int symbol_index(char *p, int hashcode) } while (this != -1); if (symdef_trace_setting) - printf("Encountered symbol %d '%s'\n", no_symbols, p); + printf("%s: Encountered symbol %d '%s'\n", current_location_text(), no_symbols, p); ensure_memory_list_available(&symbols_memlist, no_symbols+1); if (debugfile_switch) @@ -289,18 +299,19 @@ extern int symbol_index(char *p, int hashcode) } 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 (!symbols_free_space || symbols_free_space+len+1 >= symbols_ceiling) + { + /* Allocate a new chunk whose size is big enough for the current + symbol, or SYMBOLS_CHUNK_SIZE, whichever is greater. */ + int chunklen = SYMBOLS_CHUNK_SIZE; + if (chunklen < len+1) + chunklen = len+1; + symbols_free_space + = my_malloc(chunklen, "symbol names chunk"); + symbols_ceiling = symbols_free_space + chunklen; ensure_memory_list_available(&symbol_name_space_chunks_memlist, no_symbol_name_space_chunks+1); symbol_name_space_chunks[no_symbol_name_space_chunks++] = 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(symbols_free_space, p); @@ -322,17 +333,29 @@ extern int symbol_index(char *p, int hashcode) if (track_unused_routines) df_note_function_symbol(no_symbols); + if (created) *created = TRUE; return(no_symbols++); } -extern void end_symbol_scope(int k) +extern void end_symbol_scope(int k, int neveruse) { /* Remove the given symbol from the hash table, making it - invisible to symbol_index. This is used by the Undef directive. - If the symbol is not found, this silently does nothing. + invisible to symbol_index. This is used by the Undef directive + and put_token_back(). + + If you know the symbol has never been used, set neveruse and + it will be flagged as an error if it *is* used. + + If the symbol is not found in the hash table, this silently does + nothing. */ int j; + + symbols[k].flags |= UNHASHED_SFLAG; + if (neveruse) + symbols[k].flags |= DISCARDED_SFLAG; + j = hash_code_from_string(symbols[k].name); if (start_of_list[j] == k) { start_of_list[j] = symbols[k].next_entry; @@ -388,8 +411,8 @@ static void describe_flags(int flags) if (flags & USED_SFLAG) printf("(used) "); if (flags & DEFCON_SFLAG) printf("(Defaulted) "); if (flags & STUB_SFLAG) printf("(Stubbed) "); - if (flags & IMPORT_SFLAG) printf("(Imported) "); - if (flags & EXPORT_SFLAG) printf("(Exported) "); + if (flags & UNHASHED_SFLAG) printf("(not in hash chain) "); + if (flags & DISCARDED_SFLAG) printf("(removed, do not use) "); if (flags & ALIASED_SFLAG) printf("(aliased) "); if (flags & CHANGE_SFLAG) printf("(value will change) "); if (flags & SYSTEM_SFLAG) printf("(System) "); @@ -527,15 +550,22 @@ extern void issue_unused_warnings(void) } /* Now back to mark anything necessary as used */ - i = symbol_index("Main", -1); - if (!(symbols[i].flags & UNKNOWN_SFLAG)) symbols[i].flags |= USED_SFLAG; + i = get_symbol_index("Main"); + if (i >= 0 && !(symbols[i].flags & UNKNOWN_SFLAG)) { + symbols[i].flags |= USED_SFLAG; + } for (i=0;i"); debug_file_printf - ("##%s", idname_string); + ("##%s", temp_symbol_buf); debug_file_printf("%d", symbols[i].value); debug_file_printf(""); } action_name_strings[symbols[i].value] - = compile_string(idname_string, STRCTX_SYMBOL); + = compile_string(temp_symbol_buf, STRCTX_SYMBOL); } } for (i=0; i= 0 && 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; } - ix = symbol_index("Main", -1); - if (symbols[ix].type == ROUTINE_T) { + ix = get_symbol_index("Main"); + if (ix >= 0 && symbols[ix].type == ROUTINE_T) { uint32 addr = symbols[ix].value * (glulx_mode ? 1 : scale_factor); tofunc = df_function_for_address(addr); if (tofunc) @@ -1537,11 +1589,12 @@ extern void init_symbols_vars(void) symbols = NULL; start_of_list = NULL; symbol_debug_info = NULL; + temp_symbol_buf = NULL; symbol_name_space_chunks = NULL; no_symbol_name_space_chunks = 0; - symbols_free_space=NULL; - symbols_ceiling=NULL; + symbols_free_space = NULL; + symbols_ceiling = NULL; no_symbols = 0; @@ -1581,6 +1634,11 @@ extern void symbols_allocate_arrays(void) sizeof(symboldebuginfo), 6400, (void**)&symbol_debug_info, "symbol debug backpatch info"); } + + initialise_memory_list(&temp_symbol_buf_memlist, + sizeof(char), 64, (void**)&temp_symbol_buf, + "temporary symbol name"); + start_of_list = my_calloc(sizeof(int32), HASH_TAB_SIZE, "hash code list beginnings"); @@ -1634,6 +1692,8 @@ extern void symbols_free_arrays(void) { deallocate_memory_list(&symbol_debug_info_memlist); } + deallocate_memory_list(&temp_symbol_buf_memlist); + my_free(&start_of_list, "hash code list beginnings"); if (symbol_replacements) diff --git a/src/syntax.c b/src/syntax.c index ad7e121..cae1bc4 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "syntax" : Syntax analyser and compiler */ /* */ -/* Part of Inform 6.41 */ -/* copyright (c) Graham Nelson 1993 - 2022 */ +/* Part of Inform 6.42 */ +/* copyright (c) Graham Nelson 1993 - 2024 */ /* */ /* Inform is free software: you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -86,10 +86,12 @@ extern void get_next_token_with_directives(void) Object, where we want to support internal #ifdefs. (Although function-parsing predates this and doesn't make use of it.) */ - int directives_save, segment_markers_save, statements_save; - while (TRUE) { + int directives_save, segment_markers_save, statements_save, + conditions_save, local_variables_save, misc_keywords_save, + system_functions_save; + get_next_token(); /* If the first token is not a '#', return it directly. */ @@ -100,6 +102,10 @@ extern void get_next_token_with_directives(void) directives_save = directives.enabled; segment_markers_save = segment_markers.enabled; statements_save = statements.enabled; + conditions_save = conditions.enabled; + local_variables_save = local_variables.enabled; + misc_keywords_save = misc_keywords.enabled; + system_functions_save = system_functions.enabled; directives.enabled = TRUE; segment_markers.enabled = FALSE; @@ -119,22 +125,19 @@ extern void get_next_token_with_directives(void) if (token_type == DIRECTIVE_TT) parse_given_directive(TRUE); else - { ebf_error("directive", token_text); + { ebf_curtoken_error("directive"); return; } - /* Restore all the lexer flags. (We are squashing several of them - into a single save variable, which I think is safe because that's - what CKnight did.) - */ + /* Restore all the lexer flags. */ directive_keywords.enabled = FALSE; directives.enabled = directives_save; segment_markers.enabled = segment_markers_save; - statements.enabled = - conditions.enabled = - local_variables.enabled = - misc_keywords.enabled = - system_functions.enabled = statements_save; + statements.enabled = statements_save; + conditions.enabled = conditions_save; + local_variables.enabled = local_variables_save; + misc_keywords.enabled = misc_keywords_save; + system_functions.enabled = system_functions_save; } } @@ -186,7 +189,7 @@ extern int parse_directive(int internal_flag) get_next_token(); df_dont_note_global_symbols = FALSE; if (token_type != SYMBOL_TT) - { ebf_error("routine name", token_text); + { ebf_curtoken_error("routine name"); return(FALSE); } if ((!(symbols[token_value].flags & UNKNOWN_SFLAG)) @@ -236,7 +239,7 @@ extern int parse_directive(int internal_flag) get_next_token(); if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP)) - { ebf_error("';' after ']'", token_text); + { ebf_curtoken_error("';' after ']'"); put_token_back(); } return TRUE; @@ -256,9 +259,9 @@ extern int parse_directive(int internal_flag) { /* If we're internal, we expect only a directive here. If we're top-level, the possibilities are broader. */ if (internal_flag) - ebf_error("directive", token_text); + ebf_curtoken_error("directive"); else - ebf_error("directive, '[' or class name", token_text); + ebf_curtoken_error("directive, '[' or class name"); panic_mode_error_recovery(); return TRUE; } @@ -266,7 +269,9 @@ extern int parse_directive(int internal_flag) return !(parse_given_directive(internal_flag)); } -/* Check what's coming up after a switch case value. */ +/* Check what's coming up after a switch case value. + (This is "switch sign" in the sense of "worm sign", not like a signed + variable.) */ static int switch_sign(void) { if ((token_type == SEP_TT)&&(token_value == COLON_SEP)) return 1; @@ -323,17 +328,18 @@ static void compile_alternatives(assembly_operand switch_value, int n, compile_alternatives_g(switch_value, n, stack_level, label, flag); } +static void generate_switch_spec(assembly_operand switch_value, int label, int label_after, int speccount); + static void parse_switch_spec(assembly_operand switch_value, int label, int action_switch) { - int i, j, label_after = -1, spec_sp = 0; - int max_equality_args = ((!glulx_mode) ? 3 : 1); + int label_after = -1, spec_sp = 0; sequence_point_follows = FALSE; do { if (spec_sp >= MAX_SPEC_STACK) - { error("At most 32 values can be given in a single 'switch' case"); + { error_fmt("At most %d values can be given in a single 'switch' case", MAX_SPEC_STACK); panic_mode_error_recovery(); return; } @@ -341,19 +347,20 @@ static void parse_switch_spec(assembly_operand switch_value, int label, if (action_switch) { get_next_token(); if (token_type == SQ_TT || token_type == DQ_TT) { - ebf_error("action (or fake action) name", token_text); + ebf_curtoken_error("action (or fake action) name"); continue; } spec_stack[spec_sp] = action_of_name(token_text); if (spec_stack[spec_sp].value == -1) { spec_stack[spec_sp].value = 0; - ebf_error("action (or fake action) name", token_text); + ebf_curtoken_error("action (or fake action) name"); } } - else + else { spec_stack[spec_sp] = code_generate(parse_expression(CONSTANT_CONTEXT), CONSTANT_CONTEXT, -1); + } misc_keywords.enabled = TRUE; get_next_token(); @@ -363,75 +370,86 @@ static void parse_switch_spec(assembly_operand switch_value, int label, switch(spec_type[spec_sp-1]) { case 0: if (action_switch) - ebf_error("',' or ':'", token_text); - else ebf_error("',', ':' or 'to'", token_text); + ebf_curtoken_error("',' or ':'"); + else ebf_curtoken_error("',', ':' or 'to'"); panic_mode_error_recovery(); return; case 1: goto GenSpecCode; case 3: if (label_after == -1) label_after = next_label++; } - } while(TRUE); - - GenSpecCode: - - if ((spec_sp > max_equality_args) && (label_after == -1)) - label_after = next_label++; - - if (label_after == -1) - { compile_alternatives(switch_value, spec_sp, 0, label, FALSE); return; - } - - for (i=0; i i) - { if (j-i > max_equality_args) j=i+max_equality_args; - - if (j == spec_sp) - compile_alternatives(switch_value, j-i, i, label, FALSE); - else - compile_alternatives(switch_value, j-i, i, label_after, TRUE); - - i=j; - } - else - { - if (!glulx_mode) { - if (i == spec_sp - 2) - { assemblez_2_branch(jl_zc, switch_value, spec_stack[i], - label, TRUE); - assemblez_2_branch(jg_zc, switch_value, spec_stack[i+1], - label, TRUE); - } - else - { assemblez_2_branch(jl_zc, switch_value, spec_stack[i], - next_label, TRUE); - assemblez_2_branch(jg_zc, switch_value, spec_stack[i+1], - label_after, FALSE); - assemble_label_no(next_label++); - } - } - else { - if (i == spec_sp - 2) - { assembleg_2_branch(jlt_gc, switch_value, spec_stack[i], - label); - assembleg_2_branch(jgt_gc, switch_value, spec_stack[i+1], - label); - } - else - { assembleg_2_branch(jlt_gc, switch_value, spec_stack[i], - next_label); - assembleg_2_branch(jle_gc, switch_value, spec_stack[i+1], - label_after); - assemble_label_no(next_label++); - } - } - i = i+2; - } - } - - assemble_label_no(label_after); + } while(TRUE); + + GenSpecCode: + generate_switch_spec(switch_value, label, label_after, spec_sp); +} + +/* Generate code for a switch case. The case values are in spec_stack[] + and spec_type[]. */ +static void generate_switch_spec(assembly_operand switch_value, int label, int label_after, int speccount) +{ + int i, j; + int max_equality_args = ((!glulx_mode) ? 3 : 1); + + sequence_point_follows = FALSE; + + if ((speccount > max_equality_args) && (label_after == -1)) + label_after = next_label++; + + if (label_after == -1) + { compile_alternatives(switch_value, speccount, 0, label, FALSE); return; + } + + for (i=0; i i) + { if (j-i > max_equality_args) j=i+max_equality_args; + + if (j == speccount) + compile_alternatives(switch_value, j-i, i, label, FALSE); + else + compile_alternatives(switch_value, j-i, i, label_after, TRUE); + + i=j; + } + else + { + if (!glulx_mode) { + if (i == speccount - 2) + { assemblez_2_branch(jl_zc, switch_value, spec_stack[i], + label, TRUE); + assemblez_2_branch(jg_zc, switch_value, spec_stack[i+1], + label, TRUE); + } + else + { assemblez_2_branch(jl_zc, switch_value, spec_stack[i], + next_label, TRUE); + assemblez_2_branch(jg_zc, switch_value, spec_stack[i+1], + label_after, FALSE); + assemble_label_no(next_label++); + } + } + else { + if (i == speccount - 2) + { assembleg_2_branch(jlt_gc, switch_value, spec_stack[i], + label); + assembleg_2_branch(jgt_gc, switch_value, spec_stack[i+1], + label); + } + else + { assembleg_2_branch(jlt_gc, switch_value, spec_stack[i], + next_label); + assembleg_2_branch(jle_gc, switch_value, spec_stack[i+1], + label_after); + assemble_label_no(next_label++); + } + } + i = i+2; + } + } + + assemble_label_no(label_after); } extern int32 parse_routine(char *source, int embedded_flag, char *name, @@ -450,10 +468,7 @@ extern int32 parse_routine(char *source, int embedded_flag, char *name, restart_lexer(lexical_source, name); } - no_locals = 0; - - for (i=0;i MAX_IDENTIFIER_LENGTH) - { error_named("Local variable identifier too long:", token_text); + ebf_curtoken_error("local variable name or ';'"); panic_mode_error_recovery(); break; } if (no_locals == MAX_LOCAL_VARIABLES-1) - { error_numbered("Too many local variables for a routine; max is", + { error_fmt("Too many local variables for a routine; max is %d", MAX_LOCAL_VARIABLES-1); panic_mode_error_recovery(); break; } for (i=0;i"); + panic_mode_error_recovery(); + continue; + } + + if (constcount > MAX_SPEC_STACK) + { error_fmt("At most %d values can be given in a single 'switch' case", MAX_SPEC_STACK); + panic_mode_error_recovery(); + continue; + } + + get_next_token(); + /* Gotta fill in the spec_type values for the + spec_stacks. */ + for (ix=0; ix rough_size) + compiler_error("Paged size exceeds rough estimate."); + Write_Code_At = mark; if (!OMIT_UNUSED_ROUTINES) { code_length = zmachine_pc; @@ -697,11 +731,10 @@ or less."); } if (excess > 0) - { char memory_full_error[80]; - sprintf(memory_full_error, + { + fatalerror_fmt( "The %s exceeds version-%d limit (%dK) by %d bytes", output_called, version_number, limit, excess); - fatalerror(memory_full_error); } /* --------------------------- Offsets -------------------------------- */ @@ -735,26 +768,24 @@ or less."); */ excess = code_length + code_offset - (scale_factor*((int32) 0x10000L)); if (excess > 0) - { char code_full_error[80]; - sprintf(code_full_error, + { + fatalerror_fmt( "The code area limit has been exceeded by %d bytes", excess); - fatalerror(code_full_error); } excess = strings_length + strings_offset - (scale_factor*((int32) 0x10000L)); if (excess > 0) - { char strings_full_error[140]; + { if (oddeven_packing_switch) - sprintf(strings_full_error, + fatalerror_fmt( "The strings area limit has been exceeded by %d bytes", excess); else - sprintf(strings_full_error, + fatalerror_fmt( "The code+strings area limit has been exceeded by %d bytes. \ Try running Inform again with -B on the command line.", excess); - fatalerror(strings_full_error); } } else @@ -848,12 +879,15 @@ or less."); if (!skip_backpatching) { backpatch_zmachine_image_z(); - for (i=1; i rough_size) + compiler_error("RAM size exceeds rough estimate."); + Out_Size = Write_RAM_At + RAM_Size; /* --------------------------- Offsets -------------------------------- */ @@ -1590,18 +1642,23 @@ static void display_frequencies() for (i=0; i=0; j--) - { p1=(char *)abbreviations_at+j*MAX_ABBREV_LENGTH; + { p1=abbreviation_text(j); abbrevs_lookup[(uchar)p1[0]]=j; abbreviations[j].freq=0; } @@ -206,9 +210,13 @@ static void make_abbrevs_lookup(void) static int try_abbreviations_from(unsigned char *text, int i, int from) { int j, k; uchar *p, c; c=text[i]; - for (j=from, p=(uchar *)abbreviations_at+from*MAX_ABBREV_LENGTH; - (j= no_abbreviations) { + compiler_error("Invalid abbrev for abbreviation_text()"); + return ""; + } + + return abbreviations_text + abbreviations[num].textpos; +} + /* ------------------------------------------------------------------------- */ /* The front end routine for text translation. */ /* strctx indicates the purpose of the string. This is mostly used for */ @@ -256,6 +286,18 @@ extern void make_abbreviation(char *text) /* specially during compilation. */ /* ------------------------------------------------------------------------- */ +/* TODO: When called from a print statement (parse_print()), it would be + nice to detect if the generated string is exactly one character. In that + case, we could return the character value and a flag to indicate the + caller could use @print_char/@streamchar/@new_line/@streamunichar + instead of printing a compiled string. + + We'd need a new STRCTX value or two to distinguish direct-printed strings + from referenceable strings. + + Currently, parse_print() checks for the "^" case manually, which is a + bit icky. */ + extern int32 compile_string(char *b, int strctx) { int32 i, j, k; uchar *c; @@ -425,7 +467,9 @@ static void write_z_char_g(int i) /* 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]; + int lookup; + if (c == ' ') return 1; + lookup = iso_to_alphabet_grid[c]; if (lookup < 0) return 4; if (lookup < 26) return 1; return 2; @@ -543,9 +587,12 @@ extern int32 translate_text(int32 p_limit, char *s_text, int strctx) { 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; - (ku.val*MAX_ABBREV_LENGTH; + cx = abbreviation_text(ent->u.val); compression_table_size += (1 + 1 + strlen(cx)); break; case 4: @@ -1423,12 +1472,27 @@ typedef struct optab_s int32 popularity; int32 score; int32 location; - char text[MAX_ABBREV_LENGTH]; + char *text; /* allocated to textsize, min 4 */ + int32 textsize; } optab; 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 void optab_copy(optab *dest, const optab *src) +{ + dest->length = src->length; + dest->popularity = src->popularity; + dest->score = src->score; + dest->location = src->location; + if (src->length+1 > dest->textsize) { + int32 oldsize = dest->textsize; + dest->textsize = (src->length+1)*2; + my_realloc(&dest->text, oldsize, dest->textsize, "bestyet2.text"); + } + strcpy(dest->text, src->text); +} + static int pass_no; static void optimise_pass(void) @@ -1459,7 +1523,7 @@ static void optimise_pass(void) for (j=0; j=2)&&(nl=2) { nl++; for (j2=0; j2 bestyet[i].textsize) { + int32 oldsize = bestyet[i].textsize; + bestyet[i].textsize = (nl+1)*2; + my_realloc(&bestyet[i].text, oldsize, bestyet[i].textsize, "bestyet.text"); + } for (j2=0; j20) { char testtext[4]; - bestyet2[selected++]=bestyet[maxat]; + optab_copy(&bestyet2[selected++], &bestyet[maxat]); if (optabbrevs_trace_setting >= 1) { printf( @@ -1813,14 +1899,11 @@ int dict_entries; /* Total number of records entered */ /* 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.) */ +/* as before. In Glulx, it can be any value. */ /* */ -/* ...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).*/ +/* In further modifying the compiler to generate a Unicode dictionary, */ +/* I have to store four-byte values in the uchar array. We make the array */ +/* size DICT_WORD_BYTES (which is DICT_WORD_SIZE*DICT_CHAR_SIZE). */ /* Then we store the 32-bit character value big-endian. This lets us */ /* continue to compare arrays bytewise, which is a nice simplification. */ /* ------------------------------------------------------------------------- */ @@ -1840,14 +1923,17 @@ extern void copy_sorts(uchar *d1, uchar *d2) d1[i] = d2[i]; } -static uchar prepared_sort[MAX_DICT_WORD_BYTES]; /* Holds the sort code - of current word */ +static memory_list prepared_sort_memlist; +static uchar *prepared_sort; /* Holds the sort code of current word */ -static int number_and_case; +static int prepared_dictflags_pos; /* Dict flags set by the current word */ +static int prepared_dictflags_neg; /* Dict flags *not* set by the word */ /* Also used by verbs.c */ static void dictionary_prepare_z(char *dword, uchar *optresult) -{ int i, j, k, k2, wd[13]; int32 tot; +{ int i, j, k, k2, wd[13]; + int32 tot; + int negflag; /* A rapid text translation algorithm using only the simplified rules applying to the text of dictionary entries: first produce a sequence @@ -1855,22 +1941,50 @@ static void dictionary_prepare_z(char *dword, uchar *optresult) int dictsize = (version_number==3) ? 6 : 9; - number_and_case = 0; + prepared_dictflags_pos = 0; + prepared_dictflags_neg = 0; - for (i=0, j=0; dword[j]!=0; i++, j++) - { if ((dword[j] == '/') && (dword[j+1] == '/')) - { for (j+=2; dword[j] != 0; j++) - { switch(dword[j]) - { case 'p': number_and_case |= 4; break; + for (i=0, j=0; dword[j]!=0; j++) + { + if ((dword[j] == '/') && (dword[j+1] == '/')) + { + /* The rest of the word is dict flags. Run through them. */ + negflag = FALSE; + for (j+=2; dword[j] != 0; j++) + { + switch(dword[j]) + { + case '~': + if (!dword[j+1]) + error_named("'//~' with no flag character (pn) in dict word", dword); + negflag = !negflag; + break; + case 'p': + if (!negflag) + prepared_dictflags_pos |= 4; + else + prepared_dictflags_neg |= 4; + negflag = FALSE; + break; + case 'n': + if (!negflag) + prepared_dictflags_pos |= 128; + else + prepared_dictflags_neg |= 128; + negflag = FALSE; + break; default: - error_named("Expected 'p' after '//' \ -to give number of dictionary word", dword); + error_named("Expected flag character (pn~) after '//' in dict word", dword); break; } } break; } - if (i>=dictsize) break; + + /* LONG_DICT_FLAG_BUG emulates the old behavior where we stop looping + at dictsize. */ + if (LONG_DICT_FLAG_BUG && i>=dictsize) + break; k=(int) dword[j]; if (k==(int) '\'') @@ -1901,26 +2015,37 @@ apostrophe in", dword); char_error("Character can be printed but not input:", k); else { /* Use 4 more Z-chars to encode a ZSCII escape sequence */ - - wd[i++] = 5; wd[i++] = 6; + if (i dictsize) + compiler_error("dict word buffer overflow"); + + /* Fill up to the end of the dictionary block with PAD characters + (for safety, we right-pad to 9 chars even in V3) */ for (; i<9; i++) wd[i]=5; /* The array of Z-chars is converted to two or three 2-byte blocks */ - + ensure_memory_list_available(&prepared_sort_memlist, DICT_WORD_BYTES); + tot = wd[2] + wd[1]*(1<<5) + wd[0]*(1<<10); prepared_sort[1]=tot%0x100; prepared_sort[0]=(tot/0x100)%0x100; @@ -1947,25 +2072,48 @@ static void dictionary_prepare_g(char *dword, uchar *optresult) { int i, j, k; int32 unicode; + int negflag; - number_and_case = 0; + prepared_dictflags_pos = 0; + prepared_dictflags_neg = 0; - for (i=0, j=0; (dword[j]!=0); i++, j++) { + for (i=0, j=0; (dword[j]!=0); j++) { if ((dword[j] == '/') && (dword[j+1] == '/')) { + /* The rest of the word is dict flags. Run through them. */ + negflag = FALSE; for (j+=2; dword[j] != 0; j++) { switch(dword[j]) { + case '~': + if (!dword[j+1]) + error_named("'//~' with no flag character (pn) in dict word", dword); + negflag = !negflag; + break; case 'p': - number_and_case |= 4; - break; + if (!negflag) + prepared_dictflags_pos |= 4; + else + prepared_dictflags_neg |= 4; + negflag = FALSE; + break; + case 'n': + if (!negflag) + prepared_dictflags_pos |= 128; + else + prepared_dictflags_neg |= 128; + negflag = FALSE; + break; default: - error_named("Expected 'p' after '//' \ -to give gender or number of dictionary word", dword); + error_named("Expected flag character (pn~) after '//' in dict word", dword); break; } } break; } - if (i>=DICT_WORD_SIZE) break; + + /* LONG_DICT_FLAG_BUG emulates the old behavior where we stop looping + at DICT_WORD_SIZE. */ + if (LONG_DICT_FLAG_BUG && i>=DICT_WORD_SIZE) + break; k= ((unsigned char *)dword)[j]; if (k=='\'') @@ -1996,17 +2144,27 @@ Define DICT_CHAR_SIZE=4 for a Unicode-compatible dictionary."); if (k >= (unsigned)'A' && k <= (unsigned)'Z') k += ('a' - 'A'); + ensure_memory_list_available(&prepared_sort_memlist, DICT_WORD_BYTES); + if (DICT_CHAR_SIZE == 1) { - prepared_sort[i] = k; + if (i> 24) & 0xFF; - prepared_sort[4*i+1] = (k >> 16) & 0xFF; - prepared_sort[4*i+2] = (k >> 8) & 0xFF; - prepared_sort[4*i+3] = (k) & 0xFF; + if (i> 24) & 0xFF; + prepared_sort[4*i+1] = (k >> 16) & 0xFF; + prepared_sort[4*i+2] = (k >> 8) & 0xFF; + prepared_sort[4*i+3] = (k) & 0xFF; + i++; + } } } + if (i > DICT_WORD_SIZE) + compiler_error("dict word buffer overflow"); + + /* Right-pad with zeroes */ if (DICT_CHAR_SIZE == 1) { for (; i 3) { p[4]=prepared_sort[4]; p[5]=prepared_sort[5]; } - 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; + p[res]=flag1; p[res+1]=flag2; + if (!ZCODE_LESS_DICT_DATA) p[res+2]=flag3; dictionary_top += DICT_ENTRY_BYTE_LENGTH; @@ -2288,11 +2449,9 @@ extern int dictionary_add(char *dword, int x, int y, int z) p[i] = prepared_sort[i]; p += DICT_WORD_BYTES; - p[0] = 0; p[1] = x; - p[2] = y/256; p[3] = y%256; - p[4] = 0; p[5] = z; - if (x & 128) - p[1] |= number_and_case; + p[0] = (flag1/256); p[1] = (flag1%256); + p[2] = (flag2/256); p[3] = (flag2%256); + p[4] = (flag3/256); p[5] = (flag3%256); dictionary_top += DICT_ENTRY_BYTE_LENGTH; @@ -2512,11 +2671,13 @@ static void recursively_show_z(int node, int level) flags = (int) p[res]; if (flags & 128) - { printf("noun "); - if (flags & 4) printf("p"); else printf(" "); - printf(" "); - } - else printf(" "); + printf("noun "); + else + printf(" "); + if (flags & 4) + printf("p "); + else + printf(" "); if (flags & 8) { if (grammar_version_number == 1) printf("preposition:%d ", (int) p[res+2]); @@ -2571,11 +2732,13 @@ static void recursively_show_g(int node, int level) for (i=0; i 33;\ if (w == 0) w=80;\ w2 = (w - maxw)/2;\ + if (w2 < 3) w2 = 3;\ style reverse;\ @sub w2 2 -> w;\ line = 5;\ @@ -211,11 +213,16 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = prop = (i-->0) & $7fff;\ }\ }", - "p = #identifiers_table;\ + "#IFDEF OMIT_SYMBOL_TABLE;\ + p = size = 0;\ + print \"\";\ + #IFNOT;\ + p = #identifiers_table;\ size = p-->0;\ if (prop<=0 || prop>=size || p-->prop==0)\ print \"\";\ else print (string) p-->prop;\ + #ENDIF;\ ]", "", "", "", "" }, @@ -266,6 +273,10 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = "CA__Pr", "obj id a b c d e f x y z s s2 n m;\ + #IFV3;\ + #Message error \"Object message calls are not supported in v3.\";\ + obj = id = a = b = c = d = e = f = x = y = z = s = s2 = n = m = 0;\ + #IFNOT;\ if (obj < 1 || obj > #largest_object-255)\ { switch(Z__Region(obj))\ { 2: if (id == call)\ @@ -327,6 +338,7 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = default: return x-->m;\ }\ }\ + #ENDIF;\ rfalse;\ ]" }, @@ -417,7 +429,11 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = identifier = (identifier & $3f00) / $100;\ if (~~(obj ofclass cla)) rfalse; i=0-->5;\ if (cla == 2) return i+2*identifier-2;\ + #IFV3;\ + i = (i+60+cla*9)-->0;\ + #IFNOT;\ i = 0-->((i+124+cla*14)/2);\ + #ENDIF;\ i = CP__Tab(i + 2*(0->i) + 1, -1)+6;\ return CP__Tab(i, identifier);\ }\ @@ -438,16 +454,23 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = }, { /* RL__Pr: read the property length of an individual property value, - returning 0 if it isn't provided by the given object */ + returning 0 if it isn't provided by the given object. + This is also used for inherited values (of the form + class::prop). */ "RL__Pr", "obj identifier x;\ if (identifier<64 && identifier>0) return obj.#identifier;\ x = obj..&identifier;\ if (x==0) rfalse;\ - if (identifier&$C000==$4000)\ + if (identifier&$C000==$4000) {\ + #IFV3;\ + return 1+((x-1)->0)/$20;\ + #IFNOT;\ switch (((x-1)->0)&$C0)\ { 0: return 1; $40: return 2; $80: return ((x-1)->0)&$3F; }\ + #ENDIF;\ + }\ return (x-1)->0;\ ]", "", "", "", "", "" }, @@ -583,8 +606,13 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = \" in the\"; switch(size&7){0,1:q=0; 2:print \" string\";\ q=1; 3:print \" table\";q=1; 4:print \" buffer\";q=WORDSIZE;} \ if(size&16) print\" (->)\"; if(size&8) print\" (-->)\";\ + #IFDEF OMIT_SYMBOL_TABLE;\ + \" array which has entries \", q, \" up to \",id,\" **]\";\ + #IFNOT;\ \" array ~\", (string) #array_names_offset-->p,\ - \"~, which has entries \", q, \" up to \",id,\" **]\"; }\ + \"~, which has entries \", q, \" up to \",id,\" **]\";\ + #ENDIF;\ + }\ if (crime >= 24 && crime <=27) { if (crime<=25) print \"read\";\ else print \"write\"; print \" outside memory using \";\ switch(crime) { 24,26:\"-> **]\"; 25,27:\"--> **]\"; } }\ @@ -618,10 +646,12 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = \", but it is longer than 2 bytes so you cannot use ~.~\";\ else\ { print \" has no property \", (property) id;\ + #IFNDEF OMIT_SYMBOL_TABLE;\ p = #identifiers_table;\ size = p-->0;\ if (id<0 || id>=size)\ print \" (and nor has any other object)\";\ + #ENDIF;\ }\ print \" to \", (string) crime, \" **]^\";\ ]", "" @@ -687,6 +717,16 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = "CP__Tab", "x id n l;\ + #IFV3;\ + while (1)\ + { n = x->0;\ + if (n == 0) break;\ + x++;\ + if (id == (n & $1f)) return x;\ + l = (n/$20)+1;\ + x = x + l;\ + }\ + #IFNOT;\ while ((n=0->x) ~= 0)\ { if (n & $80) { x++; l = (0->x) & $3f; }\ else { if (n & $40) l=2; else l=1; }\ @@ -694,12 +734,17 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = if ((n & $3f) == id) return x;\ x = x + l;\ }\ + #ENDIF;\ if (id<0) return x+1; rfalse; ]", "", "", "", "", "" }, { /* Cl__Ms: the five message-receiving properties of Classes */ "Cl__Ms", "obj id y a b c d x;\ + #IFV3;\ + #Message error \"Class messages are not supported in v3.\";\ + obj = id = y = a = b = c = d = x = 0;\ + #IFNOT;\ switch(id)\ { create:\ if (children(obj)<=1) rfalse; x=child(obj);\ @@ -730,6 +775,7 @@ static VeneerRoutine VRs_z[VENEER_ROUTINES] = { RT__Err(\"copy\", b, -obj); rfalse; }\ Copy__Primitive(a, b); rfalse;\ }\ + #ENDIF;\ ]", "", "", "" }, { /* RT__ChT: check at run-time that a proposed object move is legal @@ -986,6 +1032,10 @@ static VeneerRoutine VRs_g[VENEER_ROUTINES] = print (name) cla, \"::\";\ @ushiftr prop 16 prop;\ }\ + #IFDEF OMIT_SYMBOL_TABLE;\ + ptab = maxcom = minind = maxind = str = 0;\ + print \"\";\ + #IFNOT;\ ptab = #identifiers_table;\ maxcom = ptab-->1;\ minind = INDIV_PROP_START;\ @@ -1001,6 +1051,7 @@ static VeneerRoutine VRs_g[VENEER_ROUTINES] = print (string) str;\ else\ print \"\";\ + #ENDIF;\ ]", "", "", "", "", "" }, @@ -1429,8 +1480,13 @@ static VeneerRoutine VRs_g[VENEER_ROUTINES] = \" in the\"; switch(size&7){0,1:q=0; 2:print \" string\";\ q=1; 3:print \" table\";q=1; 4:print \" buffer\";q=WORDSIZE;} \ if(size&16) print\" (->)\"; if(size&8) print\" (-->)\";\ + #IFDEF OMIT_SYMBOL_TABLE;\ + \" array which has entries \", q, \" up to \",id,\" **]\";\ + #IFNOT;\ \" array ~\", (string) #array_names_offset-->(p+1),\ - \"~, which has entries \", q, \" up to \",id,\" **]\"; }\ + \"~, which has entries \", q, \" up to \",id,\" **]\";\ + #ENDIF;\ + }\ if (crime >= 24 && crime <=27) { if (crime<=25) print \"read\";\ else print \"write\"; print \" outside memory using \";\ switch(crime) { 24,26:\"-> **]\"; 25,27:\"--> **]\"; } }\ @@ -1462,10 +1518,12 @@ static VeneerRoutine VRs_g[VENEER_ROUTINES] = if (id<0) print \"is not of class \", (name) -id;", "else\ { print \" has no property \", (property) id;\ + #IFNDEF OMIT_SYMBOL_TABLE;\ p = #identifiers_table;\ size = INDIV_PROP_START + p-->3;\ if (id<0 || id>=size)\ print \" (and nor has any other object)\";\ + #ENDIF;\ }\ print \" to \", (string) crime, \" **]^\";\ ]", "" @@ -2203,15 +2261,16 @@ static void compile_symbol_table_routine(void) { int32 j, nl, arrays_l, routines_l, constants_l; assembly_operand AO, AO2, AO3; + clear_local_variables(); /* Assign local var names for the benefit of the debugging information 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"); + add_local_variable("dummy1"); + add_local_variable("dummy2"); - veneer_mode = TRUE; j = symbol_index("Symb__Tab", -1); + veneer_mode = TRUE; j = symbol_index("Symb__Tab", -1, NULL); assign_symbol(j, - assemble_routine_header(2, FALSE, "Symb__Tab", FALSE, j), + assemble_routine_header(FALSE, "Symb__Tab", FALSE, j), ROUTINE_T); symbols[j].flags |= SYSTEM_SFLAG + USED_SFLAG; if (trace_fns_setting==3) symbols[j].flags |= STAR_SFLAG; @@ -2363,7 +2422,7 @@ extern void compile_veneer(void) { try_veneer_again = FALSE; for (i=0; i= 255) { error("Grammar version 1 cannot support more than 255 prepositions"); @@ -464,9 +482,13 @@ static int make_adjective(char *English_word) error("Grammar version 1 cannot be used with ZCODE_LESS_DICT_DATA"); return 0; } + + /* Allocate the extra space even though we might not need it. We'll use + the prospective new adjective_sort_code slot as a workspace. */ ensure_memory_list_available(&adjectives_memlist, no_adjectives+1); ensure_memory_list_available(&adjective_sort_code_memlist, (no_adjectives+1) * DICT_WORD_BYTES); + new_sort_code = adjective_sort_code+no_adjectives*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); + error_fmt("Verb word is too long -- max length is %d", 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; @@ -592,11 +611,44 @@ static int get_verb(void) return j; } - ebf_error("an English verb in quotes", token_text); + ebf_curtoken_error("an English verb in quotes"); return -1; } +void locate_dead_grammar_lines() +{ + /* Run through the grammar table and check whether each entry is + associated with a verb word. (Some might have been detached by + "Extend only".) + */ + int verb; + char *p; + + for (verb=0; verb= no_Inform_verbs) { + error_named("An entry in the English verb list had an invalid verb number", p+3); + } + else { + Inform_verbs[verb].used = TRUE; + } + p=p+(uchar)p[0]; + } + + for (verb=0; verb' clause", token_text); + ebf_curtoken_error("'->' clause"); return FALSE; } if ((token_type == SEP_TT) && (token_value == ARROW_SEP)) { if (last_was_slash && (grammar_token>0)) - ebf_error("grammar token", token_text); + ebf_curtoken_error("grammar token"); break; } @@ -694,7 +746,7 @@ static int grammar_line(int verbnum, int line) { if (grammar_version_number == 1) error("'/' can only be used with Library 6/3 or later"); if (last_was_slash) - ebf_error("grammar token or '->'", token_text); + ebf_curtoken_error("grammar token or '->'"); else { last_was_slash = TRUE; slash_mode = TRUE; @@ -724,7 +776,7 @@ static int grammar_line(int verbnum, int line) if ((token_type != SYMBOL_TT) || (symbols[token_value].type != ROUTINE_T)) { discard_token_location(beginning_debug_location); - ebf_error("routine name after 'noun='", token_text); + ebf_curtoken_error("routine name after 'noun='"); panic_mode_error_recovery(); return FALSE; } @@ -779,7 +831,7 @@ are using Library 6/3 or later"); get_next_token(); if (!((token_type==SEP_TT)&&(token_value==SETEQUALS_SEP))) { discard_token_location(beginning_debug_location); - ebf_error("'=' after 'scope'", token_text); + ebf_curtoken_error("'=' after 'scope'"); panic_mode_error_recovery(); return FALSE; } @@ -788,7 +840,7 @@ are using Library 6/3 or later"); if ((token_type != SYMBOL_TT) || (symbols[token_value].type != ROUTINE_T)) { discard_token_location(beginning_debug_location); - ebf_error("routine name after 'scope='", token_text); + ebf_curtoken_error("routine name after 'scope='"); panic_mode_error_recovery(); return FALSE; } @@ -865,9 +917,9 @@ tokens in any line (unless you're compiling with library 6/3 or later)"); get_next_token(); dont_enter_into_symbol_table = FALSE; - if (token_type != DQ_TT) + if (token_type != UQ_TT) { discard_token_location(beginning_debug_location); - ebf_error("name of new or existing action", token_text); + ebf_curtoken_error("name of new or existing action"); panic_mode_error_recovery(); return FALSE; } @@ -957,7 +1009,7 @@ extern void make_verb(void) } if (no_given == 0) - { ebf_error("English verb in quotes", token_text); + { ebf_curtoken_error("English verb in quotes"); panic_mode_error_recovery(); return; } @@ -968,7 +1020,7 @@ extern void make_verb(void) if (Inform_verb == -1) return; get_next_token(); if (!((token_type == SEP_TT) && (token_value == SEMICOLON_SEP))) - ebf_error("';' after English verb", token_text); + ebf_curtoken_error("';' after English verb"); } else { verb_equals_form = FALSE; @@ -976,11 +1028,17 @@ extern void make_verb(void) error("Z-code is limited to 255 verbs."); panic_mode_error_recovery(); return; } + if (no_Inform_verbs >= 65535) { + error("Inform is limited to 65535 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"); + Inform_verbs[no_Inform_verbs].line = get_brief_location(&ErrorReport); + Inform_verbs[no_Inform_verbs].used = FALSE; } for (i=0, pos=0; i= 65535) { + error("Inform is limited to 65535 verbs."); + panic_mode_error_recovery(); return; + } ensure_memory_list_available(&Inform_verbs_memlist, no_Inform_verbs+1); l = -1; while (get_next_token(), @@ -1061,6 +1123,8 @@ extern void extend_verb(void) Inform_verbs[no_Inform_verbs].l = my_malloc(sizeof(int) * Inform_verbs[no_Inform_verbs].size, "grammar lines for one verb"); for (k=0; k