--- /dev/null
+/* ------------------------------------------------------------------------- */
+/* "expressp" : The expression parser */
+/* */
+/* Copyright (c) Graham Nelson 1993 - 2018 */
+/* */
+/* This file is part of Inform. */
+/* */
+/* Inform is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* Inform is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with Inform. If not, see https://gnu.org/licenses/ */
+/* */
+/* ------------------------------------------------------------------------- */
+
+#include "header.h"
+
+/* --- Interface to lexer -------------------------------------------------- */
+
+static char separators_to_operators[103];
+static char conditionals_to_operators[7];
+static char token_type_allowable[301];
+
+#define NOT_AN_OPERATOR (char) 0x7e
+
+static void make_lexical_interface_tables(void)
+{ int i;
+ for (i=0;i<103;i++)
+ separators_to_operators[i] = NOT_AN_OPERATOR;
+ for (i=0;i<NUM_OPERATORS;i++)
+ if (operators[i].token_type == SEP_TT)
+ separators_to_operators[operators[i].token_value] = i;
+
+ for (i=0;i<7;i++) /* 7 being the size of keyword_group "conditions" */
+ conditionals_to_operators[i] = NOT_AN_OPERATOR;
+ for (i=0;i<NUM_OPERATORS;i++)
+ if (operators[i].token_type == CND_TT)
+ conditionals_to_operators[operators[i].token_value] = i;
+
+ for (i=0;i<301;i++) token_type_allowable[i] = 0;
+
+ token_type_allowable[VARIABLE_TT] = 1;
+ token_type_allowable[SYSFUN_TT] = 1;
+ token_type_allowable[DQ_TT] = 1;
+ token_type_allowable[DICTWORD_TT] = 1;
+ token_type_allowable[SUBOPEN_TT] = 1;
+ token_type_allowable[SUBCLOSE_TT] = 1;
+ token_type_allowable[SMALL_NUMBER_TT] = 1;
+ token_type_allowable[LARGE_NUMBER_TT] = 1;
+ token_type_allowable[ACTION_TT] = 1;
+ token_type_allowable[SYSTEM_CONSTANT_TT] = 1;
+ token_type_allowable[OP_TT] = 1;
+}
+
+static token_data current_token, previous_token, heldback_token;
+
+static int comma_allowed, arrow_allowed, superclass_allowed,
+ bare_prop_allowed,
+ array_init_ambiguity, action_ambiguity,
+ etoken_count, inserting_token, bracket_level;
+
+extern int *variable_usage;
+
+int system_function_usage[32];
+
+static int get_next_etoken(void)
+{ int v, symbol = 0, mark_symbol_as_used = FALSE,
+ initial_bracket_level = bracket_level;
+
+ etoken_count++;
+
+ if (inserting_token)
+ { current_token = heldback_token;
+ inserting_token = FALSE;
+ }
+ else
+ { get_next_token();
+ current_token.text = token_text;
+ current_token.value = token_value;
+ current_token.type = token_type;
+ current_token.marker = 0;
+ current_token.symtype = 0;
+ current_token.symflags = -1;
+ }
+
+ switch(current_token.type)
+ { case LOCAL_VARIABLE_TT:
+ current_token.type = VARIABLE_TT;
+ variable_usage[current_token.value] = TRUE;
+ break;
+
+ case DQ_TT:
+ current_token.marker = STRING_MV;
+ break;
+
+ case SQ_TT:
+ { int32 unicode = text_to_unicode(token_text);
+ if (token_text[textual_form_length] == 0)
+ {
+ if (!glulx_mode) {
+ current_token.value = unicode_to_zscii(unicode);
+ if (current_token.value == 5)
+ { unicode_char_error("Character can be printed \
+but not used as a value:", unicode);
+ current_token.value = '?';
+ }
+ if (current_token.value >= 0x100)
+ current_token.type = LARGE_NUMBER_TT;
+ else current_token.type = SMALL_NUMBER_TT;
+ }
+ else {
+ current_token.value = unicode;
+ if (current_token.value >= 0x8000
+ || current_token.value < -0x8000)
+ current_token.type = LARGE_NUMBER_TT;
+ else current_token.type = SMALL_NUMBER_TT;
+ }
+ }
+ else
+ { current_token.type = DICTWORD_TT;
+ current_token.marker = DWORD_MV;
+ }
+ }
+ break;
+
+ case SYMBOL_TT:
+ ReceiveSymbol:
+ symbol = current_token.value;
+
+ mark_symbol_as_used = TRUE;
+
+ v = svals[symbol];
+
+ current_token.symtype = stypes[symbol];
+ current_token.symflags = sflags[symbol];
+ switch(stypes[symbol])
+ { case ROUTINE_T:
+ /* Replaced functions must always be backpatched
+ because there could be another definition coming. */
+ if (sflags[symbol] & REPLACE_SFLAG)
+ { current_token.marker = SYMBOL_MV;
+ if (module_switch) import_symbol(symbol);
+ v = symbol;
+ break;
+ }
+ current_token.marker = IROUTINE_MV;
+ break;
+ case GLOBAL_VARIABLE_T:
+ current_token.marker = VARIABLE_MV;
+ break;
+ case OBJECT_T:
+ case CLASS_T:
+ /* All objects must be backpatched in Glulx. */
+ if (module_switch || glulx_mode)
+ current_token.marker = OBJECT_MV;
+ break;
+ case ARRAY_T:
+ current_token.marker = ARRAY_MV;
+ break;
+ case INDIVIDUAL_PROPERTY_T:
+ if (module_switch) current_token.marker = IDENT_MV;
+ break;
+ case CONSTANT_T:
+ if (sflags[symbol] & (UNKNOWN_SFLAG + CHANGE_SFLAG))
+ { current_token.marker = SYMBOL_MV;
+ if (module_switch) import_symbol(symbol);
+ v = symbol;
+ }
+ else current_token.marker = 0;
+ break;
+ case LABEL_T:
+ error_named("Label name used as value:", token_text);
+ break;
+ default:
+ current_token.marker = 0;
+ break;
+ }
+ if (sflags[symbol] & SYSTEM_SFLAG)
+ current_token.marker = 0;
+
+ current_token.value = v;
+
+ if (!glulx_mode) {
+ if (((current_token.marker != 0)
+ && (current_token.marker != VARIABLE_MV))
+ || (v < 0) || (v > 255))
+ current_token.type = LARGE_NUMBER_TT;
+ else current_token.type = SMALL_NUMBER_TT;
+ }
+ else {
+ if (((current_token.marker != 0)
+ && (current_token.marker != VARIABLE_MV))
+ || (v < -0x8000) || (v >= 0x8000))
+ current_token.type = LARGE_NUMBER_TT;
+ else current_token.type = SMALL_NUMBER_TT;
+ }
+
+ if (stypes[symbol] == GLOBAL_VARIABLE_T)
+ { current_token.type = VARIABLE_TT;
+ variable_usage[current_token.value] = TRUE;
+ }
+ break;
+
+ case NUMBER_TT:
+ if (!glulx_mode) {
+ if (current_token.value >= 256)
+ current_token.type = LARGE_NUMBER_TT;
+ else
+ current_token.type = SMALL_NUMBER_TT;
+ }
+ else {
+ if (current_token.value < -0x8000
+ || current_token.value >= 0x8000)
+ current_token.type = LARGE_NUMBER_TT;
+ else
+ current_token.type = SMALL_NUMBER_TT;
+ }
+ break;
+
+ case SEP_TT:
+ switch(current_token.value)
+ { case ARROW_SEP:
+ if (!arrow_allowed)
+ current_token.type = ENDEXP_TT;
+ break;
+
+ case COMMA_SEP:
+ if ((bracket_level==0) && (!comma_allowed))
+ current_token.type = ENDEXP_TT;
+ break;
+
+ case SUPERCLASS_SEP:
+ if ((bracket_level==0) && (!superclass_allowed))
+ current_token.type = ENDEXP_TT;
+ break;
+
+ case GREATER_SEP:
+ get_next_token();
+ if ((token_type == SEP_TT)
+ &&((token_value == SEMICOLON_SEP)
+ || (token_value == GREATER_SEP)))
+ current_token.type = ENDEXP_TT;
+ put_token_back();
+ break;
+
+ case OPENB_SEP:
+ bracket_level++;
+ if (expr_trace_level>=3)
+ { printf("Previous token type = %d\n",previous_token.type);
+ printf("Previous token val = %d\n",previous_token.value);
+ }
+ if ((previous_token.type == OP_TT)
+ || (previous_token.type == SUBOPEN_TT)
+ || (previous_token.type == ENDEXP_TT)
+ || (array_init_ambiguity)
+ || ((bracket_level == 1) && (action_ambiguity)))
+ current_token.type = SUBOPEN_TT;
+ else
+ { inserting_token = TRUE;
+ heldback_token = current_token;
+ current_token.text = "<call>";
+ bracket_level--;
+ }
+ break;
+
+ case CLOSEB_SEP:
+ bracket_level--;
+ if (bracket_level < 0)
+ current_token.type = ENDEXP_TT;
+ else current_token.type = SUBCLOSE_TT;
+ break;
+
+ case SEMICOLON_SEP:
+ current_token.type = ENDEXP_TT; break;
+
+ case MINUS_SEP:
+ if ((previous_token.type == OP_TT)
+ || (previous_token.type == SUBOPEN_TT)
+ || (previous_token.type == ENDEXP_TT))
+ current_token.value = UNARY_MINUS_SEP; break;
+
+ case INC_SEP:
+ if ((previous_token.type == VARIABLE_TT)
+ || (previous_token.type == SUBCLOSE_TT)
+ || (previous_token.type == LARGE_NUMBER_TT)
+ || (previous_token.type == SMALL_NUMBER_TT))
+ current_token.value = POST_INC_SEP; break;
+
+ case DEC_SEP:
+ if ((previous_token.type == VARIABLE_TT)
+ || (previous_token.type == SUBCLOSE_TT)
+ || (previous_token.type == LARGE_NUMBER_TT)
+ || (previous_token.type == SMALL_NUMBER_TT))
+ current_token.value = POST_DEC_SEP; break;
+
+ case HASHHASH_SEP:
+ token_text = current_token.text + 2;
+
+ ActionUsedAsConstant:
+
+ current_token.type = ACTION_TT;
+ current_token.text = token_text;
+ current_token.value = 0;
+ current_token.marker = ACTION_MV;
+
+ break;
+
+ case HASHADOLLAR_SEP:
+ obsolete_warning("'#a$Act' is now superseded by '##Act'");
+ token_text = current_token.text + 3;
+ goto ActionUsedAsConstant;
+
+ case HASHGDOLLAR_SEP:
+
+ /* This form generates the position of a global variable
+ in the global variables array. So Glob is the same as
+ #globals_array --> #g$Glob */
+
+ current_token.text += 3;
+ current_token.type = SYMBOL_TT;
+ symbol = symbol_index(current_token.text, -1);
+ if (stypes[symbol] != GLOBAL_VARIABLE_T) {
+ ebf_error(
+ "global variable name after '#g$'",
+ current_token.text);
+ current_token.value = 0;
+ current_token.type = SMALL_NUMBER_TT;
+ current_token.marker = 0;
+ break;
+ }
+ mark_symbol_as_used = TRUE;
+ current_token.value = svals[symbol] - MAX_LOCAL_VARIABLES;
+ current_token.marker = 0;
+ if (!glulx_mode) {
+ if (current_token.value >= 0x100)
+ current_token.type = LARGE_NUMBER_TT;
+ else current_token.type = SMALL_NUMBER_TT;
+ }
+ else {
+ if (current_token.value >= 0x8000
+ || current_token.value < -0x8000)
+ current_token.type = LARGE_NUMBER_TT;
+ else current_token.type = SMALL_NUMBER_TT;
+ }
+ break;
+
+ case HASHNDOLLAR_SEP:
+
+ /* This form is still needed for constants like #n$a (the
+ dictionary address of the word "a"), since 'a' means
+ the ASCII value of 'a' */
+
+ if (strlen(token_text) > 4)
+ obsolete_warning(
+ "'#n$word' is now superseded by ''word''");
+ current_token.type = DICTWORD_TT;
+ current_token.value = 0;
+ current_token.text = token_text + 3;
+ current_token.marker = DWORD_MV;
+ break;
+
+ case HASHRDOLLAR_SEP:
+
+ /* This form -- #r$Routinename, to return the routine's */
+ /* packed address -- is needed far less often in Inform 6, */
+ /* where just giving the name Routine returns the packed */
+ /* address. But it's used in a lot of Inform 5 code. */
+
+ obsolete_warning(
+ "'#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);
+ goto ReceiveSymbol;
+
+ case HASHWDOLLAR_SEP:
+ error("The obsolete '#w$word' construct has been removed");
+ break;
+
+ case HASH_SEP:
+ system_constants.enabled = TRUE;
+ 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);
+ break;
+ }
+ else
+ { current_token.type = token_type;
+ current_token.value = token_value;
+ current_token.text = token_text;
+ current_token.marker = INCON_MV;
+ }
+ break;
+ }
+ break;
+
+ case CND_TT:
+ v = conditionals_to_operators[current_token.value];
+ if (v != NOT_AN_OPERATOR)
+ { current_token.type = OP_TT; current_token.value = v;
+ }
+ break;
+ }
+
+ if (current_token.type == SEP_TT)
+ { v = separators_to_operators[current_token.value];
+ if (v != NOT_AN_OPERATOR)
+ { if ((veneer_mode)
+ || ((v!=MESSAGE_OP) && (v!=MPROP_NUM_OP) && (v!=MPROP_NUM_OP)))
+ { current_token.type = OP_TT; current_token.value = v;
+ if (array_init_ambiguity &&
+ ((v==MINUS_OP) || (v==UNARY_MINUS_OP)) &&
+ (initial_bracket_level == 0) &&
+ (etoken_count != 1))
+ warning("Without bracketing, the minus sign '-' is ambiguous");
+ }
+ }
+ }
+
+ /* A feature of Inform making it annoyingly hard to parse left-to-right
+ is that there is no clear delimiter for expressions; that is, the
+ legal syntax often includes sequences of expressions with no
+ intervening markers such as commas. We therefore need to use some
+ internal context to determine whether an end is in sight... */
+
+ if (token_type_allowable[current_token.type]==0)
+ { if (expr_trace_level >= 3)
+ { printf("Discarding as not allowable: '%s' ", current_token.text);
+ describe_token(current_token);
+ printf("\n");
+ }
+ current_token.type = ENDEXP_TT;
+ }
+ else
+ if ((!((initial_bracket_level > 0)
+ || (previous_token.type == ENDEXP_TT)
+ || ((previous_token.type == OP_TT)
+ && (operators[previous_token.value].usage != POST_U))
+ || (previous_token.type == SYSFUN_TT)))
+ && ((current_token.type != OP_TT)
+ || (operators[current_token.value].usage == PRE_U)))
+ { if (expr_trace_level >= 3)
+ { printf("Discarding as no longer part: '%s' ", current_token.text);
+ describe_token(current_token);
+ printf("\n");
+ }
+ current_token.type = ENDEXP_TT;
+ }
+ else
+ { if (mark_symbol_as_used) sflags[symbol] |= USED_SFLAG;
+ if (expr_trace_level >= 3)
+ { printf("Expr token = '%s' ", current_token.text);
+ describe_token(current_token);
+ printf("\n");
+ }
+ }
+
+ if ((previous_token.type == ENDEXP_TT)
+ && (current_token.type == ENDEXP_TT)) return FALSE;
+
+ previous_token = current_token;
+
+ return TRUE;
+}
+
+/* --- Operator precedences ------------------------------------------------ */
+
+#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 */
+
+const int prec_table[] = {
+
+/* a .......... ( ) end op 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
+
+};
+
+static int find_prec(token_data a, 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.
+
+ Note that this routine is not symmetrical and that the relation
+ is not trichotomous.
+
+ If a and b are equal (and aren't brackets), then
+
+ a LOWER_P a if a right-associative
+ a GREATER_P a if a left-associative
+ */
+
+ int i, j, l1, l2;
+
+ switch(a.type)
+ { case SUBOPEN_TT: i=0; break;
+ case SUBCLOSE_TT: i=1; break;
+ case ENDEXP_TT: i=2; break;
+ case OP_TT: i=3; break;
+ default: i=4; break;
+ }
+ switch(b.type)
+ { 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;
+ }
+
+ j = prec_table[i]; if (j != -1) return j;
+
+ l1 = operators[a.value].precedence;
+ l2 = operators[b.value].precedence;
+ if (operators[b.value].usage == PRE_U) return LOWER_P;
+ if (operators[a.value].usage == POST_U) return GREATER_P;
+
+ /* Anomalous rule to resolve the function call precedence, which is
+ different on the right from on the left, e.g., in:
+
+ alpha.beta(gamma)
+ beta(gamma).alpha
+ */
+
+ if ((l1 == 11) && (l2 > 11)) return GREATER_P;
+
+ if (l1 < l2) return LOWER_P;
+ if (l1 > l2) return GREATER_P;
+ switch(operators[a.value].associativity)
+ { case L_A: return GREATER_P;
+ case R_A: return LOWER_P;
+ case 0: return e5;
+ }
+ return GREATER_P;
+}
+
+/* --- Converting token to operand ----------------------------------------- */
+
+/* Must match the switch statement below */
+int z_system_constant_list[] =
+ { adjectives_table_SC,
+ actions_table_SC,
+ classes_table_SC,
+ identifiers_table_SC,
+ preactions_table_SC,
+ largest_object_SC,
+ strings_offset_SC,
+ code_offset_SC,
+ actual_largest_object_SC,
+ static_memory_offset_SC,
+ array_names_offset_SC,
+ readable_memory_offset_SC,
+ cpv__start_SC,
+ cpv__end_SC,
+ ipv__start_SC,
+ ipv__end_SC,
+ array__start_SC,
+ array__end_SC,
+ highest_attribute_number_SC,
+ attribute_names_array_SC,
+ highest_property_number_SC,
+ property_names_array_SC,
+ highest_action_number_SC,
+ action_names_array_SC,
+ highest_fake_action_number_SC,
+ fake_action_names_array_SC,
+ highest_routine_number_SC,
+ routine_names_array_SC,
+ routines_array_SC,
+ routine_flags_array_SC,
+ highest_global_number_SC,
+ global_names_array_SC,
+ globals_array_SC,
+ global_flags_array_SC,
+ highest_array_number_SC,
+ array_names_array_SC,
+ array_flags_array_SC,
+ highest_constant_number_SC,
+ constant_names_array_SC,
+ highest_class_number_SC,
+ class_objects_array_SC,
+ highest_object_number_SC,
+ -1 };
+
+static int32 value_of_system_constant_z(int t)
+{ switch(t)
+ { case adjectives_table_SC:
+ return adjectives_offset;
+ case actions_table_SC:
+ return actions_offset;
+ case classes_table_SC:
+ return class_numbers_offset;
+ case identifiers_table_SC:
+ return identifier_names_offset;
+ case preactions_table_SC:
+ return preactions_offset;
+ case largest_object_SC:
+ return 256 + no_objects - 1;
+ case strings_offset_SC:
+ return strings_offset/scale_factor;
+ case code_offset_SC:
+ return code_offset/scale_factor;
+ case actual_largest_object_SC:
+ return no_objects;
+ case static_memory_offset_SC:
+ return static_memory_offset;
+ case array_names_offset_SC:
+ return array_names_offset;
+ case readable_memory_offset_SC:
+ return Write_Code_At;
+ case cpv__start_SC:
+ return prop_values_offset;
+ case cpv__end_SC:
+ return class_numbers_offset;
+ case ipv__start_SC:
+ return individuals_offset;
+ case ipv__end_SC:
+ return variables_offset;
+ case array__start_SC:
+ return variables_offset + (MAX_GLOBAL_VARIABLES*WORDSIZE);
+ case array__end_SC:
+ return static_memory_offset;
+
+ case highest_attribute_number_SC:
+ return no_attributes-1;
+ case attribute_names_array_SC:
+ return attribute_names_offset;
+
+ case highest_property_number_SC:
+ return no_individual_properties-1;
+ case property_names_array_SC:
+ return identifier_names_offset + 2;
+
+ case highest_action_number_SC:
+ return no_actions-1;
+ case action_names_array_SC:
+ return action_names_offset;
+
+ case highest_fake_action_number_SC:
+ return ((grammar_version_number==1)?256:4096) + no_fake_actions-1;
+ case fake_action_names_array_SC:
+ return fake_action_names_offset;
+
+ case highest_routine_number_SC:
+ return no_named_routines-1;
+ case routine_names_array_SC:
+ return routine_names_offset;
+ case routines_array_SC:
+ return routines_array_offset;
+ case routine_flags_array_SC:
+ return routine_flags_array_offset;
+ case highest_global_number_SC:
+ return 16 + no_globals-1;
+ case global_names_array_SC:
+ return global_names_offset;
+ case globals_array_SC:
+ return variables_offset;
+ case global_flags_array_SC:
+ return global_flags_array_offset;
+ case highest_array_number_SC:
+ return no_arrays-1;
+ case array_names_array_SC:
+ return array_names_offset;
+ case array_flags_array_SC:
+ return array_flags_array_offset;
+ case highest_constant_number_SC:
+ return no_named_constants-1;
+ case constant_names_array_SC:
+ return constant_names_offset;
+ case highest_class_number_SC:
+ return no_classes-1;
+ case class_objects_array_SC:
+ return class_numbers_offset;
+ case highest_object_number_SC:
+ return no_objects-1;
+ }
+
+ error_named("System constant not implemented in Z-code",
+ system_constants.keywords[t]);
+
+ return(0);
+}
+
+/* Must match the switch statement below */
+int glulx_system_constant_list[] =
+ { classes_table_SC,
+ identifiers_table_SC,
+ array_names_offset_SC,
+ cpv__start_SC,
+ cpv__end_SC,
+ dictionary_table_SC,
+ dynam_string_table_SC,
+ grammar_table_SC,
+ actions_table_SC,
+ globals_array_SC,
+ highest_class_number_SC,
+ highest_object_number_SC,
+ -1 };
+
+static int32 value_of_system_constant_g(int t)
+{
+ switch (t) {
+ case classes_table_SC:
+ return Write_RAM_At + class_numbers_offset;
+ case identifiers_table_SC:
+ return Write_RAM_At + identifier_names_offset;
+ case array_names_offset_SC:
+ return Write_RAM_At + array_names_offset;
+ case cpv__start_SC:
+ return prop_defaults_offset;
+ case cpv__end_SC:
+ return Write_RAM_At + class_numbers_offset;
+ case dictionary_table_SC:
+ return dictionary_offset;
+ case dynam_string_table_SC:
+ return abbreviations_offset;
+ case grammar_table_SC:
+ return grammar_table_offset;
+ case actions_table_SC:
+ return actions_offset;
+ case globals_array_SC:
+ return variables_offset;
+ case highest_class_number_SC:
+ return no_classes-1;
+ case highest_object_number_SC:
+ return no_objects-1;
+ }
+
+ error_named("System constant not implemented in Glulx",
+ system_constants.keywords[t]);
+
+ return 0;
+}
+
+extern int32 value_of_system_constant(int t)
+{
+ if (!glulx_mode)
+ return value_of_system_constant_z(t);
+ else
+ return value_of_system_constant_g(t);
+}
+
+static int evaluate_term(token_data t, assembly_operand *o)
+{
+ /* If the given token is a constant, evaluate it into the operand.
+ For now, the identifiers are considered variables.
+
+ Returns FALSE if it fails to understand type. */
+
+ int32 v;
+
+ o->marker = t.marker;
+ o->symtype = t.symtype;
+ o->symflags = t.symflags;
+
+ switch(t.type)
+ { case LARGE_NUMBER_TT:
+ v = t.value;
+ if (!glulx_mode) {
+ if (v < 0) v = v + 0x10000;
+ o->type = LONG_CONSTANT_OT;
+ o->value = v;
+ }
+ else {
+ o->value = v;
+ o->type = CONSTANT_OT;
+ }
+ return(TRUE);
+ case SMALL_NUMBER_TT:
+ v = t.value;
+ if (!glulx_mode) {
+ if (v < 0) v = v + 0x10000;
+ o->type = SHORT_CONSTANT_OT;
+ o->value = v;
+ }
+ else {
+ o->value = v;
+ set_constant_ot(o);
+ }
+ return(TRUE);
+ case DICTWORD_TT:
+ /* Find the dictionary address, adding to dictionary if absent */
+ if (!glulx_mode)
+ o->type = LONG_CONSTANT_OT;
+ else
+ o->type = CONSTANT_OT;
+ o->value = dictionary_add(t.text, 0x80, 0, 0);
+ return(TRUE);
+ case DQ_TT:
+ /* Create as a static string */
+ if (!glulx_mode)
+ o->type = LONG_CONSTANT_OT;
+ else
+ o->type = CONSTANT_OT;
+ o->value = compile_string(t.text, FALSE, FALSE);
+ return(TRUE);
+ case VARIABLE_TT:
+ if (!glulx_mode) {
+ o->type = VARIABLE_OT;
+ }
+ else {
+ if (t.value >= MAX_LOCAL_VARIABLES) {
+ o->type = GLOBALVAR_OT;
+ }
+ else {
+ /* This includes "local variable zero", which is really
+ the stack-pointer magic variable. */
+ o->type = LOCALVAR_OT;
+ }
+ }
+ o->value = t.value;
+ return(TRUE);
+ case SYSFUN_TT:
+ if (!glulx_mode) {
+ o->type = VARIABLE_OT;
+ o->value = t.value + 256;
+ }
+ else {
+ o->type = SYSFUN_OT;
+ o->value = t.value;
+ }
+ system_function_usage[t.value] = 1;
+ return(TRUE);
+ case ACTION_TT:
+ *o = action_of_name(t.text);
+ return(TRUE);
+ case SYSTEM_CONSTANT_TT:
+ /* Certain system constants depend only on the
+ version number and need no backpatching, as they
+ are known in advance. We can therefore evaluate
+ them immediately. */
+ if (!glulx_mode) {
+ o->type = LONG_CONSTANT_OT;
+ switch(t.value)
+ {
+ case version_number_SC:
+ o->type = SHORT_CONSTANT_OT;
+ o->marker = 0;
+ v = version_number; break;
+ case dict_par1_SC:
+ o->type = SHORT_CONSTANT_OT;
+ o->marker = 0;
+ v = (version_number==3)?4:6; break;
+ case dict_par2_SC:
+ o->type = SHORT_CONSTANT_OT;
+ o->marker = 0;
+ v = (version_number==3)?5:7; break;
+ case dict_par3_SC:
+ o->type = SHORT_CONSTANT_OT;
+ o->marker = 0;
+ v = (version_number==3)?6:8; break;
+ case lowest_attribute_number_SC:
+ case lowest_action_number_SC:
+ case lowest_routine_number_SC:
+ case lowest_array_number_SC:
+ case lowest_constant_number_SC:
+ case lowest_class_number_SC:
+ o->type = SHORT_CONSTANT_OT; o->marker = 0; v = 0; break;
+ case lowest_object_number_SC:
+ case lowest_property_number_SC:
+ o->type = SHORT_CONSTANT_OT; o->marker = 0; v = 1; break;
+ case lowest_global_number_SC:
+ o->type = SHORT_CONSTANT_OT; o->marker = 0; v = 16; break;
+ case lowest_fake_action_number_SC:
+ o->type = LONG_CONSTANT_OT; o->marker = 0;
+ v = ((grammar_version_number==1)?256:4096); break;
+ case oddeven_packing_SC:
+ o->type = SHORT_CONSTANT_OT; o->marker = 0;
+ v = oddeven_packing_switch; break;
+ default:
+ v = t.value;
+ o->marker = INCON_MV;
+ break;
+ }
+ o->value = v;
+ }
+ else {
+ o->type = CONSTANT_OT;
+ switch(t.value)
+ {
+ /* The three dict_par flags point at the lower byte
+ of the flag field, because the library is written
+ to expect one-byte fields, even though the compiler
+ generates a dictionary with room for two. */
+ case dict_par1_SC:
+ o->type = BYTECONSTANT_OT;
+ o->marker = 0;
+ v = DICT_ENTRY_FLAG_POS+1;
+ break;
+ case dict_par2_SC:
+ o->type = BYTECONSTANT_OT;
+ o->marker = 0;
+ v = DICT_ENTRY_FLAG_POS+3;
+ break;
+ case dict_par3_SC:
+ o->type = BYTECONSTANT_OT;
+ o->marker = 0;
+ v = DICT_ENTRY_FLAG_POS+5;
+ break;
+
+ case lowest_attribute_number_SC:
+ case lowest_action_number_SC:
+ case lowest_routine_number_SC:
+ case lowest_array_number_SC:
+ case lowest_constant_number_SC:
+ case lowest_class_number_SC:
+ o->type = BYTECONSTANT_OT;
+ o->marker = 0;
+ v = 0;
+ break;
+ case lowest_object_number_SC:
+ case lowest_property_number_SC:
+ o->type = BYTECONSTANT_OT;
+ o->marker = 0;
+ v = 1;
+ break;
+
+ /* ###fix: need to fill more of these in! */
+
+ default:
+ v = t.value;
+ o->marker = INCON_MV;
+ break;
+ }
+ o->value = v;
+ }
+ return(TRUE);
+ default:
+ return(FALSE);
+ }
+}
+
+/* --- Emitter ------------------------------------------------------------- */
+
+expression_tree_node *ET;
+static int ET_used;
+
+extern void clear_expression_space(void)
+{ ET_used = 0;
+}
+
+static assembly_operand *emitter_stack;
+static int *emitter_markers;
+static int *emitter_bracket_counts;
+
+#define FUNCTION_VALUE_MARKER 1
+#define ARGUMENT_VALUE_MARKER 2
+#define OR_VALUE_MARKER 3
+
+static int emitter_sp;
+
+static int is_property_t(int symbol_type)
+{ return ((symbol_type == PROPERTY_T) || (symbol_type == INDIVIDUAL_PROPERTY_T));
+}
+
+static void mark_top_of_emitter_stack(int marker, token_data t)
+{ if (emitter_sp < 1)
+ { compiler_error("SR error: Attempt to add a marker to the top of an empty emitter stack");
+ return;
+ }
+ if (expr_trace_level >= 2)
+ { printf("Marking top of emitter stack (which is ");
+ print_operand(emitter_stack[emitter_sp-1]);
+ printf(") as ");
+ switch(marker)
+ {
+ case FUNCTION_VALUE_MARKER:
+ printf("FUNCTION");
+ break;
+ case ARGUMENT_VALUE_MARKER:
+ printf("ARGUMENT");
+ break;
+ case OR_VALUE_MARKER:
+ printf("OR_VALUE");
+ break;
+ default:
+ printf("UNKNOWN");
+ break;
+ }
+ printf("\n");
+ }
+ if (emitter_markers[emitter_sp-1])
+ { if (marker == ARGUMENT_VALUE_MARKER)
+ {
+ warning("Ignoring spurious leading comma");
+ return;
+ }
+ error_named("Missing operand for", t.text);
+ if (emitter_sp == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ emitter_markers[emitter_sp] = 0;
+ emitter_bracket_counts[emitter_sp] = 0;
+ emitter_stack[emitter_sp] = zero_operand;
+ emitter_sp++;
+ }
+ emitter_markers[emitter_sp-1] = marker;
+}
+
+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");
+ ++emitter_bracket_counts[emitter_sp-depth-1];
+}
+
+static void remove_bracket_layer_from_emitter_stack()
+{ /* Bracket layers that don't fence off any values will not have been tracked. */
+ if (emitter_sp < 2) return;
+ if (expr_trace_level >= 2)
+ printf("Removing bracket layer\n");
+ if (emitter_bracket_counts[emitter_sp-2] <= 0)
+ { compiler_error("SR error: Attempt to remove a nonexistent bracket layer from the emitter stack");
+ return;
+ }
+ --emitter_bracket_counts[emitter_sp-2];
+}
+
+static void emit_token(token_data t)
+{ assembly_operand o1, o2; int arity, stack_size, i;
+ int op_node_number, operand_node_number, previous_node_number;
+ int32 x = 0;
+
+ if (expr_trace_level >= 2)
+ { printf("Output: %-19s%21s ", t.text, "");
+ for (i=0; i<emitter_sp; i++)
+ { print_operand(emitter_stack[i]); printf(" ");
+ if (emitter_markers[i] == FUNCTION_VALUE_MARKER) printf(":FUNCTION ");
+ if (emitter_markers[i] == ARGUMENT_VALUE_MARKER) printf(":ARGUMENT ");
+ if (emitter_markers[i] == OR_VALUE_MARKER) printf(":OR ");
+ if (emitter_bracket_counts[i]) printf(":BRACKETS(%d) ", emitter_bracket_counts[i]);
+ }
+ printf("\n");
+ }
+
+ if (t.type == SUBOPEN_TT) return;
+
+ stack_size = 0;
+ while ((stack_size < emitter_sp) &&
+ !emitter_markers[emitter_sp-stack_size-1] &&
+ !emitter_bracket_counts[emitter_sp-stack_size-1])
+ stack_size++;
+
+ if (t.type == SUBCLOSE_TT)
+ { if (stack_size < emitter_sp && emitter_bracket_counts[emitter_sp-stack_size-1])
+ { if (stack_size == 0)
+ { error("No expression between brackets '(' and ')'");
+ emitter_stack[emitter_sp] = zero_operand;
+ emitter_markers[emitter_sp] = 0;
+ emitter_bracket_counts[emitter_sp] = 0;
+ ++emitter_sp;
+ }
+ else if (stack_size < 1)
+ compiler_error("SR error: emitter stack empty in subexpression");
+ else if (stack_size > 1)
+ compiler_error("SR error: emitter stack overfull in subexpression");
+ remove_bracket_layer_from_emitter_stack();
+ }
+ return;
+ }
+
+ if (t.type != OP_TT)
+ { emitter_markers[emitter_sp] = 0;
+ emitter_bracket_counts[emitter_sp] = 0;
+
+ if (emitter_sp == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ if (!evaluate_term(t, &(emitter_stack[emitter_sp++])))
+ compiler_error_named("Emit token error:", t.text);
+ return;
+ }
+
+ /* A comma is argument-separating if it follows an argument (or a function
+ call, since we ignore spurious leading commas in function argument lists)
+ with no intervening brackets. Function calls are variadic, so we don't
+ apply argument-separating commas. */
+ if (t.value == COMMA_OP &&
+ stack_size < emitter_sp &&
+ (emitter_markers[emitter_sp-stack_size-1] == ARGUMENT_VALUE_MARKER ||
+ emitter_markers[emitter_sp-stack_size-1] == FUNCTION_VALUE_MARKER) &&
+ !emitter_bracket_counts[emitter_sp-stack_size-1])
+ { if (expr_trace_level >= 2)
+ printf("Treating comma as argument-separating\n");
+ return;
+ }
+
+ if (t.value == OR_OP)
+ return;
+
+ arity = 1;
+ if (t.value == FCALL_OP)
+ { if (expr_trace_level >= 3)
+ { printf("FCALL_OP finds marker stack: ");
+ for (x=0; x<emitter_sp; x++) printf("%d ", emitter_markers[x]);
+ printf("\n");
+ }
+ if (emitter_markers[emitter_sp-1] == ARGUMENT_VALUE_MARKER)
+ warning("Ignoring spurious trailing comma");
+ while (emitter_markers[emitter_sp-arity] != FUNCTION_VALUE_MARKER)
+ {
+ if ((glulx_mode &&
+ emitter_stack[emitter_sp-arity].type == SYSFUN_OT) ||
+ (!glulx_mode &&
+ emitter_stack[emitter_sp-arity].type == VARIABLE_OT &&
+ emitter_stack[emitter_sp-arity].value >= 256 &&
+ emitter_stack[emitter_sp-arity].value < 288))
+ { int index = emitter_stack[emitter_sp-arity].value;
+ if(!glulx_mode)
+ index -= 256;
+ if(index >= 0 && index < NUMBER_SYSTEM_FUNCTIONS)
+ error_named("System function name used as a value:", system_functions.keywords[index]);
+ else
+ compiler_error("Found unnamed system function used as a value");
+ emitter_stack[emitter_sp-arity] = zero_operand;
+ }
+ ++arity;
+ }
+ }
+ else
+ { arity = 1;
+ if (operators[t.value].usage == IN_U) arity = 2;
+
+ if (operators[t.value].precedence == 3)
+ { arity = 2;
+ x = emitter_sp-1;
+ if(!emitter_markers[x] && !emitter_bracket_counts[x])
+ { for (--x; emitter_markers[x] == OR_VALUE_MARKER && !emitter_bracket_counts[x]; --x)
+ { ++arity;
+ ++stack_size;
+ }
+ for (;x >= 0 && !emitter_markers[x] && !emitter_bracket_counts[x]; --x)
+ ++stack_size;
+ }
+ }
+
+ if (arity > stack_size)
+ { error_named("Missing operand for", t.text);
+ while (arity > stack_size)
+ { if (emitter_sp == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ emitter_markers[emitter_sp] = 0;
+ emitter_bracket_counts[emitter_sp] = 0;
+ emitter_stack[emitter_sp] = zero_operand;
+ emitter_sp++;
+ stack_size++;
+ }
+ }
+ }
+
+ /* pseudo-typecheck in 6.30 */
+ for (i = 1; i <= arity; i++)
+ {
+ o1 = emitter_stack[emitter_sp - i];
+ if (is_property_t(o1.symtype) ) {
+ switch(t.value)
+ {
+ case FCALL_OP:
+ case SETEQUALS_OP: case NOTEQUAL_OP:
+ case CONDEQUALS_OP:
+ case PROVIDES_OP: case NOTPROVIDES_OP:
+ case PROP_ADD_OP: case PROP_NUM_OP:
+ case SUPERCLASS_OP:
+ case MPROP_ADD_OP: case MESSAGE_OP:
+ case PROPERTY_OP:
+ if (i < arity) break;
+ case GE_OP: case LE_OP:
+ if ((i < arity) && (o1.symflags & STAR_SFLAG)) break;
+ default:
+ warning("Property name in expression is not qualified by object");
+ }
+ } /* if (is_property_t */
+ }
+
+ switch(arity)
+ { case 1:
+ o1 = emitter_stack[emitter_sp - 1];
+ if ((o1.marker == 0) && is_constant_ot(o1.type))
+ { switch(t.value)
+ { case UNARY_MINUS_OP: x = -o1.value; goto FoldConstant;
+ case ARTNOT_OP:
+ if (!glulx_mode)
+ x = (~o1.value) & 0xffff;
+ else
+ x = (~o1.value) & 0xffffffff;
+ goto FoldConstant;
+ case LOGNOT_OP:
+ if (o1.value != 0) x=0; else x=1;
+ goto FoldConstant;
+ }
+ }
+ break;
+
+ case 2:
+ o1 = emitter_stack[emitter_sp - 2];
+ o2 = emitter_stack[emitter_sp - 1];
+
+ if ((o1.marker == 0) && (o2.marker == 0)
+ && is_constant_ot(o1.type) && is_constant_ot(o2.type))
+ {
+ int32 ov1, ov2;
+ if (glulx_mode)
+ { ov1 = o1.value;
+ ov2 = o2.value;
+ }
+ else
+ { ov1 = (o1.value >= 0x8000) ? (o1.value - 0x10000) : o1.value;
+ ov2 = (o2.value >= 0x8000) ? (o2.value - 0x10000) : o2.value;
+ }
+
+ switch(t.value)
+ {
+ case PLUS_OP: x = ov1 + ov2; goto FoldConstantC;
+ case MINUS_OP: x = ov1 - ov2; goto FoldConstantC;
+ case TIMES_OP: x = ov1 * ov2; goto FoldConstantC;
+ case DIVIDE_OP:
+ case REMAINDER_OP:
+ if (ov2 == 0)
+ error("Division of constant by zero");
+ else
+ if (t.value == DIVIDE_OP) {
+ if (ov2 < 0) {
+ ov1 = -ov1;
+ ov2 = -ov2;
+ }
+ if (ov1 >= 0)
+ x = ov1 / ov2;
+ else
+ x = -((-ov1) / ov2);
+ }
+ else {
+ if (ov2 < 0) {
+ ov2 = -ov2;
+ }
+ if (ov1 >= 0)
+ x = ov1 % ov2;
+ else
+ x = -((-ov1) % ov2);
+ }
+ goto FoldConstant;
+ case ARTAND_OP: x = o1.value & o2.value; goto FoldConstant;
+ case ARTOR_OP: x = o1.value | o2.value; goto FoldConstant;
+ case CONDEQUALS_OP:
+ if (o1.value == o2.value) x = 1; else x = 0;
+ goto FoldConstant;
+ case NOTEQUAL_OP:
+ if (o1.value != o2.value) x = 1; else x = 0;
+ goto FoldConstant;
+ case GE_OP:
+ if (o1.value >= o2.value) x = 1; else x = 0;
+ goto FoldConstant;
+ case GREATER_OP:
+ if (o1.value > o2.value) x = 1; else x = 0;
+ goto FoldConstant;
+ case LE_OP:
+ if (o1.value <= o2.value) x = 1; else x = 0;
+ goto FoldConstant;
+ case LESS_OP:
+ if (o1.value < o2.value) x = 1; else x = 0;
+ goto FoldConstant;
+ case LOGAND_OP:
+ if ((o1.value != 0) && (o2.value != 0)) x=1; else x=0;
+ goto FoldConstant;
+ case LOGOR_OP:
+ if ((o1.value != 0) || (o2.value != 0)) x=1; else x=0;
+ goto FoldConstant;
+ }
+
+ }
+ }
+
+ op_node_number = ET_used++;
+ if (op_node_number == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+
+ ET[op_node_number].operator_number = t.value;
+ ET[op_node_number].up = -1;
+ ET[op_node_number].down = -1;
+ ET[op_node_number].right = -1;
+
+ /* This statement is redundant, but prevents compilers from wrongly
+ issuing a "used before it was assigned a value" error: */
+ previous_node_number = 0;
+
+ for (i = emitter_sp-arity; i != emitter_sp; i++)
+ {
+ if (expr_trace_level >= 3)
+ printf("i=%d, emitter_sp=%d, arity=%d, ETU=%d\n",
+ i, emitter_sp, arity, ET_used);
+ if (emitter_stack[i].type == EXPRESSION_OT)
+ operand_node_number = emitter_stack[i].value;
+ else
+ { operand_node_number = ET_used++;
+ if (operand_node_number == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ ET[operand_node_number].down = -1;
+ ET[operand_node_number].value = emitter_stack[i];
+ }
+ ET[operand_node_number].up = op_node_number;
+ ET[operand_node_number].right = -1;
+ if (i == emitter_sp - arity)
+ { ET[op_node_number].down = operand_node_number;
+ }
+ else
+ { ET[previous_node_number].right = operand_node_number;
+ }
+ previous_node_number = operand_node_number;
+ }
+
+ emitter_sp = emitter_sp - arity + 1;
+
+ emitter_stack[emitter_sp - 1].type = EXPRESSION_OT;
+ emitter_stack[emitter_sp - 1].value = op_node_number;
+ emitter_stack[emitter_sp - 1].marker = 0;
+ emitter_markers[emitter_sp - 1] = 0;
+ emitter_bracket_counts[emitter_sp - 1] = 0;
+ /* Remove the marker for the brackets implied by operator precedence */
+ remove_bracket_layer_from_emitter_stack();
+
+ return;
+
+ FoldConstantC:
+
+ /* In Glulx, skip this test; we can't check out-of-range errors
+ 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;
+ switch(t.value)
+ {
+ case PLUS_OP:
+ sprintf(folding_error, "%d + %d = %d", ov1, ov2, x);
+ break;
+ case MINUS_OP:
+ sprintf(folding_error, "%d - %d = %d", ov1, ov2, x);
+ break;
+ case TIMES_OP:
+ sprintf(folding_error, "%d * %d = %d", ov1, ov2, x);
+ break;
+ }
+ error_named("Signed arithmetic on compile-time constants overflowed \
+the range -32768 to +32767:", folding_error);
+ }
+
+ FoldConstant:
+
+ if (!glulx_mode) {
+ while (x < 0) x = x + 0x10000;
+ x = x & 0xffff;
+ }
+ else {
+ x = x & 0xffffffff;
+ }
+
+ emitter_sp = emitter_sp - arity + 1;
+
+ if (!glulx_mode) {
+ if (x<256)
+ emitter_stack[emitter_sp - 1].type = SHORT_CONSTANT_OT;
+ else emitter_stack[emitter_sp - 1].type = LONG_CONSTANT_OT;
+ }
+ else {
+ if (x == 0)
+ emitter_stack[emitter_sp - 1].type = ZEROCONSTANT_OT;
+ else if (x >= -128 && x <= 127)
+ emitter_stack[emitter_sp - 1].type = BYTECONSTANT_OT;
+ else if (x >= -32768 && x <= 32767)
+ emitter_stack[emitter_sp - 1].type = HALFCONSTANT_OT;
+ else
+ emitter_stack[emitter_sp - 1].type = CONSTANT_OT;
+ }
+
+ emitter_stack[emitter_sp - 1].value = x;
+ emitter_stack[emitter_sp - 1].marker = 0;
+ emitter_markers[emitter_sp - 1] = 0;
+ emitter_bracket_counts[emitter_sp - 1] = 0;
+
+ if (expr_trace_level >= 2)
+ { printf("Folding constant to: ");
+ print_operand(emitter_stack[emitter_sp - 1]);
+ printf("\n");
+ }
+
+ /* Remove the marker for the brackets implied by operator precedence */
+ remove_bracket_layer_from_emitter_stack();
+ return;
+}
+
+/* --- Pretty printing ----------------------------------------------------- */
+
+static void show_node(int n, int depth, int annotate)
+{ int j;
+ for (j=0; j<2*depth+2; j++) printf(" ");
+
+ if (ET[n].down == -1)
+ { print_operand(ET[n].value);
+ if (annotate && (ET[n].value.marker != 0))
+ printf(" [%s]", describe_mv(ET[n].value.marker));
+ printf("\n");
+ }
+ else
+ { printf("%s ", operators[ET[n].operator_number].description);
+ j = operators[ET[n].operator_number].precedence;
+ if ((annotate) && ((j==2) || (j==3)))
+ { printf(" %d|%d ", ET[n].true_label, ET[n].false_label);
+ if (ET[n].label_after != -1) printf(" def %d after ",
+ ET[n].label_after);
+ if (ET[n].to_expression) printf(" con to expr ");
+ }
+ printf("\n");
+ show_node(ET[n].down, depth+1, 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);
+ else
+ { printf("Constant: "); print_operand(AO);
+ if (annotate && (AO.marker != 0))
+ printf(" [%s]", describe_mv(AO.marker));
+ printf("\n");
+ }
+}
+
+/* --- Lvalue transformations ---------------------------------------------- */
+
+/* This only gets called in Z-code, since Glulx doesn't distinguish
+ individual property operators from general ones. */
+static void check_property_operator(int from_node)
+{ int below = ET[from_node].down;
+ int opnum = ET[from_node].operator_number;
+
+ ASSERT_ZCODE();
+
+ if (veneer_mode) return;
+
+ if ((below != -1) && (ET[below].right != -1))
+ { int n = ET[below].right, flag = FALSE;
+
+ if ((ET[n].down == -1)
+ && ((ET[n].value.type == LONG_CONSTANT_OT)
+ || (ET[n].value.type == SHORT_CONSTANT_OT))
+ && ((ET[n].value.value > 0) && (ET[n].value.value < 64))
+ && ((!module_switch) || (ET[n].value.marker == 0)))
+ flag = TRUE;
+
+ if (!flag)
+ { switch(opnum)
+ { case PROPERTY_OP: opnum = MESSAGE_OP; break;
+ case PROP_ADD_OP: opnum = MPROP_ADD_OP; break;
+ case PROP_NUM_OP: opnum = MPROP_NUM_OP; break;
+ }
+ }
+
+ ET[from_node].operator_number = opnum;
+ }
+
+ if (below != -1)
+ check_property_operator(below);
+ if (ET[from_node].right != -1)
+ check_property_operator(ET[from_node].right);
+}
+
+static void check_lvalues(int from_node)
+{ int below = ET[from_node].down;
+ int opnum = ET[from_node].operator_number, opnum_below;
+ int lvalue_form, i, j = 0;
+
+ if (below != -1)
+ {
+ if ((opnum == FCALL_OP) && (ET[below].down != -1))
+ { opnum_below = ET[below].operator_number;
+ if ((opnum_below == PROPERTY_OP) || (opnum_below == MESSAGE_OP))
+ { i = ET[ET[from_node].down].right;
+ ET[from_node].down = ET[below].down;
+ ET[ET[below].down].up = from_node;
+ ET[ET[ET[below].down].right].up = from_node;
+ ET[ET[ET[below].down].right].right = i;
+ opnum = PROP_CALL_OP;
+ ET[from_node].operator_number = opnum;
+ }
+ }
+
+ if (operators[opnum].requires_lvalue)
+ { opnum_below = ET[below].operator_number;
+
+ if (ET[below].down == -1)
+ { if (!is_variable_ot(ET[below].value.type))
+ { error("'=' applied to undeclared variable");
+ goto LvalueError;
+ }
+ }
+ else
+ { lvalue_form=0;
+ switch(opnum)
+ { case SETEQUALS_OP:
+ switch(opnum_below)
+ { case ARROW_OP: lvalue_form = ARROW_SETEQUALS_OP; break;
+ case DARROW_OP: lvalue_form = DARROW_SETEQUALS_OP; break;
+ case MESSAGE_OP: lvalue_form = MESSAGE_SETEQUALS_OP; break;
+ case PROPERTY_OP: lvalue_form = PROPERTY_SETEQUALS_OP; break;
+ }
+ break;
+ case INC_OP:
+ switch(opnum_below)
+ { case ARROW_OP: lvalue_form = ARROW_INC_OP; break;
+ case DARROW_OP: lvalue_form = DARROW_INC_OP; break;
+ case MESSAGE_OP: lvalue_form = MESSAGE_INC_OP; break;
+ case PROPERTY_OP: lvalue_form = PROPERTY_INC_OP; break;
+ }
+ break;
+ case POST_INC_OP:
+ switch(opnum_below)
+ { case ARROW_OP: lvalue_form = ARROW_POST_INC_OP; break;
+ case DARROW_OP: lvalue_form = DARROW_POST_INC_OP; break;
+ case MESSAGE_OP: lvalue_form = MESSAGE_POST_INC_OP; break;
+ case PROPERTY_OP: lvalue_form = PROPERTY_POST_INC_OP; break;
+ }
+ break;
+ case DEC_OP:
+ switch(opnum_below)
+ { case ARROW_OP: lvalue_form = ARROW_DEC_OP; break;
+ case DARROW_OP: lvalue_form = DARROW_DEC_OP; break;
+ case MESSAGE_OP: lvalue_form = MESSAGE_DEC_OP; break;
+ case PROPERTY_OP: lvalue_form = PROPERTY_DEC_OP; break;
+ }
+ break;
+ case POST_DEC_OP:
+ switch(opnum_below)
+ { case ARROW_OP: lvalue_form = ARROW_POST_DEC_OP; break;
+ case DARROW_OP: lvalue_form = DARROW_POST_DEC_OP; break;
+ case MESSAGE_OP: lvalue_form = MESSAGE_POST_DEC_OP; break;
+ case PROPERTY_OP: lvalue_form = PROPERTY_POST_DEC_OP; break;
+ }
+ break;
+ }
+ if (lvalue_form == 0)
+ { error_named("'=' applied to",
+ (char *) operators[opnum_below].description);
+ goto LvalueError;
+ }
+
+ /* Transform from_node from_node
+ | \ | \\\ \
+ below value to value
+ | \\\
+ */
+
+ ET[from_node].operator_number = lvalue_form;
+ i = ET[below].down;
+ ET[from_node].down = i;
+ while (i != -1)
+ { ET[i].up = from_node;
+ j = i;
+ i = ET[i].right;
+ }
+ ET[j].right = ET[below].right;
+ }
+ }
+ check_lvalues(below);
+ }
+ if (ET[from_node].right != -1)
+ check_lvalues(ET[from_node].right);
+ return;
+
+ LvalueError:
+ ET[from_node].down = -1;
+ ET[from_node].value = zero_operand;
+ if (ET[from_node].right != -1)
+ check_lvalues(ET[from_node].right);
+}
+
+/* --- Tree surgery for conditionals --------------------------------------- */
+
+static void negate_condition(int n)
+{ int i;
+
+ if (ET[n].right != -1) negate_condition(ET[n].right);
+ if (ET[n].down == -1) return;
+ i = operators[ET[n].operator_number].negation;
+ if (i!=0) ET[n].operator_number = i;
+ if (operators[i].precedence==2) negate_condition(ET[n].down);
+}
+
+static void delete_negations(int n, int context)
+{
+ /* Recursively apply
+
+ ~~(x && y) = ~~x || ~~y
+ ~~(x || y) = ~~x && ~~y
+ ~~(x == y) = x ~= y
+
+ (etc) to delete the ~~ operator from the tree. Since this is
+ depth first, the ~~ being deleted has no ~~s beneath it, which
+ is important to make "negate_condition" work. */
+
+ int i;
+
+ if (ET[n].right != -1) delete_negations(ET[n].right, context);
+ if (ET[n].down == -1) return;
+ delete_negations(ET[n].down, context);
+
+ if (ET[n].operator_number == LOGNOT_OP)
+ { negate_condition(ET[n].down);
+ ET[n].operator_number
+ = ET[ET[n].down].operator_number;
+ ET[n].down = ET[ET[n].down].down;
+ i = ET[n].down;
+ while(i != -1) { ET[i].up = n; i = ET[i].right; }
+ }
+}
+
+static void insert_exp_to_cond(int n, int context)
+{
+ /* Insert a ~= test when an expression is used as a condition.
+
+ Check for possible confusion over = and ==, e.g. "if (a = 1) ..." */
+
+ int new, i;
+
+ if (ET[n].right != -1) insert_exp_to_cond(ET[n].right, context);
+
+ if (ET[n].down == -1)
+ { if (context==CONDITION_CONTEXT)
+ { new = ET_used++;
+ if (new == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ ET[new] = ET[n];
+ ET[n].down = new; ET[n].operator_number = NONZERO_OP;
+ ET[new].up = n; ET[new].right = -1;
+ }
+ return;
+ }
+
+ switch(operators[ET[n].operator_number].precedence)
+ { case 3: /* Conditionals have level 3 */
+ context = QUANTITY_CONTEXT;
+ break;
+ case 2: /* Logical operators level 2 */
+ context = CONDITION_CONTEXT;
+ break;
+ case 1: /* Forms of '=' have level 1 */
+ if (context == CONDITION_CONTEXT)
+ warning("'=' used as condition: '==' intended?");
+ default:
+ if (context != CONDITION_CONTEXT) break;
+
+ new = ET_used++;
+ if (new == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ ET[new] = ET[n];
+ ET[n].down = new; ET[n].operator_number = NONZERO_OP;
+ ET[new].up = n; ET[new].right = -1;
+
+ i = ET[new].down;
+ while (i!= -1) { ET[i].up = new; i = ET[i].right; }
+ context = QUANTITY_CONTEXT; n = new;
+ }
+
+ insert_exp_to_cond(ET[n].down, context);
+}
+
+static unsigned int etoken_num_children(int n)
+{
+ int count = 0;
+ int i;
+ i = ET[n].down;
+ if (i == -1) { return 0; }
+ do {
+ count++;
+ i = ET[i].right;
+ } while (i!=-1);
+ return count;
+}
+
+static void func_args_on_stack(int n, int context)
+{
+ /* Make sure that the arguments of every function-call expression
+ are stored to the stack. If any aren't (ie, if any arguments are
+ constants or variables), cover them with push operators.
+ (The very first argument does not need to be so treated, because
+ it's the function address, not a function argument. We also
+ skip the treatment for most system functions.) */
+
+ int new, pn, fnaddr, opnum;
+
+ ASSERT_GLULX();
+
+ if (ET[n].right != -1)
+ func_args_on_stack(ET[n].right, context);
+ if (ET[n].down == -1) {
+ pn = ET[n].up;
+ if (pn != -1) {
+ opnum = ET[pn].operator_number;
+ if (opnum == FCALL_OP
+ || opnum == MESSAGE_CALL_OP
+ || opnum == PROP_CALL_OP) {
+ /* If it's an FCALL, get the operand which contains the function
+ address (or system-function number) */
+ if (opnum == MESSAGE_CALL_OP
+ || opnum == PROP_CALL_OP
+ || ((fnaddr=ET[pn].down) != n
+ && (ET[fnaddr].value.type != SYSFUN_OT
+ || ET[fnaddr].value.value == INDIRECT_SYSF
+ || ET[fnaddr].value.value == GLK_SYSF))) {
+ if (etoken_num_children(pn) > (unsigned int)(opnum == FCALL_OP ? 4:3)) {
+ new = ET_used++;
+ if (new == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ ET[new] = ET[n];
+ ET[n].down = new;
+ ET[n].operator_number = PUSH_OP;
+ ET[new].up = n;
+ ET[new].right = -1;
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ func_args_on_stack(ET[n].down, context);
+}
+
+static assembly_operand check_conditions(assembly_operand AO, int context)
+{ int n;
+
+ if (AO.type != EXPRESSION_OT)
+ { if (context != CONDITION_CONTEXT) return AO;
+ n = ET_used++;
+ if (n == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ ET[n].down = -1;
+ ET[n].up = -1;
+ ET[n].right = -1;
+ ET[n].value = AO;
+ INITAOT(&AO, EXPRESSION_OT);
+ AO.value = n;
+ }
+
+ insert_exp_to_cond(AO.value, context);
+ delete_negations(AO.value, context);
+
+ if (glulx_mode)
+ func_args_on_stack(AO.value, context);
+
+ return AO;
+}
+
+/* --- Shift-reduce parser ------------------------------------------------- */
+
+static int sr_sp;
+static token_data *sr_stack;
+
+extern assembly_operand parse_expression(int context)
+{
+ /* Parses an expression, evaluating it as a constant if possible.
+
+ Possible contexts are:
+
+ VOID_CONTEXT the expression is used as a statement, so that
+ its value will be thrown away and it only
+ needs to exist for any resulting side-effects
+ (function calls and assignments)
+
+ CONDITION_CONTEXT the result must be a condition
+
+ CONSTANT_CONTEXT there is required to be a constant result
+ (so that, for instance, comma becomes illegal)
+
+ QUANTITY_CONTEXT the default: a quantity is to be specified
+
+ ACTION_Q_CONTEXT like QUANTITY_CONTEXT, but postfixed brackets
+ at the top level do not indicate function call:
+ used for e.g.
+ <Insert button (random(pocket1, pocket2))>
+
+ RETURN_Q_CONTEXT like QUANTITY_CONTEXT, but a single property
+ name does not generate a warning
+
+ ASSEMBLY_CONTEXT a quantity which cannot use the '->' operator
+ (needed for assembly language to indicate
+ store destinations)
+
+ FORINIT_CONTEXT a quantity which cannot use an (unbracketed)
+ '::' operator
+
+ ARRAY_CONTEXT like CONSTANT_CONTEXT, but where an unbracketed
+ minus sign is ambiguous, and brackets always
+ indicate subexpressions, not function calls
+
+ Return value: an assembly operand.
+
+ If the type is OMITTED_OT, then the expression has no resulting value.
+
+ If the type is EXPRESSION_OT, then the value will need to be
+ calculated at run-time by code compiled from the expression tree
+ whose root node-number is the operand value.
+
+ Otherwise the assembly operand is the value of the expression, which
+ 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.
+ */
+
+ token_data a, b, pop; int i;
+ assembly_operand AO;
+
+ superclass_allowed = (context != FORINIT_CONTEXT);
+ if (context == FORINIT_CONTEXT) context = VOID_CONTEXT;
+
+ comma_allowed = (context == VOID_CONTEXT);
+ arrow_allowed = (context != ASSEMBLY_CONTEXT);
+ bare_prop_allowed = (context == RETURN_Q_CONTEXT);
+ array_init_ambiguity = ((context == ARRAY_CONTEXT) ||
+ (context == ASSEMBLY_CONTEXT));
+
+ action_ambiguity = (context == ACTION_Q_CONTEXT);
+
+ if (context == ASSEMBLY_CONTEXT) context = QUANTITY_CONTEXT;
+ if (context == ACTION_Q_CONTEXT) context = QUANTITY_CONTEXT;
+ if (context == RETURN_Q_CONTEXT) context = QUANTITY_CONTEXT;
+ if (context == ARRAY_CONTEXT) context = CONSTANT_CONTEXT;
+
+ etoken_count = 0;
+ inserting_token = FALSE;
+
+ emitter_sp = 0;
+ bracket_level = 0;
+
+ previous_token.text = "$";
+ previous_token.type = ENDEXP_TT;
+ previous_token.value = 0;
+
+ sr_sp = 1;
+ sr_stack[0] = previous_token;
+
+ AO = zero_operand;
+
+ statements.enabled = FALSE;
+ directives.enabled = FALSE;
+
+ if (get_next_etoken() == FALSE)
+ { ebf_error("expression", token_text);
+ return AO;
+ }
+
+ do
+ { if (expr_trace_level >= 2)
+ { printf("Input: %-20s", current_token.text);
+ for (i=0; i<sr_sp; i++) printf("%s ", sr_stack[i].text);
+ printf("\n");
+ }
+ if (expr_trace_level >= 3) printf("ET_used = %d\n", ET_used);
+
+ if (sr_sp == 0)
+ { compiler_error("SR error: stack empty");
+ return(AO);
+ }
+
+ a = sr_stack[sr_sp-1]; b = current_token;
+
+ if ((a.type == ENDEXP_TT) && (b.type == ENDEXP_TT))
+ { if (emitter_sp == 0)
+ { error("No expression between brackets '(' and ')'");
+ put_token_back();
+ return AO;
+ }
+ if (emitter_sp > 1)
+ { compiler_error("SR error: emitter stack overfull");
+ return AO;
+ }
+
+ AO = emitter_stack[0];
+ if (AO.type == EXPRESSION_OT)
+ { if (expr_trace_level >= 3)
+ { printf("Tree before lvalue checking:\n");
+ show_tree(AO, FALSE);
+ }
+ if (!glulx_mode)
+ check_property_operator(AO.value);
+ check_lvalues(AO.value);
+ ET[AO.value].up = -1;
+ }
+ else {
+ if ((context != CONSTANT_CONTEXT) && is_property_t(AO.symtype)
+ && (arrow_allowed) && (!bare_prop_allowed))
+ warning("Bare property name found. \"self.prop\" intended?");
+ }
+
+ check_conditions(AO, context);
+
+ if (context == CONSTANT_CONTEXT)
+ if (!is_constant_ot(AO.type))
+ { AO = zero_operand;
+ ebf_error("constant", "<expression>");
+ }
+ put_token_back();
+
+ return(AO);
+ }
+
+ switch(find_prec(a,b))
+ {
+ case e5: /* Associativity error */
+ error_named("Brackets mandatory to clarify order of:",
+ a.text);
+
+ case LOWER_P:
+ case EQUAL_P:
+ if (sr_sp == MAX_EXPRESSION_NODES)
+ memoryerror("MAX_EXPRESSION_NODES", MAX_EXPRESSION_NODES);
+ sr_stack[sr_sp++] = b;
+ switch(b.type)
+ {
+ case SUBOPEN_TT:
+ if (sr_sp >= 2 && sr_stack[sr_sp-2].type == OP_TT && sr_stack[sr_sp-2].value == FCALL_OP)
+ mark_top_of_emitter_stack(FUNCTION_VALUE_MARKER, b);
+ else
+ add_bracket_layer_to_emitter_stack(0);
+ break;
+ case OP_TT:
+ switch(b.value){
+ case OR_OP:
+ if (sr_stack[sr_sp-2].type == OP_TT &&
+ operators[sr_stack[sr_sp-2].value].precedence == 3)
+ mark_top_of_emitter_stack(OR_VALUE_MARKER, b);
+ else
+ { error("'or' not between values to the right of a condition");
+ /* Convert to + for error recovery purposes */
+ sr_stack[sr_sp-1].value = PLUS_OP;
+ }
+ break;
+ case COMMA_OP:
+ {
+ /* A comma separates arguments only if the shallowest open bracket belongs to a function call. */
+ int shallowest_open_bracket_index = sr_sp - 2;
+ while (shallowest_open_bracket_index > 0 && sr_stack[shallowest_open_bracket_index].type != SUBOPEN_TT)
+ --shallowest_open_bracket_index;
+ if (shallowest_open_bracket_index > 0 &&
+ sr_stack[shallowest_open_bracket_index-1].type == OP_TT &&
+ sr_stack[shallowest_open_bracket_index-1].value == FCALL_OP)
+ { mark_top_of_emitter_stack(ARGUMENT_VALUE_MARKER, b);
+ break;
+ }
+ /* Non-argument-separating commas get treated like any other operator; we fall through to the default case. */
+ }
+ default:
+ {
+ /* Add a marker for the brackets implied by operator precedence */
+ int operands_on_left = (operators[b.value].usage == PRE_U) ? 0 : 1;
+ add_bracket_layer_to_emitter_stack(operands_on_left);
+ }
+ }
+ }
+ get_next_etoken();
+ break;
+ case GREATER_P:
+ do
+ { pop = sr_stack[sr_sp - 1];
+ emit_token(pop);
+ sr_sp--;
+ } while (find_prec(sr_stack[sr_sp-1], pop) != LOWER_P);
+ break;
+
+ case e1: /* Missing operand error */
+ error_named("Missing operand after", a.text);
+ put_token_back();
+ current_token.type = NUMBER_TT;
+ current_token.value = 0;
+ current_token.marker = 0;
+ current_token.text = "0";
+ break;
+
+ case e2: /* Unexpected close bracket */
+ error("Found '(' without matching ')'");
+ get_next_etoken();
+ break;
+
+ case e3: /* Missing operator error */
+ error("Missing operator: inserting '+'");
+ put_token_back();
+ current_token.type = OP_TT;
+ current_token.value = PLUS_OP;
+ current_token.marker = 0;
+ current_token.text = "+";
+ break;
+
+ case e4: /* Expression ends with an open bracket */
+ error("Found '(' without matching ')'");
+ sr_sp--;
+ break;
+
+ }
+ }
+ while (TRUE);
+}
+
+/* --- Test for simple ++ or -- usage: used to optimise "for" loop code ---- */
+
+extern int test_for_incdec(assembly_operand AO)
+{ int s = 0;
+ if (AO.type != EXPRESSION_OT) return 0;
+ if (ET[AO.value].down == -1) return 0;
+ switch(ET[AO.value].operator_number)
+ { case INC_OP: s = 1; break;
+ case POST_INC_OP: s = 1; break;
+ case DEC_OP: s = -1; break;
+ case POST_DEC_OP: s = -1; break;
+ }
+ if (s==0) return 0;
+ if (ET[ET[AO.value].down].down != -1) return 0;
+ if (!is_variable_ot(ET[ET[AO.value].down].value.type)) return 0;
+ return s*(ET[ET[AO.value].down].value.value);
+}
+
+/* ========================================================================= */
+/* Data structure management routines */
+/* ------------------------------------------------------------------------- */
+
+extern void init_expressp_vars(void)
+{ int i;
+ /* make_operands(); */
+ make_lexical_interface_tables();
+ for (i=0;i<32;i++) system_function_usage[i] = 0;
+}
+
+extern void expressp_begin_pass(void)
+{
+}
+
+extern void expressp_allocate_arrays(void)
+{ ET = my_calloc(sizeof(expression_tree_node), MAX_EXPRESSION_NODES,
+ "expression parse trees");
+ emitter_markers = my_calloc(sizeof(int), MAX_EXPRESSION_NODES,
+ "emitter markers");
+ emitter_bracket_counts = my_calloc(sizeof(int), MAX_EXPRESSION_NODES,
+ "emitter bracket layer counts");
+ emitter_stack = my_calloc(sizeof(assembly_operand), MAX_EXPRESSION_NODES,
+ "emitter stack");
+ sr_stack = my_calloc(sizeof(token_data), MAX_EXPRESSION_NODES,
+ "shift-reduce parser stack");
+}
+
+extern void expressp_free_arrays(void)
+{ my_free(&ET, "expression parse trees");
+ my_free(&emitter_markers, "emitter markers");
+ my_free(&emitter_bracket_counts, "emitter bracket layer counts");
+ my_free(&emitter_stack, "emitter stack");
+ my_free(&sr_stack, "shift-reduce parser stack");
+}
+
+/* ========================================================================= */