1#![cfg_attr(not(feature = "std"), no_std)]
23
24extern crate alloc;
25extern crate core;
26extern crate pest;
27
28pub use engine::context::ContextBuilder;
29pub use engine::context::InputVariable;
30pub use engine::Constant;
31pub use engine::Engine;
32
33pub use execution::ast::ASTExecutor;
34pub use execution::ast::AST;
35pub use execution::RuntimeError;
36
37pub use execution::optimized_ast::OptimizedAST;
38pub use execution::optimized_ast::OptimizedASTExecutor;
39
40pub use function::ToAbstractFunction;
41
42pub use parsing::error::ASTBuildingError;
43pub use parsing::error::ParsingError;
44pub use parsing::FunctionDefinition;
45pub use parsing::MoonValueKind;
46
47pub use value::MoonValue;
48
49
50#[cfg(feature = "std")]
51type HashSet<T> = std::collections::HashSet<T>;
52#[cfg(feature = "std")]
53type HashMap<K, V> = std::collections::HashMap<K, V>;
54#[cfg(feature = "std")]
55type LazyLock<T> = std::sync::LazyLock<T>;
56
57
58#[cfg(not(feature = "std"))]
59type HashMap<K, V> = alloc::collections::BTreeMap<K, V>;
60#[cfg(not(feature = "std"))]
61type LazyLock<T> = lazy_lock::LazyLock<T>;
62#[cfg(not(feature = "std"))]
63type HashSet<T> = alloc::collections::btree_set::BTreeSet<T>;
64
65
66pub mod engine;
67pub(crate) mod external_utils;
68pub mod execution;
69mod reduced_value_impl;
70pub mod parsing;
71pub mod function;
72pub mod value;
73
74#[cfg(not(feature = "std"))]
75pub(crate) mod lazy_lock;
76
77
78#[cfg(test)]
79mod test {
80 use crate::engine::context::ContextBuilder;
81 use crate::engine::Engine;
82 use crate::{FunctionDefinition, InputVariable};
83 use log::Level;
84
85 #[cfg(feature = "std")]
86 #[test]
87 fn test_optimizations() {
88 let mut engine = Engine::new();
89 engine.add_constant("ONE_AS_CONSTANT", 1);
90 engine.add_function(FunctionDefinition::new("constant_fn_get_two", || { 2 }).inline());
91 let context_with_a_constant_input_variable = ContextBuilder::new()
92 .with_variable(InputVariable::new("four").value(4));
93
94 let unoptimized_script_source = r###"
95 let three = ONE_AS_CONSTANT + constant_fn_get_two();
96 if three == 3{
97 print("First line!");
98 } else if three!=3 {
99 print("How?");
100 } else {
101 print("This won't ever happen 1");
102 }
103 if three!=3 {
104 print("This wont ever happen either 1");
105 } else {
106 print("Second line!");
107 }
108 if four == 4 {
109 print("Third line!");
110 } else if four!=4 {
111 print("How?");
112 } else {
113 print("This won't ever happen 2");
114 }
115 if four!=4 {
116 print("This wont ever happen either 2");
117 } else {
118 print("Fourth line!");
119 }
120 while three == 3 {
121 print("Eternal loop!");
122 }
123 "###;
124
125 let optimized_script_source = r###"
126 print("First line!");
127 print("Second line!");
128 print("Third line!");
129 print("Fourth line!");
130 while true {
131 print("Eternal loop!");
132 }
133 "###;
134
135 let ast_from_optimized = Engine::new()
136 .parse(optimized_script_source, context_with_a_constant_input_variable.clone()).unwrap();
137 let ast_from_unoptimized = engine
138 .parse(unoptimized_script_source, context_with_a_constant_input_variable.clone()).unwrap();
139
140 assert_eq!(ast_from_optimized, ast_from_unoptimized);
141 }
142
143 #[test]
144 fn test_array() {
145 simple_logger::init_with_level(Level::Trace);
146 let engine = Engine::default();
147
148 let ast = engine.parse("let a = [[4 2 5] [3 9 1] [6 8 7]]; a[1][2]", Default::default())
149 .unwrap();
150 let moon_result: i32 = ast.executor().execute().unwrap().try_into().unwrap();
151
152 let rust_executed = (|| {
153 let a = [[4, 2, 5], [3, 9, 1], [6, 8, 7]];
154 a[1][2]
155 })();
156 assert_eq!(rust_executed, moon_result);
157 }
158
159 #[test]
160 fn test_precedence() {
161 simple_logger::init_with_level(Level::Trace);
162 let engine = Engine::default();
163 let expected = 2 * 3 + 5 > 4 && true;
164 let moon_result: bool = engine.parse("2 * 3 + 5 > 4 && true", Default::default())
165 .unwrap().executor().execute().unwrap().try_into().unwrap();
166 assert_eq!(expected, moon_result);
167
168 let expected = true && 4 < 5 + 3 * 2;
169 let moon_result: bool = engine.parse("true && 4 < 5 + 3 * 2", Default::default())
170 .unwrap().executor().execute().unwrap().try_into().unwrap();
171 assert_eq!(expected, moon_result);
172 }
173
174 #[test]
175 fn test_binary_comparator_and_unary() {
176 simple_logger::init_with_level(Level::Trace);
177 let mut engine = Engine::default();
178 engine.add_function(FunctionDefinition::new("is_flag", |()| false)
179 .associated_type_name("agent").known_return_type_name("bool"));
180 engine.add_function(crate::parsing::FunctionDefinition::new("get_bool", |()| false)
181 .associated_type_name("agent").known_return_type_name("bool"));
182
183 let mut context = ContextBuilder::default();
184 context.push_variable(crate::engine::context::InputVariable::new("agent")
185 .associated_type("agent")
186 .lazy_value(|| 46397));
187
188 let res : bool = engine.parse(r#"(!agent.is_flag() && agent.get_bool())"#, context)
189 .unwrap().executor().execute().expect("TODO: panic message").try_into().unwrap();
190 assert_eq!(false, res);
191 }
192
193 #[cfg_attr(not(feature = "std"), test)]
194 fn test_custom_unnamed_type() {
195 let _ = simple_logger::init_with_level(log::Level::Trace);
196
197 let mut engine = Engine::default();
198 let mut context = ContextBuilder::default();
199 context.push_variable(crate::engine::context::InputVariable::new("agent")
200 .lazy_value(|| 46397)
201 .associated_type("agent"));
202 context.push_variable(crate::engine::context::InputVariable::new("effect")
203 .lazy_value(|| 377397)
204 .associated_type("effect"));
205
206 context.push_variable(crate::engine::context::InputVariable::new("forced_true")
207 .associated_type("boolean")
208 .lazy_value(|| true));
209
210 engine.add_function(crate::parsing::FunctionDefinition::new("alt", |()| 0)
211 .associated_type_name("agent").known_return_type_name("int"));
212 engine.add_function(crate::parsing::FunctionDefinition::new("is_flag", |()| false)
213 .associated_type_name("agent").known_return_type_name("bool"));
214
215
216 engine.add_function(crate::parsing::FunctionDefinition::new("set_scale",
217 |(), _scale: f32| {}, )
218 .associated_type_name("effect"));
219 engine.add_function(crate::parsing::FunctionDefinition::new("lived_time",
220 |()| { 3 }, )
221 .associated_type_name("effect"));
222 engine.add_function(crate::parsing::FunctionDefinition::new("set_pos",
223 |(), _x: f32, _y: f32, _z: f32| {},
224 ).associated_type_name("effect"));
225 engine.add_function(crate::parsing::FunctionDefinition::new("set_color",
226 |(), x: f32, y: f32, z: f32| {
227 x + y + z
228 },
229 ).associated_type_name("effect"));
230
231 engine.add_function(crate::parsing::FunctionDefinition::new("kill", |()| {
232 #[cfg(feature = "std")]
233 println!("Removing effect");
234 })
235 .associated_type_name("effect").known_return_type_name("effect"));
236 engine.add_function(crate::parsing::FunctionDefinition::new("effect", |()| 1)
237 .associated_type_name("effect").known_return_type_name("effect"));
238
239 let ast = engine.parse(r#"
240 agent.alt%2==1
241
242
243 "#, context).map_err(|error| panic!("{error}"));
244 ast.unwrap().executor().execute().unwrap();
245 }
246}
247
248#[cfg(test)]
249mod book_tests {
250 use crate::{ContextBuilder, Engine, FunctionDefinition, InputVariable, MoonValue};
251 use alloc::format;
252 use alloc::string::{String, ToString};
253
254
255 #[cfg(feature = "std")]
256 #[test]
257 fn developers_guide___engine() {
258 let engine = Engine::new();
259 let context = ContextBuilder::new();
260
261 let ast = engine.parse(r###"println("Hello world")"###, context).unwrap();
263
264 ast.execute();
266 }
267
268 #[test]
269 fn developers_guide___engine___add_constants() {
270 let mut engine = Engine::new();
271
272 engine.add_constant("ONE", 1);
274
275 let ast_result = engine.parse(r###"return ONE;"###, ContextBuilder::new()).unwrap()
277 .execute().unwrap();
278
279 assert_eq!(MoonValue::Integer(1), ast_result);
280
281 let ast_result_as_i32: i32 = ast_result.try_into().unwrap();
285 assert_eq!(1, ast_result_as_i32);
286 }
287
288 #[test]
289 fn developers_guide___engine___add_functions() {
290 let mut engine = Engine::new();
291
292 let function_sum_two = FunctionDefinition::new("sum_two", |n: u8, m: u8| n + m)
295 .inline();
296
297 engine.add_function(function_sum_two);
299
300 let ast_result: i32 = engine.parse(r###"sum_two(1,2);"###, ContextBuilder::new())
302 .unwrap().execute().unwrap().try_into().unwrap();
303 assert_eq!(3, ast_result);
304 }
305
306 #[test]
307 fn developers_guide___engine___add_functions__Result() {
308 let mut engine = Engine::new();
309
310 let function_sum_two = FunctionDefinition::new("sum_two", |n: u8, m: u8| n.checked_add(m).ok_or(format!("Error, numbers too large ({n}, {m})")))
313 .inline();
314
315 engine.add_function(function_sum_two);
317
318 let ast_result: i32 = engine.parse(r###"sum_two(1,2);"###, ContextBuilder::new())
320 .unwrap().execute().unwrap().try_into().unwrap();
321 assert_eq!(3, ast_result);
322
323 let error = engine.parse(r###"sum_two(100,200);"###, ContextBuilder::new())
325 .err().unwrap().couldnt_build_ast_error().unwrap().as_display_struct(false);
326 let compilation_error = format!("{}", error);
327 #[cfg(feature = "std")]
328 println!("{}", compilation_error);
329
330 assert_eq!(compilation_error.replace(" ", "").replace("\n", ""), (r###"
331 Error: Could not compile.
332 Cause:
333 - Position: On line 1 and column 1
334 - At: sum_two(100,200)
335 - Error: The constant function sum_two was tried to be inlined, but it returned this error:
336 Could not execute a function due to: Error, numbers too large (100, 200).
337 "###).replace(" ", "").replace("\n", ""));
338 }
339
340 #[derive(Clone)]
341 struct MyType {
342 name: String,
343 age: u16,
344 }
345
346 impl From<MyType> for MoonValue {
347 fn from(value: MyType) -> Self {
348 MoonValue::from([
349 MoonValue::from(value.name),
350 MoonValue::from(value.age)
351 ])
352 }
353 }
354
355 impl TryFrom<MoonValue> for MyType {
356 type Error = ();
357
358 fn try_from(value: MoonValue) -> Result<Self, Self::Error> {
359 match value {
360 MoonValue::Array(mut moon_values) => Ok(
361 Self {
362 name: String::try_from(moon_values.remove(0)).map_err(|_| ())?,
363 age: u16::try_from(moon_values.remove(0)).map_err(|_| ())?,
364 }
365 ),
366 _ => Err(())
367 }
368 }
369 }
370
371 #[test]
372 fn developers_guide___engine___custom_type() {
373 let mut engine = Engine::new();
374
375 let my_type_example = MyType { name: "Jorge".to_string(), age: 23 };
377
378 engine.add_constant("BASE_HUMAN", my_type_example.clone());
380
381 engine.add_function(FunctionDefinition::new("age", |value: MyType| {
383 value.age
384 })
385 .associated_type_of::<MyType>());
390
391 let age: u16 = engine.parse("BASE_HUMAN.age", Default::default())
394 .unwrap().execute().unwrap().try_into().unwrap();
395
396 assert_eq!(my_type_example.age, age);
397 }
398
399 #[test]
400 fn developers_guide___context___input_variables() {
401 let engine = Engine::new();
402
403 let context = ContextBuilder::new()
405 .with_variable(InputVariable::new("user_name").value("Jorge"));
406
407 let user_name: String = engine.parse("return user_name;", context)
409 .unwrap().execute().unwrap().try_into().unwrap();
410
411 assert_eq!("Jorge", user_name);
412 }
413
414 #[test]
415 fn developers_guide___context___ast_input_variables() {
416 let engine = Engine::new();
417
418 let context = ContextBuilder::new()
420 .with_variable(InputVariable::new("user_name").associated_type_of::<String>());
421
422 let ast = engine.parse("return user_name;", context)
426 .unwrap();
427 let ast_executor = ast.executor()
428 .push_variable("user_name", "Jorge");
429
430 let user_name: String = ast_executor
432 .execute().unwrap().try_into().unwrap();
433
434 assert_eq!("Jorge", user_name);
435 }
436
437 #[test]
438 fn developers_guide___context___line_error() {
439 let engine = Engine::new();
440 let script = r###"
443let a = 5;
444let b = 10;
445let sum = a + b;
446calling_an_non_existing_function(sum)
447 "###;
448
449 let context_builder = ContextBuilder::new();
451
452 let error = engine.parse(script, context_builder).err().unwrap();
455
456 #[cfg(feature = "std")]
457 println!("{error}");
459
460 let simple_error = format!("{error}").lines().skip(2).next().unwrap().to_string();
461
462 assert_eq!(" - Position: On line 5 and column 1", simple_error);
464
465 let context_builder = ContextBuilder::new()
468 .with_start_parsing_position_offset(100, 100);
469 let error = engine.parse(script, context_builder).err().unwrap();
470
471 let simple_error = format!("{error}").lines().skip(2).next().unwrap().to_string();
472
473 assert_eq!(" - Position: On line 105 and column 1", simple_error);
481 }
482}
483