This commit is contained in:
parent
fb0f96a357
commit
39ae9cb977
2 changed files with 59 additions and 52 deletions
|
@ -35,7 +35,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
let items = [ 1, 2, 3, 4]
|
let items = [ 1, 2, 3, 4]
|
||||||
FOR EACH item IN items
|
FOR EACH item IN items
|
||||||
PRINT item
|
PRINT item
|
||||||
NEXT item
|
NEXT item
|
||||||
let list = FIND "rob", "ACTION=EMUL1"
|
let list = FIND "rob", "ACTION=EMUL1"
|
||||||
let text = GET "example.com" "#;
|
let text = GET "example.com" "#;
|
||||||
|
|
||||||
|
|
|
@ -20,59 +20,65 @@ 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$",
|
],
|
||||||
],
|
true, // We're modifying the scope by adding the loop variable
|
||||||
false,
|
|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();
|
let next_var = inputs[3].get_string_value().unwrap();
|
||||||
let next_var = inputs[3].get_string_value().unwrap();
|
|
||||||
|
|
||||||
// Verify variable names match
|
// Verify variable names match
|
||||||
if loop_var != next_var {
|
if loop_var != next_var {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"NEXT variable '{}' doesn't match FOR EACH variable '{}'",
|
"NEXT variable '{}' doesn't match FOR EACH variable '{}'",
|
||||||
next_var, loop_var
|
next_var, loop_var
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the collection expression
|
||||||
|
let collection = context.eval_expression_tree(&inputs[1])?;
|
||||||
|
|
||||||
|
// Get the block as an expression tree
|
||||||
|
let block = &inputs[2];
|
||||||
|
|
||||||
|
// Convert to array
|
||||||
|
let array: Array = if collection.is_array() {
|
||||||
|
collection.cast()
|
||||||
|
} else {
|
||||||
|
return Err("FOR EACH can only iterate over arrays".into());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remember original scope length
|
||||||
|
let orig_len = context.scope().len();
|
||||||
|
|
||||||
|
for item in array {
|
||||||
|
// Push the loop variable into the scope
|
||||||
|
context.scope_mut().push(loop_var.to_string(), item);
|
||||||
|
|
||||||
|
// Evaluate the block with the current scope
|
||||||
|
match context.eval_expression_tree(block) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) if e.to_string() == "EXIT FOR" => break,
|
||||||
|
Err(e) => {
|
||||||
|
// Rewind the scope before returning error
|
||||||
|
context.scope_mut().rewind(orig_len);
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluate the collection expression
|
// Remove the loop variable for next iteration
|
||||||
let collection = context.eval_expression_tree(&inputs[1])?;
|
context.scope_mut().rewind(orig_len);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the block content as a string
|
Ok(Dynamic::UNIT)
|
||||||
let block = inputs[2].get_string_value().unwrap_or_default();
|
},
|
||||||
|
)
|
||||||
// Convert to array
|
.unwrap();
|
||||||
let array: Array = if collection.is_array() {
|
|
||||||
collection.cast()
|
|
||||||
} else {
|
|
||||||
return Err("FOR EACH can only iterate over arrays".into());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new scope for the loop
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
|
|
||||||
for item in array {
|
|
||||||
// Set the variable in the scope
|
|
||||||
scope.set_value(loop_var.clone(), item);
|
|
||||||
|
|
||||||
// Evaluate the block with the new scope
|
|
||||||
match context.engine().eval_with_scope::<()>(&mut scope, &block) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) if e.to_string() == "EXIT FOR" => break,
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Register EXIT FOR
|
// Register EXIT FOR
|
||||||
engine
|
engine
|
||||||
|
@ -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));
|
||||||
|
|
Loading…
Add table
Reference in a new issue