Skip to content

Commit 1768f5b

Browse files
authored
Handle syntax checking and caching for nodes with dynamic values (#4460)
1 parent 9e6790f commit 1768f5b

File tree

2 files changed

+108
-26
lines changed

2 files changed

+108
-26
lines changed

code/parse/sexp.cpp

Lines changed: 107 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -991,8 +991,12 @@ bool ship_class_unchanged(const ship_registry_entry *ship_entry);
991991
void multi_sexp_modify_variable();
992992

993993
// jg18
994-
// container_value_index is for using one specific value from acontainer
994+
// container_value_index is for using one specific value from a container
995995
int copy_node_to_replacement_args(int node, int container_value_index = -1);
996+
// checks whether the node's value can change over the course of the mission
997+
bool is_node_value_dynamic(int node);
998+
// returns 0 on success or a SEXP_CHECK_* value on failure
999+
int check_dynamic_value_node_type(int node, bool is_string, bool is_number);
9961000

9971001
#define NO_OPERATOR_INDEX_DEFINED -2
9981002
#define NOT_A_SEXP_OPERATOR -1
@@ -2464,9 +2468,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
24642468
}
24652469
Assert(ship_node >= 0);
24662470

2467-
// we can't check special-arg ships
2468-
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
2469-
break;
2471+
if (is_node_value_dynamic(ship_node)) {
2472+
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
2473+
if (dyn_val_check) {
2474+
return dyn_val_check;
2475+
} else {
2476+
// since the value changes at runtime
2477+
// we can check only the type
2478+
break;
2479+
}
2480+
}
24702481

24712482
auto shipname = CTEXT(ship_node);
24722483
shipnum = ship_name_lookup(shipname, 1);
@@ -2551,9 +2562,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
25512562

25522563
ship_node = CDR(op_node);
25532564

2554-
// we can't check special-arg ships
2555-
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
2556-
break;
2565+
if (is_node_value_dynamic(ship_node)) {
2566+
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
2567+
if (dyn_val_check) {
2568+
return dyn_val_check;
2569+
} else {
2570+
// since the value changes at runtime
2571+
// we can check only the type
2572+
break;
2573+
}
2574+
}
25572575

25582576
auto shipname = CTEXT(ship_node);
25592577
shipnum = ship_name_lookup(shipname, 1);
@@ -2920,9 +2938,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
29202938
auto ship_node = Sexp_nodes[op_node].rest;
29212939
Assert(ship_node >= 0);
29222940

2923-
// we can't check special-arg ships
2924-
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
2925-
break;
2941+
if (is_node_value_dynamic(ship_node)) {
2942+
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
2943+
if (dyn_val_check) {
2944+
return dyn_val_check;
2945+
} else {
2946+
// since the value changes at runtime
2947+
// we can check only the type
2948+
break;
2949+
}
2950+
}
29262951

29272952
ship_num = ship_name_lookup(CTEXT(ship_node), 1); // Goober5000 - include players
29282953
if (ship_num < 0) {
@@ -3060,9 +3085,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
30603085
z = Sexp_nodes[z].rest; // first argument of operator should be mission name
30613086
Assert(z >= 0);
30623087

3063-
// can't check special-arg
3064-
if (Sexp_nodes[z].flags & SNF_SPECIAL_ARG_IN_NODE)
3065-
break;
3088+
if (is_node_value_dynamic(z)) {
3089+
const int dyn_val_check = check_dynamic_value_node_type(z, true, false);
3090+
if (dyn_val_check) {
3091+
return dyn_val_check;
3092+
} else {
3093+
// since the value changes at runtime
3094+
// we can check only the type
3095+
break;
3096+
}
3097+
}
30663098

30673099
// look for mission
30683100
count = count_items_with_name(CTEXT(z), Campaign.missions, Campaign.num_missions);
@@ -3145,9 +3177,16 @@ int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, i
31453177
}
31463178
Assert(ship_node >= 0);
31473179

3148-
// can't check special-arg
3149-
if (Sexp_nodes[ship_node].flags & SNF_SPECIAL_ARG_IN_NODE)
3150-
break;
3180+
if (is_node_value_dynamic(ship_node)) {
3181+
const int dyn_val_check = check_dynamic_value_node_type(ship_node, true, false);
3182+
if (dyn_val_check) {
3183+
return dyn_val_check;
3184+
} else {
3185+
// since the value changes at runtime
3186+
// we can check only the type
3187+
break;
3188+
}
3189+
}
31513190

31523191
// look for the ship that has this dockpoint
31533192
ship_num = ship_name_lookup(CTEXT(ship_node), 1);
@@ -4904,8 +4943,8 @@ const ship_registry_entry *eval_ship(int node)
49044943
auto ship_it = Ship_registry_map.find(CTEXT(node));
49054944
if (ship_it != Ship_registry_map.end())
49064945
{
4907-
// cache the value, unless this node is a variable or argument because the value may change
4908-
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE))
4946+
// cache the value if it can't change later
4947+
if (!is_node_value_dynamic(node))
49094948
Sexp_nodes[node].cache = new sexp_cached_data(OPF_SHIP, -1, ship_it->second);
49104949

