use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::PathBuf;
use std::rc::Rc;
use std::cell::RefCell;
use super::parser::{Expr, BinaryOp, UnaryOp, Stmt};
use super::value::Value;
use std::process::Command;
use std::fs::{read_to_string, write};
use std::env;
use std::thread::sleep;
use std::time::Duration;
pub struct Interpreter {
globals: Rc<RefCell<HashMap<String, Value>>>,
env: Rc<RefCell<HashMap<String, Value>>>,
imported: Rc<RefCell<HashSet<PathBuf>>>,
base_path: PathBuf,
}
#[derive(Debug, Clone)]
enum ControlFlow {
Return(Value),
Break,
Continue,
}
impl Interpreter {
pub fn new() -> Self {
let globals = Rc::new(RefCell::new(HashMap::new()));
Self::register_builtins(&mut globals.borrow_mut());
Interpreter {
globals: globals.clone(),
env: globals,
imported: Rc::new(RefCell::new(HashSet::new())),
base_path: PathBuf::from("."),
}
}
pub fn with_base_path(mut self, path: &str) -> Self {
self.base_path = PathBuf::from(path);
self
}
pub fn mark_imported(&mut self, path: &str) {
let file_path = self.base_path.join(path);
if let Ok(canonical) = fs::canonicalize(&file_path) {
self.imported.borrow_mut().insert(canonical);
} else {
self.imported.borrow_mut().insert(file_path);
}
}
fn register_builtins(env: &mut HashMap<String, Value>) {
let builtins = [
("print", Value::Builtin(builtin_print)),
("println", Value::Builtin(builtin_println)),
("input", Value::Builtin(builtin_input)),
("len", Value::Builtin(builtin_len)),
("typeof", Value::Builtin(builtin_typeof)),
("push", Value::Builtin(builtin_push)),
("pop", Value::Builtin(builtin_pop)),
("keys", Value::Builtin(builtin_keys)),
("values", Value::Builtin(builtin_values)),
("range", Value::Builtin(builtin_range)),
("str", Value::Builtin(builtin_str)),
("num", Value::Builtin(builtin_num)),
("system", Value::Builtin(builtin_system)),
("fs_read", Value::Builtin(builtin_fs_read)),
("fs_write", Value::Builtin(builtin_fs_write)),
("env_get", Value::Builtin(builtin_env_get)),
("sleep", Value::Builtin(builtin_sleep)),
("proc_list", Value::Builtin(builtin_proc_list)),
];
for (name, value) in builtins.iter() {
env.insert((*name).to_string(), value.clone());
}
let iris_namespace = builtins
.iter()
.map(|(name, value)| ((*name).to_string(), value.clone()))
.collect::<HashMap<_, _>>();
env.insert(
"iris".to_string(),
Value::Object(Rc::new(RefCell::new(iris_namespace.clone()))),
);
env.insert(
"std".to_string(),
Value::Object(Rc::new(RefCell::new(iris_namespace))),
);
}
pub fn execute(&mut self, stmts: &[Stmt]) -> Result<Value, String> {
let last = Value::Null;
for stmt in stmts {
match self.execute_stmt(stmt)? {
Some(ControlFlow::Return(v)) => return Ok(v),
Some(ControlFlow::Break) => return Err("break outside loop".to_string()),
Some(ControlFlow::Continue) => return Err("continue outside loop".to_string()),
None => {}
}
}
Ok(last)
}
pub fn invoke_main_if_present(&mut self) -> Result<(), String> {
let maybe_main = self
.env
.borrow()
.get("main")
.cloned()
.or_else(|| self.globals.borrow().get("main").cloned());
if let Some(func) = maybe_main {
match func {
Value::Function { .. } | Value::Builtin(_) => {
self.invoke_callable(func, vec![])?;
}
_ => {}
}
}
Ok(())
}
fn execute_stmt(&mut self, stmt: &Stmt) -> Result<Option<ControlFlow>, String> {
match stmt {
Stmt::Expr(expr) => {
self.evaluate(expr)?;
Ok(None)
}
Stmt::Let { name, value } => {
let val = self.evaluate(value)?;
self.env.borrow_mut().insert(name.clone(), val);
Ok(None)
}
Stmt::Const { name, value } => {
let val = self.evaluate(value)?;
self.env.borrow_mut().insert(name.clone(), val);
Ok(None)
}
Stmt::Fn { name, params, body } => {
let func = Value::Function {
params: params.clone(),
body: body.clone(),
closure: self.env.clone(),
};
self.env.borrow_mut().insert(name.clone(), func);
Ok(None)
}
Stmt::Block(stmts) => {
for stmt in stmts {
match self.execute_stmt(stmt)? {
Some(cf) => return Ok(Some(cf)),
None => {}
}
}
Ok(None)
}
Stmt::If { condition, then_branch, else_branch } => {
if self.evaluate(condition)?.is_truthy() {
for stmt in then_branch {
match self.execute_stmt(stmt)? {
Some(cf) => return Ok(Some(cf)),
None => {}
}
}
} else if let Some(else_stmts) = else_branch {
for stmt in else_stmts {
match self.execute_stmt(stmt)? {
Some(cf) => return Ok(Some(cf)),
None => {}
}
}
}
Ok(None)
}
Stmt::While { condition, body } => {
loop {
if !self.evaluate(condition)?.is_truthy() {
break;
}
for stmt in body {
match self.execute_stmt(stmt)? {
Some(ControlFlow::Return(v)) => return Ok(Some(ControlFlow::Return(v))),
Some(ControlFlow::Break) => return Ok(None),
Some(ControlFlow::Continue) => break,
None => {}
}
}
}
Ok(None)
}
Stmt::For { var, iterable, body } => {
let iter_val = self.evaluate(iterable)?;
let items = match iter_val {
Value::Array(arr) => arr.borrow().clone(),
Value::String(s) => s.chars().map(|c| Value::String(c.to_string())).collect(),
_ => return Err("Cannot iterate over this type".to_string()),
};
for item in items {
self.env.borrow_mut().insert(var.clone(), item);
for stmt in body {
match self.execute_stmt(stmt)? {
Some(ControlFlow::Return(v)) => return Ok(Some(ControlFlow::Return(v))),
Some(ControlFlow::Break) => return Ok(None),
Some(ControlFlow::Continue) => break,
None => {}
}
}
}
Ok(None)
}
Stmt::Return(expr) => {
let val = match expr {
Some(e) => self.evaluate(e)?,
None => Value::Null,
};
Ok(Some(ControlFlow::Return(val)))
}
Stmt::Break => Ok(Some(ControlFlow::Break)),
Stmt::Continue => Ok(Some(ControlFlow::Continue)),
Stmt::SystemIris { traits } => {
let mut existing = self.globals.borrow_mut();
existing.insert(
"system_iris_traits".to_string(),
Value::Array(Rc::new(RefCell::new(traits.iter().map(|t| Value::String(t.clone())).collect()))),
);
existing.insert(
"system_iris_foundation".to_string(),
Value::Bool(traits.iter().any(|t| t.eq_ignore_ascii_case("foundation"))),
);
existing.insert(
"system_iris_compiler_speed_target".to_string(),
Value::String("faster-than-rust-cpp-ocaml".to_string()),
);
Ok(None)
}
Stmt::Import { path } => {
let file_path = self.base_path.join(path);
let canonical = fs::canonicalize(&file_path).unwrap_or(file_path.clone());
if self.imported.borrow().contains(&canonical) {
return Ok(None);
}
let source = read_to_string(&file_path)
.map_err(|e| format!("Failed to import '{}': {}", path, e))?;
let tokens = super::lexer::tokenize(&source)?;
let ast = super::parser::parse(&tokens)?;
self.imported.borrow_mut().insert(canonical);
let previous_base = self.base_path.clone();
if let Some(parent) = file_path.parent() {
self.base_path = parent.to_path_buf();
}
let result = self.execute(&ast);
self.base_path = previous_base;
result.map(|_| None)
}
}
}
fn evaluate(&mut self, expr: &Expr) -> Result<Value, String> {
match expr {
Expr::Null => Ok(Value::Null),
Expr::Bool(b) => Ok(Value::Bool(*b)),
Expr::Number(n) => Ok(Value::Number(*n)),
Expr::String(s) => Ok(Value::String(s.clone())),
Expr::Identifier(name) => {
if let Some(val) = self.env.borrow().get(name) {
return Ok(val.clone());
}
if let Some(val) = self.globals.borrow().get(name) {
return Ok(val.clone());
}
Err(format!("Undefined variable: '{}'", name))
}
Expr::Array(elements) => {
let mut vals = Vec::new();
for elem in elements {
vals.push(self.evaluate(elem)?);
}
Ok(Value::Array(Rc::new(RefCell::new(vals))))
}
Expr::Object(pairs) => {
let mut map = HashMap::new();
for (k, v) in pairs {
map.insert(k.clone(), self.evaluate(v)?);
}
Ok(Value::Object(Rc::new(RefCell::new(map))))
}
Expr::Binary { left, op, right } => {
let l = self.evaluate(left)?;
let r = self.evaluate(right)?;
match op {
BinaryOp::Add => {
match (&l, &r) {
(Value::String(a), _) => Ok(Value::String(format!("{}{}", a, r))),
(_, Value::String(b)) => Ok(Value::String(format!("{}{}", l, b))),
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
_ => Err("Invalid operands for +".to_string()),
}
}
BinaryOp::Sub => numeric_op(l, r, |a, b| a - b),
BinaryOp::Mul => numeric_op(l, r, |a, b| a * b),
BinaryOp::Div => numeric_op(l, r, |a, b| a / b),
BinaryOp::Mod => numeric_op(l, r, |a, b| a % b),
BinaryOp::Eq => Ok(Value::Bool(l == r)),
BinaryOp::Neq => Ok(Value::Bool(l != r)),
BinaryOp::Lt => compare_op(l, r, |a, b| a < b),
BinaryOp::Gt => compare_op(l, r, |a, b| a > b),
BinaryOp::Lte => compare_op(l, r, |a, b| a <= b),
BinaryOp::Gte => compare_op(l, r, |a, b| a >= b),
BinaryOp::And => Ok(Value::Bool(l.is_truthy() && r.is_truthy())),
BinaryOp::Or => Ok(Value::Bool(l.is_truthy() || r.is_truthy())),
}
}
Expr::Unary { op, expr } => {
let val = self.evaluate(expr)?;
match op {
UnaryOp::Neg => match val {
Value::Number(n) => Ok(Value::Number(-n)),
_ => Err("Cannot negate non-number".to_string()),
},
UnaryOp::Not => Ok(Value::Bool(!val.is_truthy())),
}
}
Expr::Call { callee, args } => {
let func = self.evaluate(callee)?;
let arg_vals: Result<Vec<Value>, String> = args.iter().map(|a| self.evaluate(a)).collect();
let arg_vals = arg_vals?;
self.invoke_callable(func, arg_vals)
}
Expr::Index { object, index } => {
let obj = self.evaluate(object)?;
let idx = self.evaluate(index)?;
match (obj, idx) {
(Value::Array(arr), Value::Number(n)) => {
let i = n as usize;
let arr = arr.borrow();
if i >= arr.len() {
return Err("Index out of bounds".to_string());
}
Ok(arr[i].clone())
}
(Value::String(s), Value::Number(n)) => {
let i = n as usize;
if i >= s.len() {
return Err("Index out of bounds".to_string());
}
Ok(Value::String(s.chars().nth(i).unwrap().to_string()))
}
(Value::Object(map), Value::String(key)) => {
let map = map.borrow();
match map.get(&key) {
Some(v) => Ok(v.clone()),
None => Ok(Value::Null),
}
}
_ => Err("Cannot index this type".to_string()),
}
}
Expr::Member { object, property } => {
let obj = self.evaluate(object)?;
match obj {
Value::Object(map) => {
let map = map.borrow();
match map.get(property) {
Some(v) => Ok(v.clone()),
None => Ok(Value::Null),
}
}
_ => Err("Cannot access property".to_string()),
}
}
Expr::Assign { target, value } => {
let val = self.evaluate(value)?;
match target.as_ref() {
Expr::Identifier(name) => {
self.env.borrow_mut().insert(name.clone(), val.clone());
Ok(val)
}
Expr::Member { object, property } => {
let obj = self.evaluate(object)?;
match obj {
Value::Object(map) => {
map.borrow_mut().insert(property.clone(), val.clone());
Ok(val)
}
_ => Err("Cannot set property".to_string()),
}
}
Expr::Index { object, index } => {
let obj = self.evaluate(object)?;
let idx = self.evaluate(index)?;
match (obj, idx) {
(Value::Array(arr), Value::Number(n)) => {
let i = n as usize;
arr.borrow_mut()[i] = val.clone();
Ok(val)
}
(Value::Object(map), Value::String(key)) => {
map.borrow_mut().insert(key, val.clone());
Ok(val)
}
_ => Err("Cannot set index".to_string()),
}
}
_ => Err("Invalid assignment target".to_string()),
}
}
Expr::Lambda { params, body } => {
Ok(Value::Function {
params: params.clone(),
body: body.clone(),
closure: self.env.clone(),
})
}
}
}
fn invoke_callable(&mut self, func: Value, arg_vals: Vec<Value>) -> Result<Value, String> {
match func {
Value::Builtin(f) => f(&arg_vals),
Value::Function { params, body, closure } => {
let mut new_env = HashMap::new();
for (k, v) in closure.borrow().iter() {
new_env.insert(k.clone(), v.clone());
}
for (i, param) in params.iter().enumerate() {
new_env.insert(param.clone(), arg_vals.get(i).cloned().unwrap_or(Value::Null));
}
let old_env = self.env.clone();
self.env = Rc::new(RefCell::new(new_env));
let mut result = Value::Null;
for stmt in &body {
match self.execute_stmt(stmt)? {
Some(ControlFlow::Return(v)) => { result = v; break; }
Some(ControlFlow::Break) => { result = Value::Null; break; }
Some(ControlFlow::Continue) => { result = Value::Null; break; }
None => {}
}
}
self.env = old_env;
Ok(result)
}
_ => Err("Not a function".to_string()),
}
}
}
fn numeric_op<F>(left: Value, right: Value, op: F) -> Result<Value, String>
where F: Fn(f64, f64) -> f64 {
match (left, right) {
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(op(a, b))),
_ => Err("Invalid operands for numeric operation".to_string()),
}
}
fn compare_op<F>(left: Value, right: Value, op: F) -> Result<Value, String>
where
F: Fn(f64, f64) -> bool
{
match (left, right) {
(Value::Number(a), Value::Number(b)) => Ok(Value::Bool(op(a, b))),
_ => Err("Invalid operands for comparison".to_string()),
}
}
fn builtin_print(args: &[Value]) -> Result<Value, String> {
let mut msg = String::new();
for (i, arg) in args.iter().enumerate() {
if i > 0 {
msg.push(' ');
}
msg.push_str(&arg.to_string());
}
print!("{}", msg);
Ok(Value::Null)
}
fn builtin_println(args: &[Value]) -> Result<Value, String> {
let mut msg = String::new();
for (i, arg) in args.iter().enumerate() {
if i > 0 {
msg.push(' ');
}
msg.push_str(&arg.to_string());
}
println!("{}", msg);
Ok(Value::Null)
}
fn builtin_input(args: &[Value]) -> Result<Value, String> {
use std::io::{self, Write};
if let Some(arg) = args.first() {
print!("{}", arg);
io::stdout().flush().unwrap();
}
let mut buf = String::new();
io::stdin().read_line(&mut buf).map_err(|e| e.to_string())?;
Ok(Value::String(buf.trim().to_string()))
}
fn builtin_len(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(Value::Array(arr)) => Ok(Value::Number(arr.borrow().len() as f64)),
Some(Value::String(s)) => Ok(Value::Number(s.len() as f64)),
_ => Ok(Value::Number(0.0)),
}
}
fn builtin_typeof(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(v) => Ok(Value::String(v.type_name().to_string())),
None => Ok(Value::String("null".to_string())),
}
}
fn builtin_push(args: &[Value]) -> Result<Value, String> {
if args.len() < 2 {
return Err("push requires 2 arguments".to_string());
}
match &args[0] {
Value::Array(arr) => {
arr.borrow_mut().push(args[1].clone());
Ok(args[0].clone())
}
_ => Err("First argument must be an array".to_string()),
}
}
fn builtin_pop(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(Value::Array(arr)) => {
match arr.borrow_mut().pop() {
Some(v) => Ok(v),
None => Ok(Value::Null),
}
}
_ => Err("pop requires an array".to_string()),
}
}
fn builtin_keys(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(Value::Object(map)) => {
let keys: Vec<Value> = map.borrow().keys().cloned().map(Value::String).collect();
Ok(Value::Array(Rc::new(RefCell::new(keys))))
}
_ => Err("keys requires an object".to_string()),
}
}
fn builtin_values(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(Value::Object(map)) => {
let vals: Vec<Value> = map.borrow().values().cloned().collect();
Ok(Value::Array(Rc::new(RefCell::new(vals))))
}
_ => Err("values requires an object".to_string()),
}
}
fn builtin_range(args: &[Value]) -> Result<Value, String> {
if args.len() < 2 {
return Err("range requires 2 arguments".to_string());
}
match (&args[0], &args[1]) {
(Value::Number(start), Value::Number(end)) => {
let mut vals = Vec::new();
let s = *start as i64;
let e = *end as i64;
for i in s..=e {
vals.push(Value::Number(i as f64));
}
Ok(Value::Array(Rc::new(RefCell::new(vals))))
}
_ => Err("range requires numeric arguments".to_string()),
}
}
fn builtin_str(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(v) => Ok(Value::String(v.to_string())),
None => Ok(Value::String("".to_string())),
}
}
fn builtin_num(args: &[Value]) -> Result<Value, String> {
match args.first() {
Some(Value::String(s)) => match s.parse::<f64>() {
Ok(n) => Ok(Value::Number(n)),
Err(_) => Ok(Value::Number(0.0)),
},
Some(Value::Number(n)) => Ok(Value::Number(*n)),
Some(Value::Bool(b)) => Ok(Value::Number(if *b { 1.0 } else { 0.0 })),
_ => Ok(Value::Number(0.0)),
}
}
fn builtin_system(args: &[Value]) -> Result<Value, String> {
if let Some(Value::String(cmd)) = args.first() {
let output = Command::new("cmd").args(["/c", &cmd]).output().map_err(|e| format!("system failed: {}", e))?;
Ok(Value::Number(if output.status.success() { 0.0 } else { 1.0 }))
} else {
Err("system requires string command".to_string())
}
}
fn builtin_fs_read(args: &[Value]) -> Result<Value, String> {
if let Some(Value::String(path)) = args.first() {
read_to_string(&path).map(Value::String).map_err(|e| format!("fs_read failed: {}", e))
} else {
Err("fs_read requires string path".to_string())
}
}
fn builtin_fs_write(args: &[Value]) -> Result<Value, String> {
if args.len() < 2 {
return Err("fs_write requires path and content".to_string());
}
if let (Some(Value::String(path)), Some(content)) = (args.first(), args.get(1)) {
let content_str = content.to_string();
write(&path, content_str.as_bytes()).map(|_| Value::Null).map_err(|e| format!("fs_write failed: {}", e))
} else {
Err("fs_write requires string path and content".to_string())
}
}
fn builtin_env_get(args: &[Value]) -> Result<Value, String> {
if let Some(Value::String(key)) = args.first() {
Ok(Value::String(env::var(&key).unwrap_or_else(|_| String::new())))
} else {
Err("env_get requires string key".to_string())
}
}
fn builtin_sleep(args: &[Value]) -> Result<Value, String> {
if let Some(Value::Number(ms)) = args.first() {
sleep(Duration::from_millis(*ms as u64));
Ok(Value::Null)
} else {
Err("sleep requires number (ms)".to_string())
}
}
fn builtin_proc_list(_args: &[Value]) -> Result<Value, String> {
let output = Command::new("tasklist").args(["/FO", "CSV", "/NH"]).output().map_err(|e| format!("proc_list failed: {}", e))?;
let stdout = String::from_utf8_lossy(&output.stdout);
Ok(Value::String(stdout.to_string()))
}