Support for nested expressions (#616)

- Doesn't require any new opcode
- temp_array is repurposed as sub-expression terminator
- Accompanied by sslc update
- Backwards compatible with old scripts using single-layer expressions
This commit is contained in:
phobos2077
2026-03-15 12:47:12 +01:00
parent 41fd1e3495
commit 168687c9ee
6 changed files with 61 additions and 21 deletions
+1 -2
View File
@@ -757,7 +757,7 @@ end
procedure debug_array_str_deep(variable arr, variable levels, variable prefix := false) begin
#define _newline if (levels > 1) then s += "\n";
#define _indent ii := 0; while (ii < levels - 1) do begin s += " "; ii++; end
#define _value(v) (v if (levels <= 1 or not array_exists(v)) else debug_array_str_deep(v, levels - 1))
#define _value(v) (v if (levels <= 1 or typeof(v) != VALTYPE_INT or not array_exists(v)) else debug_array_str_deep(v, levels - 1))
variable i := 0, ii, k, v, s, len;
len := len_array(arr);
if (array_is_map(arr)) then begin // print assoc array
@@ -776,7 +776,6 @@ procedure debug_array_str_deep(variable arr, variable levels, variable prefix :=
s += "}";
end else begin // print list
s := ("List("+len+"): [") if prefix else "[";
_newline
while i < len do begin
_newline
v := get_array(arr, i);
+39 -10
View File
@@ -45,7 +45,9 @@ ArrayKeysMap savedArrays;
// auto-incremented ID
DWORD nextArrayID = 1;
// special array ID for array expressions, contains the ID number of the currently created array
DWORD stackArrayId;
DWORD expressionArrayId;
// special stack for array expressions, contains ID numbers of the currently created arrays
std::vector<DWORD> arrayExpressionStack;
static char get_all_arrays_special_key[] = "...all_arrays...";
@@ -429,7 +431,19 @@ DWORD CreateArray(int len, DWORD flags) {
// var.key = sArrayElement(nextArrayID, DataType::INT);
// savedArrays[var.key] = nextArrayID;
//}
stackArrayId = nextArrayID;
if ((flags & ARRAYFLAG_EXPR_PUSH) != 0) {
// When creating array for sub-expression, make sure to add array for base expression to stack
// This is messy, but required to support older scripts:
// - We must always assign expressionArrayId for one-layer expressions from older scripts to work like they did before
// - We can't directly push first arrayID into stack b/c no way to distinguish between start of an expression and normal temp_array call
// - Compiler will only add this flag for temp_array call generated from a sub-expression
// - So only on this second call we know we are in expression and expressionArrayId definitely contains arrayId of the first layer
if (arrayExpressionStack.empty() && expressionArrayId != 0) {
arrayExpressionStack.push_back(expressionArrayId);
}
arrayExpressionStack.push_back(nextArrayID);
}
expressionArrayId = nextArrayID;
arrays[nextArrayID] = var;
return nextArrayID++;
}
@@ -757,16 +771,31 @@ void SaveArray(const ScriptValue& key, DWORD id) {
Should always return 0!
*/
long StackArray(const ScriptValue& key, const ScriptValue& val) {
if (stackArrayId == 0 || !ArrayExists(stackArrayId)) return 0;
void SetArrayFromExpression(const ScriptValue& key, const ScriptValue& val) {
DWORD arrayId = !arrayExpressionStack.empty()
? arrayExpressionStack.back()
: expressionArrayId;
if (!arrays[stackArrayId].isAssoc()) { // automatically resize array to fit one new element
size_t size = arrays[stackArrayId].val.size();
if (size >= ARRAY_MAX_SIZE) return 0;
if (key.rawValue() >= size) arrays[stackArrayId].val.resize(size + 1);
if (arrayId == 0 || !ArrayExists(arrayId)) return;
if (!arrays[arrayId].isAssoc()) { // automatically resize array to fit one new element
size_t size = arrays[arrayId].val.size();
if (size >= ARRAY_MAX_SIZE) return;
if (key.rawValue() >= size) arrays[arrayId].val.resize(size + 1);
}
SetArray(arrayId, key, val, false);
}
void PopExpressionArray() {
if (arrayExpressionStack.empty()) return;
arrayExpressionStack.pop_back();
// Reversing the hack from CreateArray
if (arrayExpressionStack.size() == 1) {
expressionArrayId = arrayExpressionStack.back();
arrayExpressionStack.pop_back();
}
SetArray(stackArrayId, key, val, false);
return 0;
}
sArrayVar* GetRawArray(DWORD id) {
+7 -2
View File
@@ -109,6 +109,8 @@ struct sArrayVarOld
#define ARRAYFLAG_ASSOC (1) // is map
#define ARRAYFLAG_CONSTVAL (2) // don't update value of key if the key exists in map
#define ARRAYFLAG_RESERVED (4)
#define ARRAYFLAG_EXPR_PUSH (32) // is created as part of array sub-expression
#define ARRAYFLAG_EXPR_POP (64) // is used to indicate end of array sub-expression, not used in actual array
typedef std::unordered_map<sArrayElement, DWORD, sArrayElement_HashFunc, sArrayElement_EqualFunc> ArrayKeysMap;
@@ -224,8 +226,11 @@ DWORD LoadArray(const ScriptValue& key);
// make array saved into the savegame with associated key
void SaveArray(const ScriptValue& key, DWORD id);
// special function that powers array expressions
long StackArray(const ScriptValue& key, const ScriptValue& val);
// sets array element from array expression
void SetArrayFromExpression(const ScriptValue& key, const ScriptValue& val);
// used to indicate the end of array sub-expression
void PopExpressionArray();
sArrayVar* GetRawArray(DWORD id);
+12 -5
View File
@@ -90,7 +90,15 @@ void op_resize_array(OpcodeContext& ctx) {
}
void op_temp_array(OpcodeContext& ctx) {
auto arrayId = CreateTempArray(ctx.arg(0).rawValue(), ctx.arg(1).rawValue());
const auto& flags = ctx.arg(1);
// Special case for array sub-expressions.
if ((flags.rawValue() & ARRAYFLAG_EXPR_POP) != 0) {
PopExpressionArray();
ctx.setReturn(0);
return;
}
auto arrayId = CreateTempArray(ctx.arg(0).rawValue(), flags.rawValue());
ctx.setReturn(arrayId);
}
@@ -120,10 +128,9 @@ void op_get_array_key(OpcodeContext& ctx) {
);
}
void op_stack_array(OpcodeContext& ctx) {
ctx.setReturn(
StackArray(ctx.arg(0), ctx.arg(1))
);
void op_arrayexpr(OpcodeContext& ctx) {
SetArrayFromExpression(ctx.arg(0), ctx.arg(1));
ctx.setReturn(0);
}
// object LISTS
+1 -1
View File
@@ -51,7 +51,7 @@ void op_load_array(OpcodeContext&);
void op_get_array_key(OpcodeContext&);
void op_stack_array(OpcodeContext&);
void op_arrayexpr(OpcodeContext&);
void op_list_begin(OpcodeContext&);
+1 -1
View File
@@ -195,7 +195,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = {
{0x254, "save_array", op_save_array, 2, false, 0, {ARG_ANY, ARG_OBJECT}},
{0x255, "load_array", op_load_array, 1, true, -1, {ARG_INTSTR}},
{0x256, "array_key", op_get_array_key, 2, true, 0, {ARG_INT, ARG_INT}},
{0x257, "arrayexpr", op_stack_array, 2, true},
{0x257, "arrayexpr", op_arrayexpr, 2, true},
// 0x258 // RESERVED for arrays
// 0x259 // RESERVED for arrays
{0x25a, "reg_anim_destroy", op_reg_anim_destroy, 1, false, 0, {ARG_OBJECT}},