Skip to content

Commit 07199cb

Browse files
committed
convert rest of tests to rhai
1 parent 002825c commit 07199cb

8 files changed

+387
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
// perfect application of AI
2+
// helps proompting LLMs to do good for this project
3+
4+
# Convert lua to rhai script
5+
Convert the current test to a rhai test. below are examples and instructions on the conversions necessary:
6+
7+
## Dynamic function calls
8+
Functions which are not native to rhai MUST be explicitly called using the below syntax:
9+
10+
```rhai
11+
type.function.call(arguments...)
12+
```
13+
14+
native rhai functions MUST NOT be converted using the syntax above.
15+
16+
Below is a list of some of the native functions their argument names and some constants available in rhai:
17+
18+
```csv
19+
function,arguments
20+
is_odd,
21+
is_even,
22+
min,"a, b"
23+
max,"a, b"
24+
to_float,
25+
to_decimal,
26+
abs,value
27+
sign,value
28+
is_zero,
29+
sin,angle
30+
cos,angle
31+
tan,angle
32+
sinh,angle
33+
cosh,angle
34+
tanh,angle
35+
hypot,"x, y"
36+
asin,value
37+
acos,value
38+
atan,value
39+
atan,"x, y"
40+
asinh,value
41+
acosh,value
42+
atanh,value
43+
sqrt,value
44+
exp,value
45+
ln,value
46+
log,value
47+
floor,
48+
ceiling,
49+
round,
50+
round,decimal_points
51+
int,
52+
fraction,
53+
round_up,decimal_points
54+
round_down,decimal_points
55+
round_half_up,decimal_points
56+
round_half_down,decimal_points
57+
to_int,
58+
to_decimal,
59+
to_float,
60+
to_degrees,
61+
to_radians,
62+
is_nan,
63+
is_finite,
64+
is_infinite,
65+
parse_int,"string, [radix]"
66+
parse_float,string
67+
parse_decimal,string
68+
to_binary,value
69+
to_octal,value
70+
to_hex,value
71+
PI,
72+
E,
73+
```
74+
75+
## Operators
76+
Operators are different in lua and rhai, below is a list of operators supported:
77+
78+
```
79+
Operators Assignment operators Supported types
80+
(see standard types)
81+
+, +=
82+
83+
INT
84+
FLOAT (if not no_float)
85+
Decimal (requires decimal)
86+
char
87+
string
88+
89+
-, *, /, %, **, -=, *=, /=, %=, **=
90+
91+
INT
92+
FLOAT (if not no_float)
93+
Decimal (requires decimal)
94+
95+
<<, >> <<=, >>=
96+
97+
INT
98+
99+
&, |, ^ &=, |=, ^=
100+
101+
INT (bit-wise)
102+
bool (non-short-circuiting)
103+
104+
&&, ||
105+
106+
bool (short-circuits)
107+
108+
==, !=
109+
110+
INT
111+
FLOAT (if not no_float)
112+
Decimal (requires decimal)
113+
bool
114+
char
115+
string
116+
BLOB
117+
numeric range
118+
()
119+
120+
>, >=, <, <=
121+
122+
INT
123+
FLOAT (if not no_float)
124+
Decimal (requires decimal)
125+
char
126+
string
127+
()
128+
```
129+
130+
## Function syntax
131+
Functions in rhai look like this:
132+
133+
```rhai
134+
fn function_name(arg1, arg2) {
135+
return value;
136+
}
137+
```
138+
139+
## Semicolons
140+
Every statement must end in a semicolon
141+
142+
143+
Below is a new section on Rhai strings that you can add to the prompt:
144+
145+
## Rhai Strings
146+
147+
Rhai supports different string types such as raw strings (enclosed by matching `#` and double-quotes), multi-line literal strings (enclosed by backticks to preserve exact formatting), and strings with interpolation (using `${…}` inside multi-line literals). These variants allow you to easily include complex content like newlines, quotes, and even embedded expressions while keeping the original formatting. Here are three examples:
148+
149+
````rhai
150+
// Raw string example:
151+
let raw_str = #"Hello, raw string! \n No escape sequences here."#;
152+
````
153+
154+
````rhai
155+
// Multi-line literal string example:
156+
let multi_line = `
157+
This is a multi-line literal string,
158+
which preserves whitespaces, newlines, and "quotes" exactly.
159+
`;
160+
````
161+
162+
````rhai
163+
// String interpolation example:
164+
let value = 42;
165+
let interpolated = `The answer is ${value}, which is computed dynamically.`;
166+
````
167+
168+
## Null Checks
169+
null checks can be performed by checking `type_of(value) == "()"`
170+
171+
## Examples
172+
Below is an example lua test and its equivalent rhai script:
173+
174+
### Lua
175+
```lua
176+
local entity_a = world.spawn()
177+
local entity_b = world.spawn()
178+
local entity_c = world.spawn()
179+
local entity_d = world._get_entity_with_test_component("CompWithFromWorldAndComponentData")
180+
181+
local component_with = world.get_type_by_name("CompWithFromWorldAndComponentData")
182+
local component_without = world.get_type_by_name("CompWithDefaultAndComponentData")
183+
184+
world.add_default_component(entity_a, component_with)
185+
world.add_default_component(entity_b, component_with)
186+
world.add_default_component(entity_c, component_with)
187+
188+
world.add_default_component(entity_b, component_without)
189+
190+
local found_entities = {}
191+
for i,result in pairs(world.query():component(component_with):without(component_without):build()) do
192+
table.insert(found_entities, result:entity())
193+
end
194+
195+
assert(#found_entities == 3, "Expected 3 entities, got " .. #found_entities)
196+
197+
expected_entities = {
198+
entity_c,
199+
entity_d,
200+
entity_a,
201+
}
202+
203+
for i, entity in ipairs(found_entities) do
204+
assert(entity:index() == expected_entities[i]:index(), "Expected entity " .. expected_entities[i]:index() .. " but got " .. entity:index())
205+
end
206+
```
207+
208+
### Rhai
209+
```rhai
210+
let entity_a = world.spawn_.call();
211+
let entity_b = world.spawn_.call();
212+
let entity_c = world.spawn_.call();
213+
let entity_d = world._get_entity_with_test_component.call("CompWithFromWorldAndComponentData");
214+
215+
let component_with = world.get_type_by_name.call("CompWithFromWorldAndComponentData");
216+
let component_without = world.get_type_by_name.call("CompWithDefaultAndComponentData");
217+
218+
world.add_default_component.call(entity_a, component_with);
219+
world.add_default_component.call(entity_b, component_with);
220+
world.add_default_component.call(entity_c, component_with);
221+
222+
world.add_default_component.call(entity_b, component_without);
223+
224+
let found_entities = [];
225+
for (result, i) in world.query.call().component.call(component_with).without.call(component_without).build.call() {
226+
found_entities.push(result.entity.call());
227+
}
228+
229+
assert(found_entities.len == 3, "Expected 3 entities, got " + found_entities.len);
230+
231+
let expected_entities = [
232+
entity_d,
233+
entity_a,
234+
entity_c,
235+
];
236+
237+
for (entity, i) in found_entities {
238+
assert(entity.index.call() == expected_entities[i].index.call(), "Expected entity " + expected_entities[i].index.call() + " but got " + entity.index.call());
239+
}
240+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
fn on_test() {
2+
let post_update_schedule = world.get_schedule_by_name.call("PostUpdate");
3+
4+
let test_system = post_update_schedule.get_system_by_name.call("on_test_post_update");
5+
6+
let system_a = world.add_system.call(
7+
post_update_schedule,
8+
system_builder.call("custom_system_a", script_id)
9+
.after.call(test_system)
10+
);
11+
12+
let system_b = world.add_system.call(
13+
post_update_schedule,
14+
system_builder.call("custom_system_b", script_id)
15+
.after.call(test_system)
16+
);
17+
18+
// generate a schedule graph and verify it's what we expect
19+
let dot_graph = post_update_schedule.render_dot.call();
20+
21+
let expected_dot_graph = `
22+
digraph {
23+
node_0 [label="bevy_mod_scripting_core::bindings::allocator::garbage_collector"];
24+
node_1 [label="on_test_post_update"];
25+
node_2 [label="script_integration_test_harness::dummy_before_post_update_system"];
26+
node_3 [label="script_integration_test_harness::dummy_post_update_system"];
27+
node_4 [label="custom_system_a"];
28+
node_5 [label="custom_system_b"];
29+
node_6 [label="SystemSet GarbageCollection"];
30+
node_7 [label="SystemSet ScriptSystem(custom_system_a)"];
31+
node_8 [label="SystemSet ScriptSystem(custom_system_b)"];
32+
node_0 -> node_6 [color=red, label="child of", arrowhead=diamond];
33+
node_4 -> node_7 [color=red, label="child of", arrowhead=diamond];
34+
node_5 -> node_8 [color=red, label="child of", arrowhead=diamond];
35+
node_1 -> node_4 [color=blue, label="runs before", arrowhead=normal];
36+
node_1 -> node_5 [color=blue, label="runs before", arrowhead=normal];
37+
node_2 -> node_3 [color=blue, label="runs before", arrowhead=normal];
38+
}`;
39+
40+
assert_str_eq.call(dot_graph, expected_dot_graph, "Expected the schedule graph to match the expected graph");
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
let runs = [];
2+
3+
fn on_test() {
4+
let post_update_schedule = world.get_schedule_by_name.call("PostUpdate");
5+
6+
world.add_system.call(
7+
post_update_schedule,
8+
system_builder.call("my_exclusive_system", script_id).exclusive.call()
9+
);
10+
11+
return true;
12+
};
13+
14+
fn my_exclusive_system() {
15+
print("my_exclusive_system");
16+
runs.push("my_non_exclusive_system");
17+
18+
let ResourceType = world.get_type_by_name.call("TestResource");
19+
let res = world.get_resource.call(ResourceType);
20+
assert(type_of(res) != "()", "Expected to get resource but got nil");
21+
};
22+
23+
fn on_test_post_update() {
24+
return true;
25+
};
26+
27+
fn on_test_last() {
28+
assert(runs.len == 1, "Expected 1 runs, got: " + runs.len);
29+
return true;
30+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
let runs = [];
2+
3+
fn on_test() {
4+
let post_update_schedule = world.get_schedule_by_name.call("PostUpdate");
5+
world.add_system.call(post_update_schedule, system_builder.call("my_non_exclusive_system", script_id));
6+
return true;
7+
}
8+
9+
fn my_non_exclusive_system() {
10+
print("my_non_exclusive_system");
11+
runs.push("my_non_exclusive_system");
12+
13+
let ResourceType = world.get_type_by_name.call("TestResource");
14+
assert_throws(|| {
15+
let res = world.get_resource.call(ResourceType);
16+
}, ".*Cannot claim access to.*");
17+
}
18+
19+
fn on_test_post_update() {
20+
return true;
21+
}
22+
23+
fn on_test_last() {
24+
assert(runs.len == 1, "Expected 1 runs, got: " + runs.len);
25+
return true;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
let runs = [];
2+
3+
let ResourceTypeA = world.get_type_by_name.call("TestResource");
4+
let ResourceTypeB = world.get_type_by_name.call("TestResourceWithVariousFields");
5+
let ComponentA = world.get_type_by_name.call("CompWithFromWorldAndComponentData");
6+
let ComponentB = world.get_type_by_name.call("CompWithDefaultAndComponentData");
7+
8+
fn on_test() {
9+
let post_update_schedule = world.get_schedule_by_name.call("PostUpdate");
10+
11+
let entity = world.spawn_.call();
12+
let entity2 = world.spawn_.call();
13+
14+
world.add_default_component.call(entity, ComponentA);
15+
world.add_default_component.call(entity, ComponentB);
16+
world.add_default_component.call(entity2, ComponentA);
17+
world.add_default_component.call(entity2, ComponentB);
18+
19+
let built_system = system_builder.call("my_parameterised_system", script_id)
20+
.resource.call(ResourceTypeA)
21+
.query.call(world.query.call().component.call(ComponentA).with_.call(ComponentB))
22+
.resource.call(ResourceTypeB);
23+
24+
world.add_system.call(post_update_schedule, built_system);
25+
26+
return true;
27+
}
28+
29+
fn my_parameterised_system(resourceA, query, resourceB) {
30+
print("my_parameterised_system");
31+
runs.push("my_non_exclusive_system");
32+
33+
assert(type_of(resourceA) != (), "Expected to get resource but got nil");
34+
assert(type_of(query) != (), "Expected to get query but got nil");
35+
assert(type_of(resourceB) != (), "Expected to get resource but got nil");
36+
37+
assert(resourceA.bytes.len() == 6, "Expected 6 bytes, got: " + resourceA.bytes.len());
38+
assert(resourceB.string == "Initial Value", "Expected 'Initial Value', got: " + resourceB.string);
39+
assert(query.len() == 2, "Expected 3 results, got: " + query.len());
40+
}
41+
42+
fn on_test_post_update() {
43+
return true;
44+
}
45+
46+
fn on_test_last() {
47+
assert(runs.len == 1, "Expected 1 runs, got: " + runs.len);
48+
return true;
49+
}

0 commit comments

Comments
 (0)