Qwen3 Coder and Cline create a Lisp interpreter in Rust for < $1

3 months ago 11
use crate::ast::Expr; pub fn add(args: Vec<Expr>) -> Result<Expr, String> { let mut sum = 0.0; for arg in args { match arg { Expr::Number(n) => sum += n, _ => return Err("Arguments to + must be numbers".to_string()), } } Ok(Expr::Number(sum)) } pub fn subtract(args: Vec<Expr>) -> Result<Expr, String> { if args.is_empty() { return Err("- requires at least one argument".to_string()); } let mut result = match &args[0] { Expr::Number(n) => *n, _ => return Err("Arguments to - must be numbers".to_string()), }; if args.len() == 1 { return Ok(Expr::Number(-result)); } for arg in &args[1..] { match arg { Expr::Number(n) => result -= n, _ => return Err("Arguments to - must be numbers".to_string()), } } Ok(Expr::Number(result)) } pub fn multiply(args: Vec<Expr>) -> Result<Expr, String> { let mut product = 1.0; for arg in args { match arg { Expr::Number(n) => product *= n, _ => return Err("Arguments to * must be numbers".to_string()), } } Ok(Expr::Number(product)) } pub fn divide(args: Vec<Expr>) -> Result<Expr, String> { if args.is_empty() { return Err("/ requires at least one argument".to_string()); } let mut result = match &args[0] { Expr::Number(n) => *n, _ => return Err("Arguments to / must be numbers".to_string()), }; if args.len() == 1 { if result == 0.0 { return Err("Division by zero".to_string()); } return Ok(Expr::Number(1.0 / result)); } for arg in &args[1..] { match arg { Expr::Number(n) => { if *n == 0.0 { return Err("Division by zero".to_string()); } result /= n; }, _ => return Err("Arguments to / must be numbers".to_string()), } } Ok(Expr::Number(result)) } pub fn equal(args: Vec<Expr>) -> Result<Expr, String> { if args.len() < 2 { return Err("= requires at least two arguments".to_string()); } let first = &args[0]; for arg in &args[1..] { if first != arg { return Ok(Expr::Boolean(false)); } } Ok(Expr::Boolean(true)) } pub fn less_than(args: Vec<Expr>) -> Result<Expr, String> { if args.len() < 2 { return Err("< requires at least two arguments".to_string()); } for i in 0..args.len() - 1 { let (left, right) = (&args[i], &args[i + 1]); match (left, right) { (Expr::Number(a), Expr::Number(b)) => { if a >= b { return Ok(Expr::Boolean(false)); } } _ => return Err("Arguments to < must be numbers".to_string()), } } Ok(Expr::Boolean(true)) } pub fn greater_than(args: Vec<Expr>) -> Result<Expr, String> { if args.len() < 2 { return Err("> requires at least two arguments".to_string()); } for i in 0..args.len() - 1 { let (left, right) = (&args[i], &args[i + 1]); match (left, right) { (Expr::Number(a), Expr::Number(b)) => { if a <= b { return Ok(Expr::Boolean(false)); } } _ => return Err("Arguments to > must be numbers".to_string()), } } Ok(Expr::Boolean(true)) } pub fn cons(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 2 { return Err("cons requires exactly two arguments".to_string()); } let car = args[0].clone(); let cdr = args[1].clone(); match cdr { Expr::List(mut list) => { list.insert(0, car); Ok(Expr::List(list)) } Expr::Nil => Ok(Expr::List(vec![car])), _ => Err("Second argument to cons must be a list or nil".to_string()), } } pub fn car(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("car requires exactly one argument".to_string()); } match &args[0] { Expr::List(list) => { if list.is_empty() { Err("Cannot take car of empty list".to_string()) } else { Ok(list[0].clone()) } } _ => Err("Argument to car must be a list".to_string()), } } pub fn cdr(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("cdr requires exactly one argument".to_string()); } match &args[0] { Expr::List(list) => { if list.is_empty() { Err("Cannot take cdr of empty list".to_string()) } else { Ok(Expr::List(list[1..].to_vec())) } } _ => Err("Argument to cdr must be a list".to_string()), } } pub fn is_null(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("null? requires exactly one argument".to_string()); } Ok(Expr::Boolean(matches!(args[0], Expr::Nil))) } pub fn is_number(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("number? requires exactly one argument".to_string()); } Ok(Expr::Boolean(matches!(args[0], Expr::Number(_)))) } pub fn is_symbol(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("symbol? requires exactly one argument".to_string()); } Ok(Expr::Boolean(matches!(args[0], Expr::Symbol(_)))) } pub fn is_boolean(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("boolean? requires exactly one argument".to_string()); } Ok(Expr::Boolean(matches!(args[0], Expr::Boolean(_)))) } pub fn is_list(args: Vec<Expr>) -> Result<Expr, String> { if args.len() != 1 { return Err("list? requires exactly one argument".to_string()); } Ok(Expr::Boolean(matches!(args[0], Expr::List(_)))) } pub fn display_help() -> Result<Expr, String> { let help_text = r#" Lisp Interpreter Help ===================== This is a simple Scheme-like interpreter with the following features: Basic Data Types: - Numbers: 1, 3.14, -5 - Booleans: true, false - Strings: "hello" - Symbols: x, hello-world - Lists: (1 2 3), (a b c) Arithmetic Operations: - (+ a b c ...) - Addition - (- a b c ...) - Subtraction - (* a b c ...) - Multiplication - (/ a b c ...) - Division Comparison Operations: - (= a b c ...) - Equality - (< a b c ...) - Less than - (> a b c ...) - Greater than List Operations: - (cons a b) - Construct a list - (car lst) - Get first element - (cdr lst) - Get rest of elements - (null? x) - Check if null - (list a b c ...) - Create a list Type Predicates: - (number? x) - Check if number - (symbol? x) - Check if symbol - (boolean? x) - Check if boolean - (list? x) - Check if list Control Structures: - (if condition then-expr else-expr) - Conditional - (define symbol value) - Define variable - (lambda (params) body) - Create function - (begin expr1 expr2 ...) - Execute expressions in sequence Quoting: - 'expr - Quote an expression (equivalent to (quote expr)) - Quoting prevents evaluation of expressions Special Commands: - (help) - Show this help - Ctrl+D - Exit interpreter Examples: - (+ 1 2 3) => 6 - (define x 10) => x - (if (> x 5) "big" "small") => "big" - ((lambda (x) (* x 2)) 5) => 10 - 'hello => hello - '(1 2 3) => (1 2 3) "#; println!("{}", help_text); Ok(Expr::Nil) }
Read Entire Article