The lambda/function code does not work

⚓ Rust    📅 2025-08-23    👤 surdeus    👁️ 5      

surdeus

I am writing a lisp interpreter in rust. I have experienced a problem with function/lambda code:

fn main() {
    let expr = /* EXPRESSION TO TEST */;
    println!("Ret > {}", eval(expr));
}
#[derive(Clone, Debug, PartialEq)]
struct Token {
    expr: Expr,
    row: usize,
    col: usize,
    file: String
}

/* Expr enum: the lisp expression*/
#[derive(Clone, Debug, PartialEq)]
enum Expr {
    Symbol(String),
    Number(f64),
    String(String),
    List(Vec<Expr>),
    Bool(bool),
    Lambda(Vec<Expr>,Vec<Expr>), // args, body
    Function(Vec<Expr>) 
}

/* Scope struct for holding variables and function */
#[derive(Clone, Debug)]
struct Scope {
    env: HashMap<String, ScopeEntry>,
}

/* Scope entry for holding scopes*/
#[derive(Debug, Clone)]
struct ScopeEntry {
    val: Expr,
    constant: bool,
}

/* Program scope*/
impl Scope {
    fn new() -> Self { // New object
	Scope { env: HashMap::new() }
    }
    fn get(&self, name: &str) -> Option<Expr> { // Get a value
	Some(self
	    .env
	    .get(name)
	    .unwrap()
	    .val
	    .clone() 
	)
    }
    fn set(&mut self, name: String, val: ScopeEntry) {
	if let Some(entry) = self.env.get(&name) {
            if self.constant(entry.clone()) {
		panic!("Cannot override constant!");
            }
	}
	self.env.insert(name, val);
    }
    fn contains(&mut self, name: String) -> bool {
	self.env.contains_key(&name)
    }
    fn constant(&self, s: ScopeEntry) -> bool {
	s.constant
    }
}

/* Implement the disply trait for Expr.*/
impl fmt::Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
	write!(f, "{}",
	       match self {
		   Expr::Symbol(s) => s.clone(),
		   Expr::Number(n) => n.to_string(),
		   Expr::String(s) => s.clone(),
		   Expr::Bool(b) => match b {
		       true => String::from("t"),
		       false => String::from("nil"),
		   }
		   Expr::List(items) => {
		       let mut Str = String::from("(");
		       for i in items {
			   Str.push_str(format!("{} ",i).as_str());
		       }
		       let trimmed = Str.trim();
		       Str = trimmed.to_string();
		       Str.push_str(")");
		       Str
		   }
		   Expr::Lambda(_, _) => {
		       let addr = self as *const Self as usize; // Get mem loc 
		       String::from(format!(
			   "#<LAMBDA @{:x}>", addr
		       ))
		   }
		   _ => String::from("todo")
		   
	       }
	)
    }
}

/* lookup: easier way of looking up.*/
fn lookup(name: &str, env_stack: &[Rc<RefCell<Scope>>]) -> Option<Expr> {
    for scope in env_stack.iter().rev() {
        if let Some(val) = scope.borrow().get(name) {
            return Some(val);
        }
    }
    None
}


