- BASIC FOR EACH
All checks were successful
GBCI / build (push) Successful in 15m5s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-07-16 01:56:22 -03:00
parent fb0f96a357
commit 39ae9cb977
2 changed files with 59 additions and 52 deletions

View file

@ -20,13 +20,12 @@ impl ScriptService {
use rhai::{Array, Dynamic, Engine, Scope}; use rhai::{Array, Dynamic, Engine, Scope};
// Register FOR EACH loop with NEXT engine
engine
.register_custom_syntax( .register_custom_syntax(
&[ &[
"FOR", "EACH", "$ident$", "IN", "$expr$", "$block$","NEXT", "$ident$", "FOR", "EACH", "$ident$", "IN", "$expr$", "$block$", "NEXT", "$ident$",
], ],
false, true, // We're modifying the scope by adding the loop variable
|context, inputs| { |context, inputs| {
// Get the iterator variable names // Get the iterator variable names
let loop_var = inputs[0].get_string_value().unwrap(); let loop_var = inputs[0].get_string_value().unwrap();
@ -44,8 +43,8 @@ impl ScriptService {
// Evaluate the collection expression // Evaluate the collection expression
let collection = context.eval_expression_tree(&inputs[1])?; let collection = context.eval_expression_tree(&inputs[1])?;
// Get the block content as a string // Get the block as an expression tree
let block = inputs[2].get_string_value().unwrap_or_default(); let block = &inputs[2];
// Convert to array // Convert to array
let array: Array = if collection.is_array() { let array: Array = if collection.is_array() {
@ -54,21 +53,28 @@ impl ScriptService {
return Err("FOR EACH can only iterate over arrays".into()); return Err("FOR EACH can only iterate over arrays".into());
}; };
// Create a new scope for the loop // Remember original scope length
let mut scope = Scope::new(); let orig_len = context.scope().len();
for item in array { for item in array {
// Set the variable in the scope // Push the loop variable into the scope
scope.set_value(loop_var.clone(), item); context.scope_mut().push(loop_var.to_string(), item);
// Evaluate the block with the new scope // Evaluate the block with the current scope
match context.engine().eval_with_scope::<()>(&mut scope, &block) { match context.eval_expression_tree(block) {
Ok(_) => (), Ok(_) => (),
Err(e) if e.to_string() == "EXIT FOR" => break, Err(e) if e.to_string() == "EXIT FOR" => break,
Err(e) => return Err(e), Err(e) => {
// Rewind the scope before returning error
context.scope_mut().rewind(orig_len);
return Err(e);
} }
} }
// Remove the loop variable for next iteration
context.scope_mut().rewind(orig_len);
}
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
}, },
) )
@ -266,7 +272,8 @@ impl ScriptService {
result.push_str("}\n"); result.push_str("}\n");
result.push_str(&" ".repeat(current_indent)); result.push_str(&" ".repeat(current_indent));
result.push_str(trimmed); result.push_str(trimmed);
result.push_str(";\n"); result.push(';');
result.push('\n');
continue; continue;
} else { } else {
panic!("NEXT without matching FOR EACH"); panic!("NEXT without matching FOR EACH");
@ -286,7 +293,7 @@ impl ScriptService {
let basic_commands = [ let basic_commands = [
"SET", "CREATE", "PRINT", "FOR", "FIND", "GET", "EXIT", "IF", "THEN", "ELSE", "SET", "CREATE", "PRINT", "FOR", "FIND", "GET", "EXIT", "IF", "THEN", "ELSE",
"END IF", "WHILE", "WEND", "DO", "LOOP", "NEXT" "END IF", "WHILE", "WEND", "DO", "LOOP",
]; ];
let is_basic_command = basic_commands.iter().any(|&cmd| trimmed.starts_with(cmd)); let is_basic_command = basic_commands.iter().any(|&cmd| trimmed.starts_with(cmd));