49114950
return &Ship_registry[ship_it->second];
@@ -4947,8 +4986,8 @@ wing *eval_wing(int node)
49474986
{
49484987
auto wingp = &Wings[wing_num];
49494988

4950-
// cache the value, unless this node is a variable or argument because the value may change
4951-
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE))
4989+
// cache the value if it can't change later
4990+
if (!is_node_value_dynamic(node))
49524991
Sexp_nodes[node].cache = new sexp_cached_data(OPF_WING, wingp);
49534992

49544993
return wingp;
@@ -4989,10 +5028,8 @@ int sexp_atoi(int node)
49895028

49905029
int num = atoi(CTEXT(node));
49915030

4992-
// cache the value, unless this node is a variable, argument, or container data, because the value may change
4993-
if (!(Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) &&
4994-
!(Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) &&
4995-
(Sexp_nodes[node].subtype != SEXP_ATOM_CONTAINER_DATA))
5031+
// cache the value if it can't change later
5032+
if (!is_node_value_dynamic(node))
49965033
Sexp_nodes[node].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
49975034

49985035
return num;
@@ -22056,8 +22093,8 @@ int sexp_string_to_int(int n)
2205622093

2205722094
int num = atoi(buf);
2205822095

22059-
// cache the value, unless this node is a variable or argument because the value may change
22060-
if (!(Sexp_nodes[n].type & SEXP_FLAG_VARIABLE) && !(Sexp_nodes[n].flags & SNF_SPECIAL_ARG_IN_NODE))
22096+
// cache the value if it can't change later
22097+
if (!is_node_value_dynamic(n))
2206122098
Sexp_nodes[n].cache = new sexp_cached_data(OPF_NUMBER, num, -1);
2206222099

2206322100
return num;
@@ -31336,6 +31373,9 @@ const char *sexp_error_message(int num)
3133631373
case SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE:
3133731374
return "Wrong container data type";
3133831375

31376+
case SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE:
31377+
return "Invalid special argument type";
31378+
3133931379
default:
3134031380
Warning(LOCATION, "Unhandled sexp error code %d!", num);
3134131381
return "Unhandled sexp error code!";
@@ -31666,6 +31706,47 @@ int copy_node_to_replacement_args(int node, int container_value_index)
3166631706
return num_args;
3166731707
}
3166831708

31709+
bool is_node_value_dynamic(int node)
31710+
{
31711+
Assertion(node >= 0, "Attempt to check whether a non-existent node has a dynamic value. Please report!");
31712+
31713+
return (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) || (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) ||
31714+
(Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA);
31715+
}
31716+
31717+
int check_dynamic_value_node_type(int node, bool is_string, bool is_number)
31718+
{
31719+
Assertion(is_node_value_dynamic(node),
31720+
"Attempt to check dynamic value type of a node that isn't a dynamic value. Please report!");
31721+
31722+
if (Sexp_nodes[node].type & SEXP_FLAG_VARIABLE) {
31723+
const int var_index = sexp_get_variable_index(node);
31724+
Assertion(var_index != -1,
31725+
"Attempt to get index of invalid variable %s. Please report!",
31726+
Sexp_nodes[node].text);
31727+
const int var_type = Sexp_variables[var_index].type;
31728+
if (((var_type & SEXP_VARIABLE_STRING) && !is_string) || ((var_type & SEXP_VARIABLE_NUMBER) && !is_number)) {
31729+
return SEXP_CHECK_INVALID_VARIABLE_TYPE;
31730+
}
31731+
} else if (Sexp_nodes[node].flags & SNF_SPECIAL_ARG_IN_NODE) {
31732+
if (((Sexp_nodes[node].subtype == SEXP_ATOM_STRING) && !is_string) ||
31733+
((Sexp_nodes[node].subtype == SEXP_ATOM_NUMBER) && !is_number)) {
31734+
return SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE;
31735+
}
31736+
} else if (Sexp_nodes[node].subtype == SEXP_ATOM_CONTAINER_DATA) {
31737+
const auto *p_container = get_sexp_container(Sexp_nodes[node].text);
31738+
Assertion(p_container,
31739+
"Attempt to check dynamic value node type of data of nonexistent container %s. Please report!",
31740+
Sexp_nodes[node].text);
31741+
if (!check_container_data_sexp_arg_type(p_container->type, is_string, is_number)) {
31742+
return SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE;
31743+
}
31744+
} else {
31745+
UNREACHABLE("Unhandled dynamic value node %s. Please report!", Sexp_nodes[node].text);
31746+
}
31747+
31748+
return 0;
31749+
}
3166931750

3167031751
void sexp_modify_variable(int n)
3167131752
{

code/parse/sexp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,7 @@ const char *CTEXT(int n);
11171117
#define SEXP_CHECK_WRONG_CONTAINER_TYPE -170
11181118
#define SEXP_CHECK_INVALID_ANIMATION -171
11191119
#define SEXP_CHECK_WRONG_CONTAINER_DATA_TYPE -172
1120+
#define SEXP_CHECK_INVALID_SPECIAL_ARG_TYPE -173
11201121

11211122

11221123
#define TRAINING_CONTEXT_SPEED (1<<0)

0 commit comments

Comments
 (0)