use super::control_flow::ControlFlow;
use super::environment::{Environment, EventHandler, UserFunction};
use super::frame::Frame;
use crate::ast::*;
use crate::error::RobinPathError;
use crate::value::{self, AttributePathSegment, Value};
use std::collections::HashMap;
const MAX_RECURSION_DEPTH: usize = 5000;
pub struct Executor {
pub environment: Environment,
call_stack: Vec<Frame>,
recursion_depth: HashMap<String, usize>,
pending_decorators: Vec<(String, Vec<Arg>)>,
}
impl Executor {
pub fn new(environment: Environment) -> Self {
let mut stack = Vec::new();
stack.push(Frame::new());
Self {
environment,
call_stack: stack,
recursion_depth: HashMap::new(),
pending_decorators: Vec::new(),
}
}
pub fn execute(&mut self, stmts: &[Stmt]) -> Result<Value, RobinPathError> {
self.collect_event_handlers(stmts);
self.collect_function_defs(stmts);
for stmt in stmts {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(v) => return Ok(v),
ControlFlow::End => return Ok(self.current_frame().last_value.clone()),
ControlFlow::Break | ControlFlow::Continue => {
return Ok(self.current_frame().last_value.clone());
}
}
}
Ok(self.current_frame().last_value.clone())
}
fn collect_event_handlers(&mut self, stmts: &[Stmt]) {
for stmt in stmts {
if let Stmt::OnBlock { event_name, body, .. } = stmt {
self.environment.event_handlers.push(EventHandler {
event_name: event_name.clone(),
body: body.clone(),
});
}
}
}
fn collect_function_defs(&mut self, stmts: &[Stmt]) {
let mut pending_decorators: Vec<(String, Vec<Arg>)> = Vec::new();
for stmt in stmts {
match stmt {
Stmt::Decorator { name, args, .. } => {
pending_decorators.push((name.clone(), args.clone()));
}
Stmt::Define { name, param_names, body, .. } => {
self.environment.register_function(UserFunction {
name: name.clone(),
param_names: param_names.clone(),
body: body.clone(),
});
for (dec_name, dec_args) in pending_decorators.drain(..) {
self.apply_decorator_metadata(name, &dec_name, &dec_args);
}
}
_ => {
pending_decorators.clear();
}
}
}
}
fn apply_decorator_metadata(&mut self, target: &str, decorator_name: &str, decorator_args: &[Arg]) {
if decorator_name == "param" {
let mut param_obj = indexmap::IndexMap::new();
let values: Vec<Value> = decorator_args.iter().filter_map(|arg| {
match arg {
Arg::Expr(Expr::Literal { value: lit, .. }) => Some(match lit {
LiteralValue::String(s) => Value::String(s.clone()),
LiteralValue::Number(n) => Value::Number(*n),
LiteralValue::Bool(b) => Value::Bool(*b),
LiteralValue::Null => Value::Null,
}),
Arg::Expr(Expr::Variable { name, .. }) => Some(Value::String(name.clone())),
_ => None,
}
}).collect();
if let Some(dt) = values.first() {
param_obj.insert("dataType".to_string(), dt.clone());
}
if let Some(Value::String(name)) = values.get(1) {
param_obj.insert("name".to_string(), Value::String(name.clone()));
}
if values.len() == 3 {
if let Some(desc) = values.get(2) {
param_obj.insert("description".to_string(), desc.clone());
}
param_obj.insert("required".to_string(), Value::Bool(true));
} else if values.len() >= 4 {
if let Some(default_val) = values.get(2) {
param_obj.insert("defaultValue".to_string(), default_val.clone());
}
if let Some(desc) = values.get(3) {
param_obj.insert("description".to_string(), desc.clone());
}
param_obj.insert("required".to_string(), Value::Bool(false));
} else {
param_obj.insert("required".to_string(), Value::Bool(true));
}
let entry = self.environment.metadata
.entry(target.to_string())
.or_insert_with(indexmap::IndexMap::new);
let params = entry.entry("parameters".to_string()).or_insert_with(|| Value::Array(Vec::new()));
if let Value::Array(arr) = params {
arr.push(Value::Object(param_obj));
}
return;
}
if decorator_name == "arg" {
let mut param_obj = indexmap::IndexMap::new();
param_obj.insert("name".to_string(), Value::String("args".to_string()));
param_obj.insert("dataType".to_string(), Value::String("array".to_string()));
if let Some(arg) = decorator_args.first() {
match arg {
Arg::Expr(Expr::Literal { value: LiteralValue::String(s), .. }) => {
param_obj.insert("itemType".to_string(), Value::String(s.clone()));
}
_ => {}
}
}
param_obj.insert("required".to_string(), Value::Bool(false));
let entry = self.environment.metadata
.entry(target.to_string())
.or_insert_with(indexmap::IndexMap::new);
let params = entry.entry("parameters".to_string()).or_insert_with(|| Value::Array(Vec::new()));
if let Value::Array(arr) = params {
arr.push(Value::Object(param_obj));
}
return;
}
if decorator_name == "required" {
if let Some(arg) = decorator_args.first() {
if let Arg::Expr(Expr::Variable { name, .. }) = arg {
let entry = self.environment.metadata
.entry(target.to_string())
.or_insert_with(indexmap::IndexMap::new);
if let Some(Value::Array(params)) = entry.get_mut("parameters") {
for p in params.iter_mut() {
if let Value::Object(obj) = p {
if obj.get("name") == Some(&Value::String(name.clone())) {
obj.insert("required".to_string(), Value::Bool(true));
}
}
}
}
}
}
return;
}
let meta_key = match decorator_name {
"desc" | "description" => "description",
"title" => "title",
other => other,
};
if let Some(arg) = decorator_args.first() {
let value = match arg {
Arg::Expr(Expr::Literal { value: lit, .. }) => {
match lit {
LiteralValue::String(s) => Value::String(s.clone()),
LiteralValue::Number(n) => Value::Number(*n),
LiteralValue::Bool(b) => Value::Bool(*b),
LiteralValue::Null => Value::Null,
}
}
_ => return,
};
let entry = self.environment.metadata
.entry(target.to_string())
.or_insert_with(indexmap::IndexMap::new);
entry.insert(meta_key.to_string(), value);
}
}
fn current_frame(&self) -> &Frame {
self.call_stack.last().expect("Call stack should never be empty")
}
fn current_frame_mut(&mut self) -> &mut Frame {
self.call_stack.last_mut().expect("Call stack should never be empty")
}
fn set_last_value(&mut self, value: Value) {
self.current_frame_mut().last_value = value;
}
fn execute_statement(&mut self, stmt: &Stmt) -> Result<ControlFlow, RobinPathError> {
match stmt {
Stmt::Command { name, args, into, .. } => {
self.execute_command(name, args, into.as_ref())
}
Stmt::Assignment {
target_name,
target_path,
value,
is_set,
is_var,
is_const,
fallback,
..
} => {
let result = self.execute_assignment(
target_name,
target_path,
value,
*is_set,
*is_var,
*is_const,
fallback.as_ref(),
);
if !self.pending_decorators.is_empty() {
let decorators = std::mem::take(&mut self.pending_decorators);
for (dec_name, dec_args) in decorators {
self.apply_decorator_metadata(target_name, &dec_name, &dec_args);
}
}
result
}
Stmt::IfBlock {
condition,
then_branch,
elseif_branches,
else_branch,
..
} => self.execute_if_block(condition, then_branch, elseif_branches, else_branch),
Stmt::InlineIf {
condition,
command,
else_command,
..
} => self.execute_inline_if(condition, command, else_command.as_deref()),
Stmt::IfTrue { command, .. } => self.execute_iftrue(command),
Stmt::IfFalse { command, .. } => self.execute_iffalse(command),
Stmt::ForLoop {
var_name,
iterable,
from,
to,
step,
key_var_name,
body,
..
} => self.execute_for_loop(
var_name,
iterable.as_ref(),
from.as_ref(),
to.as_ref(),
step.as_ref(),
key_var_name.as_deref(),
body,
),
Stmt::Define {
name,
param_names,
body,
..
} => {
self.environment.register_function(UserFunction {
name: name.clone(),
param_names: param_names.clone(),
body: body.clone(),
});
let decorators = std::mem::take(&mut self.pending_decorators);
for (dec_name, dec_args) in decorators {
self.apply_decorator_metadata(name, &dec_name, &dec_args);
}
Ok(ControlFlow::None)
}
Stmt::ScopeBlock {
param_names,
body,
into,
..
} => self.execute_scope_block(param_names, body, into.as_ref()),
Stmt::RepeatBlock {
count,
body,
into,
..
} => self.execute_repeat_block(count, body, into.as_ref()),
Stmt::TogetherBlock { blocks, .. } => {
for block in blocks {
let cf = self.execute_statement(block)?;
if !matches!(cf, ControlFlow::None) {
return Ok(cf);
}
}
Ok(ControlFlow::None)
}
Stmt::OnBlock { event_name, body, .. } => {
let already_exists = self.environment.event_handlers.iter().any(|h| {
h.event_name == *event_name && h.body.len() == body.len()
});
if !already_exists {
self.environment.event_handlers.push(EventHandler {
event_name: event_name.clone(),
body: body.clone(),
});
}
Ok(ControlFlow::None)
}
Stmt::Return { value, .. } => {
let val = if let Some(expr) = value {
self.evaluate_expression(expr)?
} else {
Value::Null
};
Ok(ControlFlow::Return(val))
}
Stmt::Break { .. } => Ok(ControlFlow::Break),
Stmt::Continue { .. } => Ok(ControlFlow::Continue),
Stmt::Comment { .. } | Stmt::ChunkMarker { .. } | Stmt::PromptBlock { .. } => {
Ok(ControlFlow::None)
}
Stmt::CellBlock { content, .. } => {
for inner_stmt in content {
let cf = self.execute_statement(inner_stmt)?;
if !matches!(cf, ControlFlow::None) {
return Ok(cf);
}
}
Ok(ControlFlow::None)
}
Stmt::Decorator { name, args, .. } => {
self.pending_decorators.push((name.clone(), args.clone()));
Ok(ControlFlow::None)
}
Stmt::UseModule { module_name, .. } => {
self.environment.used_modules.insert(module_name.clone());
self.environment.current_module = Some(module_name.clone());
Ok(ControlFlow::None)
}
}
}
fn execute_command(
&mut self,
name: &str,
args: &[Arg],
into: Option<&IntoTarget>,
) -> Result<ControlFlow, RobinPathError> {
let mut eval_args = Vec::new(); let mut positional_args = Vec::new(); let mut named_args: Vec<(String, Value)> = Vec::new();
for arg in args {
match arg {
Arg::Expr(expr) => {
let val = self.evaluate_expression(expr)?;
eval_args.push(val.clone());
positional_args.push(val);
}
Arg::Named { name, value } => {
let val = self.evaluate_expression(value)?;
eval_args.push(val.clone());
named_args.push((name.clone(), val));
}
}
}
match name {
"_var" | "_expr" | "_subexpr" => {
if let Some(val) = eval_args.into_iter().next() {
self.set_last_value(val);
}
return Ok(ControlFlow::None);
}
"end" => return Ok(ControlFlow::End),
"empty" => {
if let Some(Arg::Expr(Expr::Variable { name: var_name, .. })) = args.first() {
if self.environment.constants.contains(var_name) {
return Err(RobinPathError::ConstantReassignment {
name: var_name.clone(),
});
}
self.set_variable(var_name, Value::Null);
}
return Ok(ControlFlow::None);
}
"clear" => {
self.set_last_value(Value::Null);
return Ok(ControlFlow::None);
}
"forget" => {
if let Some(Arg::Expr(Expr::Variable { name: var_name, .. })) = args.first() {
self.current_frame_mut().forgotten.insert(var_name.clone());
self.set_last_value(Value::Null);
} else if let Some(val) = eval_args.first() {
if let Value::String(var_name) = val {
let name = var_name.trim_start_matches('$').to_string();
self.current_frame_mut().forgotten.insert(name);
}
self.set_last_value(Value::Null);
}
return Ok(ControlFlow::None);
}
"fallback" => {
if let Some(Arg::Expr(Expr::Variable { name: var_name, path, .. })) = args.first() {
let current = self.resolve_variable(var_name).unwrap_or(Value::Null);
let resolved = if path.is_empty() {
current.clone()
} else {
self.resolve_path_with_dynamic_keys(¤t, path)
};
if !resolved.is_truthy() {
let default = eval_args.get(1).cloned().unwrap_or(Value::Null);
self.set_last_value(default);
} else {
self.set_last_value(resolved);
}
}
return Ok(ControlFlow::None);
}
"has" => {
let exists = match args.first() {
Some(Arg::Expr(Expr::Variable { name: var_name, .. })) => {
self.resolve_variable(var_name).is_some()
}
_ => {
if let Some(val) = eval_args.first() {
let name_str = val.to_display_string();
let name_str = name_str.trim_start_matches('$');
let special_commands = [
"log", "say", "set", "get", "var", "const", "empty", "clear",
"forget", "fallback", "has", "getType", "repeat", "range",
"if", "for", "def", "define", "do", "return", "break",
"continue", "end", "use", "on", "together", "trigger",
"meta", "setMeta", "getMeta",
];
special_commands.contains(&name_str)
|| self.environment.functions.contains_key(name_str)
|| self.environment.builtins.contains_key(name_str)
|| self.environment.resolve_function_name(name_str).is_some()
} else {
false
}
}
};
self.set_last_value(Value::Bool(exists));
return Ok(ControlFlow::None);
}
"getType" => {
if let Some(val) = eval_args.first() {
let type_name = Value::String(val.type_of().to_string());
self.set_last_value(type_name);
}
return Ok(ControlFlow::None);
}
"trigger" => {
let event_name = eval_args.first()
.map(|v| v.to_display_string())
.unwrap_or_default();
let trigger_args: Vec<Value> = eval_args.into_iter().skip(1).collect();
self.trigger_event(&event_name, &trigger_args)?;
return Ok(ControlFlow::None);
}
"meta" | "setMeta" => {
let target_name = match args.first() {
Some(Arg::Expr(Expr::Variable { name: var_name, .. })) => var_name.clone(),
_ => eval_args.first().map(|v| v.to_display_string()).unwrap_or_default(),
};
let key = eval_args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
let value = eval_args.get(2).cloned().unwrap_or(Value::Null);
let entry = self.environment.metadata
.entry(target_name)
.or_insert_with(indexmap::IndexMap::new);
entry.insert(key, value);
self.set_last_value(Value::Null);
return Ok(ControlFlow::None);
}
"getMeta" => {
let target_name = match args.first() {
Some(Arg::Expr(Expr::Variable { name: var_name, .. })) => var_name.clone(),
_ => eval_args.first().map(|v| v.to_display_string()).unwrap_or_default(),
};
let key = eval_args.get(1).map(|v| v.to_display_string());
let result = if let Some(meta) = self.environment.metadata.get(&target_name) {
if let Some(key) = key {
meta.get(&key).cloned().unwrap_or(Value::Null)
} else {
Value::Object(meta.clone())
}
} else {
Value::Null
};
self.set_last_value(result);
return Ok(ControlFlow::None);
}
_ => {}
}
if name == "log" || name == "say" {
let output: Vec<String> = eval_args.iter().map(|v| v.to_display_string()).collect();
if name == "say" {
let concatenated = output.join("");
self.environment.output.push(concatenated.clone());
let result = Value::String(concatenated);
self.set_last_value(result.clone());
if let Some(target) = into {
self.set_variable_at_path(&target.name, &target.path, result);
}
} else {
let line = output.join(" ");
self.environment.output.push(line);
}
return Ok(ControlFlow::None);
}
if name == "repeat" {
if let Some(Value::Number(n)) = eval_args.first() {
let count = *n as usize;
let value = eval_args.get(1).cloned().unwrap_or(Value::Null);
let result = Value::Array(vec![value; count]);
self.set_last_value(result);
}
return Ok(ControlFlow::None);
}
if name == "range" {
let start = eval_args
.first()
.and_then(|v| v.as_number())
.unwrap_or(0.0) as i64;
let end = eval_args
.get(1)
.and_then(|v| v.as_number())
.unwrap_or(0.0) as i64;
let step = eval_args
.get(2)
.and_then(|v| v.as_number())
.unwrap_or(1.0) as i64;
let mut arr = Vec::new();
if step > 0 {
let mut i = start;
while i <= end {
arr.push(Value::Number(i as f64));
i += step;
}
} else if step < 0 {
let mut i = start;
while i >= end {
arr.push(Value::Number(i as f64));
i += step;
}
}
let result = Value::Array(arr);
self.set_last_value(result);
return Ok(ControlFlow::None);
}
if name == "array.push" || name == "push" {
if let Some(Arg::Expr(Expr::Variable { name: var_name, path, .. })) = args.first() {
let mut current = self.resolve_variable(var_name).unwrap_or(Value::Null);
if path.is_empty() {
if let Value::Array(ref mut arr) = current {
for arg in &eval_args[1..] {
arr.push(arg.clone());
}
}
let result = current.clone();
self.set_variable(var_name, current);
self.set_last_value(result);
} else {
let resolved = self.resolve_path_with_dynamic_keys(¤t, path);
if let Value::Array(mut arr) = resolved {
for arg in &eval_args[1..] {
arr.push(arg.clone());
}
let new_val = Value::Array(arr);
self.set_variable_at_path(var_name, path, new_val.clone());
self.set_last_value(new_val);
}
}
} else {
if let Some(Value::Array(arr)) = eval_args.first() {
let mut new_arr = arr.clone();
for arg in &eval_args[1..] {
new_arr.push(arg.clone());
}
let result = Value::Array(new_arr);
self.set_last_value(result);
}
}
return Ok(ControlFlow::None);
}
let resolved_name = self.environment.resolve_function_name(name);
if let Some(ref resolved) = resolved_name {
if let Some(builtin_fn) = self.environment.builtins.get(resolved.as_str()) {
let result = builtin_fn(&eval_args, None)
.map_err(|e| RobinPathError::runtime(e))?;
if !resolved.starts_with("test.") {
self.set_last_value(result.clone());
}
if let Some(target) = into {
self.set_variable_at_path(&target.name, &target.path, result);
}
return Ok(ControlFlow::None);
}
}
let func_name = resolved_name.as_deref().unwrap_or(name);
if let Some(user_func) = self.environment.functions.get(func_name).cloned() {
let result = self.call_user_function(&user_func, &positional_args, &named_args)?;
self.set_last_value(result.clone());
if let Some(target) = into {
self.set_variable_at_path(&target.name, &target.path, result);
}
return Ok(ControlFlow::None);
}
Err(RobinPathError::unknown_function(
name,
self.find_similar_functions(name),
))
}
fn call_user_function(
&mut self,
func: &UserFunction,
args: &[Value],
named_args: &[(String, Value)],
) -> Result<Value, RobinPathError> {
let depth = self.recursion_depth.entry(func.name.clone()).or_insert(0);
if *depth >= MAX_RECURSION_DEPTH {
return Err(RobinPathError::RecursionLimit {
name: func.name.clone(),
max_depth: MAX_RECURSION_DEPTH,
});
}
*depth += 1;
let mut frame = Frame::function_frame();
for (i, arg) in args.iter().enumerate() {
frame.locals.insert((i + 1).to_string(), arg.clone());
}
for (i, param) in func.param_names.iter().enumerate() {
if let Some(arg) = args.get(i) {
frame.locals.insert(param.clone(), arg.clone());
} else {
frame.locals.insert(param.clone(), Value::Null);
}
}
for (name, val) in named_args {
frame.locals.insert(name.clone(), val.clone());
}
self.call_stack.push(frame);
let mut result = Value::Null;
let mut explicit_return = false;
for stmt in &func.body {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(v) => {
result = v;
explicit_return = true;
break;
}
ControlFlow::Break | ControlFlow::Continue => break,
ControlFlow::End => {
result = self.current_frame().last_value.clone();
self.call_stack.pop();
*self.recursion_depth.entry(func.name.clone()).or_insert(1) -= 1;
return Ok(result);
}
}
}
if !explicit_return {
result = self.current_frame().last_value.clone();
}
self.call_stack.pop();
*self.recursion_depth.entry(func.name.clone()).or_insert(1) -= 1;
Ok(result)
}
fn execute_assignment(
&mut self,
target_name: &str,
target_path: &[AttributePathSegment],
value_expr: &Expr,
is_set: bool,
_is_var: bool,
is_const: bool,
fallback: Option<&Expr>,
) -> Result<ControlFlow, RobinPathError> {
if !is_const && self.environment.constants.contains(target_name) {
return Err(RobinPathError::ConstantReassignment {
name: target_name.to_string(),
});
}
let mut value = self.evaluate_expression(value_expr)?;
if is_set {
if let Some(fallback_expr) = fallback {
if !value.is_truthy() {
value = self.evaluate_expression(fallback_expr)?;
}
}
}
if target_path.is_empty() {
self.set_variable(target_name, value.clone());
} else {
self.set_variable_at_path(target_name, target_path, value.clone());
}
if is_const {
self.environment.constants.insert(target_name.to_string());
}
if is_set {
self.set_last_value(value);
}
Ok(ControlFlow::None)
}
fn execute_if_block(
&mut self,
condition: &Expr,
then_branch: &[Stmt],
elseif_branches: &[ElseIfBranch],
else_branch: &Option<Vec<Stmt>>,
) -> Result<ControlFlow, RobinPathError> {
let cond_val = self.evaluate_expression(condition)?;
if cond_val.is_truthy() {
for stmt in then_branch {
let cf = self.execute_statement(stmt)?;
if !matches!(cf, ControlFlow::None) {
return Ok(cf);
}
}
return Ok(ControlFlow::None);
}
for branch in elseif_branches {
let cond_val = self.evaluate_expression(&branch.condition)?;
if cond_val.is_truthy() {
for stmt in &branch.body {
let cf = self.execute_statement(stmt)?;
if !matches!(cf, ControlFlow::None) {
return Ok(cf);
}
}
return Ok(ControlFlow::None);
}
}
if let Some(else_body) = else_branch {
for stmt in else_body {
let cf = self.execute_statement(stmt)?;
if !matches!(cf, ControlFlow::None) {
return Ok(cf);
}
}
}
Ok(ControlFlow::None)
}
fn execute_inline_if(
&mut self,
condition: &Expr,
command: &Stmt,
else_command: Option<&Stmt>,
) -> Result<ControlFlow, RobinPathError> {
let cond_val = self.evaluate_expression(condition)?;
if cond_val.is_truthy() {
self.execute_statement(command)
} else if let Some(else_cmd) = else_command {
self.execute_statement(else_cmd)
} else {
Ok(ControlFlow::None)
}
}
fn execute_iftrue(&mut self, command: &Stmt) -> Result<ControlFlow, RobinPathError> {
let last = self.current_frame().last_value.clone();
if last.is_truthy() {
self.execute_statement(command)
} else {
Ok(ControlFlow::None)
}
}
fn execute_iffalse(&mut self, command: &Stmt) -> Result<ControlFlow, RobinPathError> {
let last = self.current_frame().last_value.clone();
if !last.is_truthy() {
self.execute_statement(command)
} else {
Ok(ControlFlow::None)
}
}
fn execute_for_loop(
&mut self,
var_name: &str,
iterable: Option<&Expr>,
from: Option<&Expr>,
to: Option<&Expr>,
step: Option<&Expr>,
key_var_name: Option<&str>,
body: &[Stmt],
) -> Result<ControlFlow, RobinPathError> {
let saved_last = self.current_frame().last_value.clone();
if let Some(iter_expr) = iterable {
let iter_val = self.evaluate_expression(iter_expr)?;
let items = match &iter_val {
Value::Array(arr) => arr.clone(),
Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(),
Value::Object(obj) => {
let keys: Vec<String> = obj.keys().cloned().collect();
let vals: Vec<Value> = obj.values().cloned().collect();
if let Some(key_name) = key_var_name {
for (i, val) in vals.iter().enumerate() {
self.current_frame_mut()
.locals
.insert(var_name.to_string(), val.clone());
self.current_frame_mut()
.locals
.insert(key_name.to_string(), Value::String(keys[i].clone()));
if let Some(cf) = self.run_loop_body(body)? {
match cf {
ControlFlow::Break => break,
ControlFlow::Return(_) | ControlFlow::End => return Ok(cf),
_ => break,
}
}
}
return Ok(ControlFlow::None);
}
vals
}
_ => {
vec![iter_val.clone()]
}
};
if items.is_empty() {
self.set_last_value(saved_last);
return Ok(ControlFlow::None);
}
for (i, item) in items.iter().enumerate() {
self.current_frame_mut()
.locals
.insert(var_name.to_string(), item.clone());
self.set_last_value(item.clone());
if let Some(key_name) = key_var_name {
self.current_frame_mut()
.locals
.insert(key_name.to_string(), Value::Number(i as f64));
}
if let Some(cf) = self.run_loop_body(body)? {
match cf {
ControlFlow::Break => break,
ControlFlow::Return(_) | ControlFlow::End => return Ok(cf),
_ => break,
}
}
}
} else if let (Some(from_expr), Some(to_expr)) = (from, to) {
let from_val = self.evaluate_expression(from_expr)?.to_number();
let to_val = self.evaluate_expression(to_expr)?.to_number();
let step_val = step
.map(|s| self.evaluate_expression(s))
.transpose()?
.map(|v| v.to_number())
.unwrap_or(1.0);
if step_val == 0.0 {
return Err(RobinPathError::runtime("For loop step cannot be zero"));
}
let mut i = from_val;
let mut iteration = 0;
let mut any_iteration = false;
loop {
if step_val > 0.0 && i > to_val {
break;
}
if step_val < 0.0 && i < to_val {
break;
}
any_iteration = true;
self.current_frame_mut()
.locals
.insert(var_name.to_string(), Value::Number(i));
self.set_last_value(Value::Number(i));
if let Some(key_name) = key_var_name {
self.current_frame_mut()
.locals
.insert(key_name.to_string(), Value::Number(iteration as f64));
}
if let Some(cf) = self.run_loop_body(body)? {
match cf {
ControlFlow::Break => break,
ControlFlow::Return(_) | ControlFlow::End => return Ok(cf),
_ => break,
}
}
i += step_val;
iteration += 1;
if iteration > 1_000_000 {
return Err(RobinPathError::runtime("For loop exceeded maximum iterations (1000000)"));
}
}
if !any_iteration {
self.set_last_value(saved_last);
}
}
Ok(ControlFlow::None)
}
fn run_loop_body(&mut self, body: &[Stmt]) -> Result<Option<ControlFlow>, RobinPathError> {
for stmt in body {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Break => return Ok(Some(ControlFlow::Break)),
ControlFlow::Continue => return Ok(None), ControlFlow::Return(v) => return Ok(Some(ControlFlow::Return(v))),
ControlFlow::End => return Ok(Some(ControlFlow::End)),
}
}
Ok(None)
}
fn execute_scope_block(
&mut self,
param_names: &[String],
body: &[Stmt],
into: Option<&IntoTarget>,
) -> Result<ControlFlow, RobinPathError> {
let parent_last = self.current_frame().last_value.clone();
let frame = if param_names.is_empty() {
let mut f = Frame::new();
f.last_value = parent_last.clone();
f
} else {
let mut f = Frame::isolated_scope();
for param in param_names {
let val = self.resolve_variable(param).unwrap_or(Value::Null);
f.locals.insert(param.clone(), val);
}
f
};
self.call_stack.push(frame);
let mut result = Value::Null;
let mut explicit_return = false;
for stmt in body {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(v) => {
result = v;
explicit_return = true;
break;
}
ControlFlow::Break | ControlFlow::Continue => break,
ControlFlow::End => {
self.call_stack.pop();
return Ok(ControlFlow::End);
}
}
}
if !explicit_return {
result = self.current_frame().last_value.clone();
}
if param_names.is_empty() {
let locals = self.current_frame().locals.clone();
self.call_stack.pop();
for (k, v) in locals {
self.set_variable(&k, v);
}
} else {
self.call_stack.pop();
}
if let Some(target) = into {
self.set_variable_at_path(&target.name, &target.path, result);
} else {
self.set_last_value(result);
}
Ok(ControlFlow::None)
}
fn trigger_event(&mut self, event_name: &str, trigger_args: &[Value]) -> Result<(), RobinPathError> {
let handlers: Vec<Vec<Stmt>> = self.environment.event_handlers.iter()
.filter(|h| h.event_name == event_name)
.map(|h| h.body.clone())
.collect();
for body in handlers {
let mut frame = Frame::new();
for (i, arg) in trigger_args.iter().enumerate() {
frame.locals.insert((i + 1).to_string(), arg.clone());
}
self.call_stack.push(frame);
for stmt in &body {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(_) | ControlFlow::Break | ControlFlow::End => break,
ControlFlow::Continue => continue,
}
}
self.call_stack.pop();
}
Ok(())
}
fn execute_repeat_block(
&mut self,
count_expr: &Expr,
body: &[Stmt],
into: Option<&IntoTarget>,
) -> Result<ControlFlow, RobinPathError> {
let count_val = self.evaluate_expression(count_expr)?;
let count = count_val.as_number().unwrap_or(0.0) as usize;
let mut accumulated = Value::Null;
for i in 0..count {
let mut frame = Frame::new();
frame.last_value = accumulated.clone();
frame.locals.insert("1".to_string(), Value::Number(i as f64));
frame.locals.insert("2".to_string(), accumulated.clone());
self.call_stack.push(frame);
let mut iter_result = Value::Null;
let mut explicit_return = false;
for stmt in body {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(v) => {
iter_result = v;
explicit_return = true;
break;
}
ControlFlow::Break => {
let locals = self.current_frame().locals.clone();
self.call_stack.pop();
for (k, v) in locals {
if k != "1" && k != "2" {
self.set_variable(&k, v);
}
}
self.set_last_value(accumulated);
if let Some(target) = into {
self.set_variable_at_path(&target.name, &target.path, self.current_frame().last_value.clone());
}
return Ok(ControlFlow::None);
}
ControlFlow::Continue => break,
ControlFlow::End => {
self.call_stack.pop();
return Ok(ControlFlow::End);
}
}
}
if !explicit_return {
iter_result = self.current_frame().last_value.clone();
}
let locals = self.current_frame().locals.clone();
self.call_stack.pop();
for (k, v) in locals {
if k != "1" && k != "2" {
self.set_variable(&k, v);
}
}
accumulated = iter_result;
}
if let Some(target) = into {
self.set_variable_at_path(&target.name, &target.path, accumulated);
} else {
self.set_last_value(accumulated);
}
Ok(ControlFlow::None)
}
pub fn evaluate_expression(&mut self, expr: &Expr) -> Result<Value, RobinPathError> {
match expr {
Expr::Literal { value, .. } => Ok(match value {
LiteralValue::Null => Value::Null,
LiteralValue::Bool(b) => Value::Bool(*b),
LiteralValue::Number(n) => Value::Number(*n),
LiteralValue::String(s) => Value::String(s.clone()),
}),
Expr::Variable { name, path, .. } => {
let resolved = self.resolve_variable(name);
let base = resolved.clone().unwrap_or(Value::Null);
if path.is_empty() {
Ok(base)
} else if resolved.is_none() {
Ok(Value::Undefined)
} else {
Ok(self.resolve_path_with_dynamic_keys(&base, path))
}
}
Expr::LastValue { .. } => Ok(self.current_frame().last_value.clone()),
Expr::Binary {
op, left, right, ..
} => self.evaluate_binary(*op, left, right),
Expr::Unary { op, argument, .. } => self.evaluate_unary(*op, argument),
Expr::Call { callee, args, .. } => {
let mut eval_args = Vec::new();
for a in args {
eval_args.push(self.evaluate_expression(a)?);
}
if let Some(resolved) = self.environment.resolve_function_name(callee) {
if let Some(func) = self.environment.builtins.get(&resolved) {
return func(&eval_args, None).map_err(|e| RobinPathError::runtime(e));
}
}
if let Some(user_func) = self.environment.functions.get(callee).cloned() {
return self.call_user_function(&user_func, &eval_args, &[]);
}
Err(RobinPathError::unknown_function(
callee,
self.find_similar_functions(callee),
))
}
Expr::ObjectLiteral { properties, .. } => {
let mut obj = indexmap::IndexMap::new();
for prop in properties {
if prop.key.starts_with("__computed_") {
if let Expr::ArrayLiteral { elements, .. } = &prop.value {
if elements.len() == 2 {
let key_val = self.evaluate_expression(&elements[0])?;
let val = self.evaluate_expression(&elements[1])?;
obj.insert(key_val.to_display_string(), val);
}
}
} else {
let val = self.evaluate_expression(&prop.value)?;
obj.insert(prop.key.clone(), val);
}
}
Ok(Value::Object(obj))
}
Expr::ArrayLiteral { elements, .. } => {
let mut arr = Vec::new();
for elem in elements {
arr.push(self.evaluate_expression(elem)?);
}
Ok(Value::Array(arr))
}
Expr::Subexpression { body, .. } => {
let mut result = Value::Null;
for stmt in body {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(v) => {
result = v;
break;
}
_ => break,
}
}
if result == Value::Null {
result = self.current_frame().last_value.clone();
}
Ok(result)
}
Expr::TemplateLiteral { raw, .. } => self.evaluate_template(raw),
}
}
fn evaluate_binary(
&mut self,
op: BinaryOp,
left: &Expr,
right: &Expr,
) -> Result<Value, RobinPathError> {
let l = self.evaluate_expression(left)?;
match op {
BinaryOp::And => {
if !l.is_truthy() {
return Ok(Value::Bool(false));
}
let r = self.evaluate_expression(right)?;
return Ok(Value::Bool(r.is_truthy()));
}
BinaryOp::Or => {
if l.is_truthy() {
return Ok(Value::Bool(true));
}
let r = self.evaluate_expression(right)?;
return Ok(Value::Bool(r.is_truthy()));
}
_ => {}
}
let r = self.evaluate_expression(right)?;
match op {
BinaryOp::Add => {
match (&l, &r) {
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
_ => Ok(Value::String(format!(
"{}{}",
l.to_display_string(),
r.to_display_string()
))),
}
}
BinaryOp::Sub => {
Ok(Value::Number(l.to_number() - r.to_number()))
}
BinaryOp::Mul => {
Ok(Value::Number(l.to_number() * r.to_number()))
}
BinaryOp::Div => {
let divisor = r.to_number();
if divisor == 0.0 {
Ok(Value::Number(f64::INFINITY))
} else {
Ok(Value::Number(l.to_number() / divisor))
}
}
BinaryOp::Mod => {
Ok(Value::Number(l.to_number() % r.to_number()))
}
BinaryOp::Eq => Ok(Value::Bool(l.deep_eq(&r))),
BinaryOp::Ne => Ok(Value::Bool(!l.deep_eq(&r))),
BinaryOp::Lt => Ok(Value::Bool(l.to_number() < r.to_number())),
BinaryOp::Lte => Ok(Value::Bool(l.to_number() <= r.to_number())),
BinaryOp::Gt => Ok(Value::Bool(l.to_number() > r.to_number())),
BinaryOp::Gte => Ok(Value::Bool(l.to_number() >= r.to_number())),
BinaryOp::And | BinaryOp::Or => unreachable!(),
}
}
fn evaluate_unary(
&mut self,
op: UnaryOp,
argument: &Expr,
) -> Result<Value, RobinPathError> {
let val = self.evaluate_expression(argument)?;
match op {
UnaryOp::Not => Ok(Value::Bool(!val.is_truthy())),
UnaryOp::Neg => Ok(Value::Number(-val.to_number())),
UnaryOp::Pos => Ok(Value::Number(val.to_number())),
}
}
fn evaluate_template(&mut self, raw: &str) -> Result<Value, RobinPathError> {
let normalized = raw.replace("\r\n", "\n");
let mut result = String::new();
let chars: Vec<char> = normalized.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '\\' && i + 1 < chars.len() {
match chars[i + 1] {
'$' => {
result.push('$');
i += 2;
}
'(' => {
result.push('(');
i += 2;
}
')' => {
result.push(')');
i += 2;
}
'\\' => {
result.push('\\');
i += 2;
}
'`' => {
result.push('`');
i += 2;
}
'n' => {
result.push('\n');
i += 2;
}
't' => {
result.push('\t');
i += 2;
}
_ => {
result.push(chars[i]);
i += 1;
}
}
} else if chars[i] == '$' && i + 1 >= chars.len() {
result.push_str(&self.current_frame().last_value.to_display_string());
i += 1;
} else if chars[i] == '$' {
if chars[i + 1] == '(' {
let start = i + 2;
let mut depth = 1;
let mut end = start;
while end < chars.len() && depth > 0 {
if end + 1 < chars.len() && chars[end] == '$' && chars[end + 1] == '(' {
depth += 1;
end += 1;
} else if chars[end] == ')' {
depth -= 1;
if depth == 0 {
break;
}
}
end += 1;
}
let code: String = chars[start..end].iter().collect();
let val = self.execute_template_code(&code)?;
result.push_str(&val.to_display_string());
i = end + 1;
} else if chars[i + 1].is_alphanumeric() || chars[i + 1] == '_' {
let mut var_text = String::new();
let mut j = i + 1;
while j < chars.len()
&& (chars[j].is_alphanumeric()
|| chars[j] == '_'
|| chars[j] == '.'
|| chars[j] == '[')
{
if chars[j] == '[' {
var_text.push(chars[j]);
j += 1;
while j < chars.len() && chars[j] != ']' {
var_text.push(chars[j]);
j += 1;
}
if j < chars.len() {
var_text.push(chars[j]);
j += 1;
}
} else {
var_text.push(chars[j]);
j += 1;
}
}
let (name, path) = crate::parser::parser::parse_variable_path(&var_text);
let base = self.resolve_variable(&name).unwrap_or(Value::Null);
let val = if path.is_empty() {
base
} else {
self.resolve_path_with_dynamic_keys(&base, &path)
};
result.push_str(&val.to_display_string());
i = j;
} else {
result.push_str(&self.current_frame().last_value.to_display_string());
i += 1;
}
} else {
result.push(chars[i]);
i += 1;
}
}
Ok(Value::String(result))
}
fn execute_template_code(&mut self, code: &str) -> Result<Value, RobinPathError> {
use crate::lexer::Lexer;
use crate::parser::Parser;
let tokens = Lexer::new(code).tokenize()?;
let stmts = Parser::new(tokens).parse()?;
let mut result = Value::Null;
for stmt in &stmts {
let cf = self.execute_statement(stmt)?;
match cf {
ControlFlow::None => {}
ControlFlow::Return(v) => {
result = v;
break;
}
_ => break,
}
}
if result == Value::Null {
result = self.current_frame().last_value.clone();
}
Ok(result)
}
fn resolve_variable(&self, name: &str) -> Option<Value> {
let frame = self.current_frame();
if frame.forgotten.contains(name) {
return None;
}
if let Some(val) = frame.locals.get(name) {
return Some(val.clone());
}
if frame.is_isolated_scope {
return None;
}
for i in (0..self.call_stack.len() - 1).rev() {
let f = &self.call_stack[i];
if f.forgotten.contains(name) {
return None;
}
if let Some(val) = f.locals.get(name) {
return Some(val.clone());
}
if f.is_isolated_scope {
break;
}
}
self.environment.variables.get(name).cloned()
}
fn set_variable(&mut self, name: &str, value: Value) {
if self.call_stack.len() > 1 {
self.current_frame_mut()
.locals
.insert(name.to_string(), value);
} else {
self.environment
.variables
.insert(name.to_string(), value);
}
}
fn set_variable_at_path(
&mut self,
name: &str,
path: &[AttributePathSegment],
value: Value,
) {
if path.is_empty() {
self.set_variable(name, value);
return;
}
let resolved_path = self.resolve_dynamic_path(path);
let mut base = self.resolve_variable(name).unwrap_or(Value::Null);
value::set_at_path(&mut base, &resolved_path, value);
self.set_variable(name, base);
}
fn resolve_path_with_dynamic_keys(
&self,
base: &Value,
path: &[AttributePathSegment],
) -> Value {
let mut current = base.clone();
for segment in path {
match segment {
AttributePathSegment::Property { name } => {
current = match ¤t {
Value::Object(obj) => obj.get(name).cloned().unwrap_or(Value::Null),
Value::Array(arr) if name == "length" => Value::Number(arr.len() as f64),
Value::String(s) if name == "length" => Value::Number(s.chars().count() as f64),
_ => return Value::Null,
};
}
AttributePathSegment::Index { index } => {
current = match ¤t {
Value::Array(arr) => {
let idx = *index;
if idx < 0 || idx as usize >= arr.len() {
return Value::Null;
}
arr[idx as usize].clone()
}
_ => return Value::Null,
};
}
AttributePathSegment::DynamicKey {
variable,
var_path,
} => {
let key_val = self.resolve_variable(variable).unwrap_or(Value::Null);
let key = if let Some(vp) = var_path {
self.resolve_path_with_dynamic_keys(&key_val, vp)
} else {
key_val
};
current = match ¤t {
Value::Object(obj) => {
let key_str = key.to_display_string();
obj.get(&key_str).cloned().unwrap_or(Value::Null)
}
Value::Array(arr) => {
if let Value::Number(n) = &key {
let idx = *n as i64;
if idx >= 0 && (idx as usize) < arr.len() {
arr[idx as usize].clone()
} else {
Value::Null
}
} else {
Value::Null
}
}
_ => return Value::Null,
};
}
}
}
current
}
fn resolve_dynamic_path(&self, path: &[AttributePathSegment]) -> Vec<AttributePathSegment> {
path.iter()
.map(|seg| match seg {
AttributePathSegment::DynamicKey {
variable,
var_path,
} => {
let key_val = self.resolve_variable(variable).unwrap_or(Value::Null);
let key = if let Some(vp) = var_path {
self.resolve_path_with_dynamic_keys(&key_val, vp)
} else {
key_val
};
match &key {
Value::Number(n) => AttributePathSegment::Index { index: *n as i64 },
_ => AttributePathSegment::Property {
name: key.to_display_string(),
},
}
}
other => other.clone(),
})
.collect()
}
fn find_similar_functions(&self, name: &str) -> Vec<String> {
let mut candidates: Vec<(String, usize)> = Vec::new();
for key in self.environment.builtins.keys() {
let dist = levenshtein(name, key);
if dist <= 3 {
candidates.push((key.clone(), dist));
}
}
for key in self.environment.functions.keys() {
let dist = levenshtein(name, key);
if dist <= 3 {
candidates.push((key.clone(), dist));
}
}
candidates.sort_by_key(|(_, d)| *d);
candidates.into_iter().take(3).map(|(n, _)| n).collect()
}
}
fn levenshtein(a: &str, b: &str) -> usize {
let a: Vec<char> = a.chars().collect();
let b: Vec<char> = b.chars().collect();
let m = a.len();
let n = b.len();
let mut dp = vec![vec![0usize; n + 1]; m + 1];
for i in 0..=m {
dp[i][0] = i;
}
for j in 0..=n {
dp[0][j] = j;
}
for i in 1..=m {
for j in 1..=n {
let cost = if a[i - 1] == b[j - 1] { 0 } else { 1 };
dp[i][j] = (dp[i - 1][j] + 1)
.min(dp[i][j - 1] + 1)
.min(dp[i - 1][j - 1] + cost);
}
}
dp[m][n]
}