X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=src%2Fsyntax.c;h=cae1bc47a087d2ee6aa7bedc7ba4d525dd865f81;hb=56a5292888e1d46fe3033cd1d5c636051692453f;hp=08e61c90da922db2ccc7dc159f2026419d8683bc;hpb=8760c1ba6442153afe76bcac742e086f90c59fe8;p=inform.git diff --git a/src/syntax.c b/src/syntax.c index 08e61c9..cae1bc4 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -1,8 +1,8 @@ /* ------------------------------------------------------------------------- */ /* "syntax" : Syntax analyser and compiler */ /* */ -/* Part of Inform 6.35 */ -/* copyright (c) Graham Nelson 1993 - 2020 */ +/* 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 */ @@ -15,7 +15,7 @@ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ -/* along with Inform. If not, see https://gnu.org/licenses/ * +/* along with Inform. If not, see https://gnu.org/licenses/ */ /* */ /* ------------------------------------------------------------------------- */ @@ -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; } } @@ -146,9 +149,9 @@ extern void parse_program(char *source) extern int parse_directive(int internal_flag) { - /* Internal_flag is FALSE if the directive is encountered normally, - TRUE if encountered with a # prefix inside a routine or object - definition. + /* Internal_flag is FALSE if the directive is encountered normally + (at the top level of the program); TRUE if encountered with + a # prefix inside a routine or object definition. (Only directives like #ifdef are permitted inside a definition.) @@ -158,6 +161,11 @@ extern int parse_directive(int internal_flag) int is_renamed; begin_syntax_line(FALSE); + if (!internal_flag) { + /* An internal directive can occur in the middle of an expression or + object definition. So we only release for top-level directives. */ + release_token_texts(); + } get_next_token(); if (token_type == EOF_TT) return(FALSE); @@ -181,12 +189,12 @@ 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 ((!(sflags[token_value] & UNKNOWN_SFLAG)) - && (!(sflags[token_value] & REPLACE_SFLAG))) - { ebf_symbol_error("routine name", token_text, typename(stypes[token_value]), slines[token_value]); + if ((!(symbols[token_value].flags & UNKNOWN_SFLAG)) + && (!(symbols[token_value].flags & REPLACE_SFLAG))) + { ebf_symbol_error("routine name", token_text, typename(symbols[token_value].type), symbols[token_value].line); return(FALSE); } @@ -195,7 +203,7 @@ extern int parse_directive(int internal_flag) rep_symbol = routine_symbol; is_renamed = find_symbol_replacement(&rep_symbol); - if ((sflags[routine_symbol] & REPLACE_SFLAG) + if ((symbols[routine_symbol].flags & REPLACE_SFLAG) && !is_renamed && (is_systemfile())) { /* The function is definitely being replaced (system_file always loses priority in a replacement) but is not @@ -214,9 +222,9 @@ extern int parse_directive(int internal_flag) { /* Parse the function definition and assign its symbol. */ assign_symbol(routine_symbol, parse_routine(lexical_source, FALSE, - (char *) symbs[routine_symbol], FALSE, routine_symbol), + symbols[routine_symbol].name, FALSE, routine_symbol), ROUTINE_T); - slines[routine_symbol] = routine_starts_line; + symbols[routine_symbol].line = routine_starts_line; } if (is_renamed) { @@ -224,26 +232,26 @@ extern int parse_directive(int internal_flag) The first time we see a definition for symbol X, we copy it to Y -- that's the "original" form of the function. */ - if (svals[rep_symbol] == 0) { - assign_symbol(rep_symbol, svals[routine_symbol], ROUTINE_T); + if (symbols[rep_symbol].value == 0) { + assign_symbol(rep_symbol, symbols[routine_symbol].value, ROUTINE_T); } } 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; } - if ((token_type == SYMBOL_TT) && (stypes[token_value] == CLASS_T)) + if ((token_type == SYMBOL_TT) && (symbols[token_value].type == CLASS_T)) { if (internal_flag) { error("It is illegal to nest an object in a routine using '#classname'"); return(TRUE); } - sflags[token_value] |= USED_SFLAG; - make_object(FALSE, NULL, -1, -1, svals[token_value]); + symbols[token_value].flags |= USED_SFLAG; + make_object(FALSE, NULL, -1, -1, symbols[token_value].value); return TRUE; } @@ -251,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; } @@ -261,6 +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. + (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; @@ -269,8 +280,10 @@ static int switch_sign(void) return 0; } -static assembly_operand spec_stack[32]; -static int spec_type[32]; +/* Info for the current switch statement. Both arrays indexed by spec_sp */ +#define MAX_SPEC_STACK (32) +static assembly_operand spec_stack[MAX_SPEC_STACK]; +static int spec_type[MAX_SPEC_STACK]; static void compile_alternatives_z(assembly_operand switch_value, int n, int stack_level, int label, int flag) @@ -315,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 == 32) - { error("At most 32 values can be given in a single 'switch' case"); + { if (spec_sp >= MAX_SPEC_STACK) + { error_fmt("At most %d values can be given in a single 'switch' case", MAX_SPEC_STACK); panic_mode_error_recovery(); return; } @@ -333,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(); @@ -355,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, @@ -442,9 +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