pub fn eval(expr: Token, env: &mut Vec<Rc<RefCell<Scope>>>,trace: bool) -> Expr {
    let lazy: Vec<Expr> = vec![
	Expr::Symbol("lambda".to_string())
    ];
    if trace {
	println!(">> EVAL: {:?}", expr.expr.clone());
    }
    match expr.expr {
	Expr::Symbol(s) => {
	    lookup(&s,env).expect("Unknown")
	}
	/*Expr::QSymbol(s) => {
	    Expr::Symbol(s)
	}*/
	Expr::Function(ARGS) => {
	    let mut name = ARGS[0].clone();
	    let mut args = vec![];
	    if !(lazy.contains(&name)) {
		for i in &ARGS[1..] {
		    if trace {
			println!("About to evaluate arg: {:?}", i.clone());
		    }
		    args.push(
			eval(
			    Token {
				expr: i.clone(),
				row: expr.row,
				col: expr.col,
				file: expr.file.clone(),
			    },
			    env,
			    trace
			)
		    )
		}
	    } else if trace {
		println!("{} requires lazy evaluation, therefore no arg was evaled",name.clone());
		args = ARGS[1..].to_vec();
	    } else {
		args = ARGS[1..].to_vec();
	    }
	    let mut ret = Expr::Bool(false);
	    env.push(Rc::new(RefCell::new(Scope::new())));
	    // OUT/IN FUNCTIONS
	    if name == Expr::Symbol("print".to_string()) {
		for i in args {
		    print!("{}",i)
		};
		ret = Expr::Bool(false);
            // SYMBOL & LAMBDA FUNCTIONS
            } else if name == Expr::Symbol("set".to_string()) {
                // Manually evaluate args inside set branch
                // First arg must be a quoted symbol as is (not evaluated)
                match &ARGS[1] {
                    Expr::List(S) => {
			match &S[0] {
			    Expr::Symbol(s) => {
				if trace {
				    println!("In set! symbol = {}", s.clone());
                        }

                        // Evaluate second argument now
                        let val = eval(
                            Token {
                                expr: ARGS[2].clone(),
                                row: expr.row,
                                col: expr.col,
                                file: expr.file.clone(),
                            },
                            env,
                            trace);
				env[0].borrow_mut().set(s.clone(), ScopeEntry {
				    val: val.clone(),
				    constant: false,
				});
				ret = val;
			    }
			    _ => panic!("Expected quoted Symbol for set")
			}
                    }
                    _ => panic!("Expected quoted Symbol for set"),
		}
	    } else if name == Expr::Symbol("let".to_string()) {
                // Manually evaluate args inside set branch
                // First arg must be a quoted symbol as is (not evaluated)
                match &ARGS[1] {
                    Expr::List(S) => {
			match &S[0] {
			    Expr::Symbol(s) => {
				if trace {
				    println!("In let! symbol = {}", s.clone());
                        }

                        // Evaluate second argument now
                        let val = eval(
                            Token {
                                expr: ARGS[2].clone(),
                                row: expr.row,
                                col: expr.col,
                                file: expr.file.clone(),
                            },
                            env,
                            trace,
                        );

                        env[env.len()-2].borrow_mut().set(s.clone(), ScopeEntry {
			    val: val.clone(),
			    constant: false,
			});
                        ret = val;
			    }
			    _ => panic!("Expected quoted Symbol for let")
			}
                    }
                    _ => panic!("Expected quoted Symbol for let"),
		}
	    } else if name == Expr::Symbol("defconst".to_string()) {
                // Manually evaluate args inside set branch
                // First arg must be a quoted symbol as is (not evaluated)
                match &ARGS[1] {
                    Expr::List(S) => {
			match &S[0] {
			    Expr::Symbol(s) => {
				if trace {
				    println!("In defconst! symbol = {}", s.clone());
                        }

                        // Evaluate second argument now
                        let val = eval(
                            Token {
                                expr: ARGS[2].clone(),
                                row: expr.row,
                                col: expr.col,
                                file: expr.file.clone(),
                            },
                            env,
                            trace,
                        );

                        env[env.len()-2].borrow_mut().set(s.clone(), ScopeEntry {
				    val: val.clone(),
				    constant: true,
				});
                        ret = val;
			    }
			    _ => panic!("Expected quoted Symbol for defconst")
			}
                    }
                    _ => panic!("Expected quoted Symbol for defconst"),
		}
	    } else if name == Expr::Symbol("lambda".to_string()) {
		let mut lambda_args = vec![];
		let mut lambda_body = vec![];
		match &args[0] {
		    Expr::Function(l) => {
			for i in l {
			    lambda_args.push(i.clone())
			}
		    },
		    _ => panic!("Expected a list of lambda args")
		}
		for i in &args[1..] {
		    lambda_body.push(i.clone())
		};
		ret = Expr::Lambda(lambda_args,lambda_body)
	    } else {
		match &name {
		    Expr::Symbol(s) => {
			
			if env
			    .last()
			    .unwrap()
			    .clone()
			    .borrow_mut()
			    .contains(match name {
				Expr::Symbol(ref s) => s.to_string(),
				_ => panic!("Invalid funcall")
			    }) {
				match env
				    .last()
				    .unwrap()
				    .clone()
				    .borrow_mut()
				    .get(match name {
					Expr::Symbol(ref s) => &s,
					_ => panic!("Invalid funcall")
				    })
				    .unwrap() {
					Expr::Lambda(largs,body) => {
					    let mut lambda_args = vec![];
					    for i in args.clone() {
						lambda_args.push(i)
					    }
					    let mut i = 0;
					    while i <= lambda_args.len() {
						env
						    .last()
						    .unwrap()
						    .borrow_mut()
						    .set(match &largs[0] {
							Expr::Symbol(s) => s.to_string(),
							_ => panic!("Invalid lambda args")
						    },ScopeEntry {
							val: lambda_args[1].clone(),
							constant: false,
						    }		 
						    );
						    i += 1
					    }
					    let mut last = Expr::Bool(false);
					    for i in body {
						last = eval(Token {
						    expr: i,
						    row: expr.row,
						    col: expr.col,
						    file: expr.file.clone(),
						},env,trace);
						ret = last;
					    } 
					}
					_ => panic!("Invalid funcall")
				    }
			    }
		    }
		    _ => panic!("Invalid funcall")
		}
		/*if *env
		    .last()
		    .unwrap()
		    .contains(name) {
		    match name {

			_ => panic!("Invalid funcall")
		    }
		}*/
	    }
	    env.pop();
	    ret
	}
	_ => expr.expr
    }
}

Writing a simple print expr works, whereas invalid functions do not panic (even though they should) and lambda functions do not execute.

1 post - 1 participant

Read full topic

🏷️ Rust_feed