From 6a83d228bb5a20107c37ff26465a03a86a2c3730 Mon Sep 17 00:00:00 2001 From: Roman Godmaire Date: Mon, 13 May 2024 10:07:44 -0400 Subject: [PATCH] feat: lisp/vec manipulation first, last, nth, push, pop --- mute-interpreter/src/env/core.rs | 155 +++++++++++++++++++++++++++++- mute-interpreter/src/env/mod.rs | 6 +- mute-interpreter/src/evaluator.rs | 12 ++- 3 files changed, 165 insertions(+), 8 deletions(-) diff --git a/mute-interpreter/src/env/core.rs b/mute-interpreter/src/env/core.rs index c8c28d1..fb751b0 100644 --- a/mute-interpreter/src/env/core.rs +++ b/mute-interpreter/src/env/core.rs @@ -138,7 +138,7 @@ pub(super) fn core() -> HashMap { }), ), // Collections - ("list", NativeFunc(|args| Node::List(args.into()))), + ("list", NativeFunc(Node::List)), ( "list?", NativeFunc(|args| { @@ -151,7 +151,7 @@ pub(super) fn core() -> HashMap { Node::Boolean(false) }), ), - ("vector", NativeFunc(Node::Vector)), + ("vector", NativeFunc(|args| Node::Vector(args.into()))), ( "vector?", NativeFunc(|args| { @@ -202,8 +202,9 @@ pub(super) fn core() -> HashMap { "empty?", NativeFunc(|args| { arg_count!(1, args.len()); + let mut args = args; - match args[0].borrow() { + match args.pop_front().expect("argument length checked above") { Node::List(list) => Node::Boolean(list.is_empty()), Node::Vector(vec) => Node::Boolean(vec.is_empty()), Node::Map(map) => Node::Boolean(map.is_empty()), @@ -218,8 +219,9 @@ pub(super) fn core() -> HashMap { "count", NativeFunc(|args| { arg_count!(1, args.len()); + let mut args = args; - match args[0].borrow() { + match args.pop_front().expect("argument length checked above") { Node::List(list) => Node::Int(list.len() as i64), Node::Vector(vec) => Node::Int(vec.len() as i64), Node::Map(map) => Node::Int(map.len() as i64), @@ -240,6 +242,151 @@ pub(super) fn core() -> HashMap { Node::String(val.to_string()) }), ), + // List stuff + ( + "first", + NativeFunc(|args| { + arg_count!(1, args.len()); + + match args[0].borrow() { + Node::List(list) if list.is_empty() => Node::Nil, + Node::Vector(vec) if vec.is_empty() => Node::Nil, + Node::List(list) => list.front().unwrap().clone(), + Node::Vector(vec) => vec.first().unwrap().clone(), + arg => Node::Error(format!( + "TypeError: expected list or vector, got {}.", + arg.get_type() + )), + } + }), + ), + ( + "last", + NativeFunc(|args| { + arg_count!(1, args.len()); + + match args[0].borrow() { + Node::List(list) if list.is_empty() => Node::Nil, + Node::Vector(list) if list.is_empty() => Node::Nil, + + Node::List(list) => list.back().unwrap().clone(), + Node::Vector(vec) => vec.last().unwrap().clone(), + arg => Node::Error(format!( + "TypeError: expected list or vector, got {}.", + arg.get_type() + )), + } + }), + ), + ( + "nth", + NativeFunc(|args| { + arg_count!(2, args.len()); + + let mut args = args; + let list = match args.pop_front().expect("argument length checked above") { + Node::List(list) => list, + Node::Vector(vec) => vec.into(), + arg => { + return Node::Error(format!( + "TypeError: expected list or vector, got {}.", + arg.get_type() + )) + } + }; + + let nth = match args.pop_front().expect("argument length checked above") { + Node::Int(val) => val as usize, + Node::Float(val) => val as usize, + arg => { + return Node::Error(format!( + "TypeError: expected int or float, got {}.", + arg.get_type() + )) + } + }; + + list.get(nth).unwrap().clone() + }), + ), + ( + "push-back", + NativeFunc(|args| { + let mut args = args; + match args.pop_front().expect("argument length checked above") { + Node::List(mut list) => { + while let Some(val) = args.pop_front() { + list.push_back(val); + } + + Node::List(list) + } + Node::Vector(mut vec) => { + while let Some(val) = args.pop_front() { + vec.push(val); + } + + Node::Vector(vec) + } + arg => Node::Error(format!( + "TypeError: expected list or vector, got {}.", + arg.get_type() + )), + } + }), + ), + ( + "push-front", + NativeFunc(|args| { + let mut args = args; + match args.pop_front().expect("argument length checked above") { + Node::List(mut list) => { + while let Some(val) = args.pop_back() { + list.push_front(val); + } + + Node::List(list) + } + arg => { + Node::Error(format!("TypeError: expected list, got {}.", arg.get_type())) + } + } + }), + ), + ( + "pop-front", + NativeFunc(|args| { + let mut args = args; + match args.pop_front().expect("argument length checked above") { + Node::List(mut list) => { + list.pop_front(); + Node::List(list) + } + arg => { + Node::Error(format!("TypeError: expected list, got {}.", arg.get_type())) + } + } + }), + ), + ( + "pop-back", + NativeFunc(|args| { + let mut args = args; + match args.pop_front().expect("argument length checked above") { + Node::List(mut list) => { + list.pop_back(); + Node::List(list) + } + Node::Vector(mut vec) => { + vec.pop(); + Node::Vector(vec) + } + arg => { + Node::Error(format!("TypeError: expected list, got {}.", arg.get_type())) + } + } + }), + ), ] .into_iter() .map(|(k, v)| (k.to_string(), Value::NativeFunc(v))) diff --git a/mute-interpreter/src/env/mod.rs b/mute-interpreter/src/env/mod.rs index 7b2883d..2da579e 100644 --- a/mute-interpreter/src/env/mod.rs +++ b/mute-interpreter/src/env/mod.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::rc::Rc; use crate::Node; @@ -10,10 +10,10 @@ mod macros; mod standard; #[derive(Debug, Clone)] -pub struct NativeFunc(fn(args: Vec) -> Node); +pub struct NativeFunc(fn(args: VecDeque) -> Node); impl NativeFunc { - pub fn call(&self, args: Vec) -> Node { + pub fn call(&self, args: VecDeque) -> Node { (self.0)(args) } } diff --git a/mute-interpreter/src/evaluator.rs b/mute-interpreter/src/evaluator.rs index c74f919..6fd664e 100644 --- a/mute-interpreter/src/evaluator.rs +++ b/mute-interpreter/src/evaluator.rs @@ -102,7 +102,7 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result { let args = list .into_iter() .map(|node| eval_node(env, node)) - .collect::>>()?; + .collect::>>()?; func.call(args) } @@ -420,6 +420,16 @@ mod test { // Strings #[case("(str (+ 1 2))", "3")] #[case("(str (list 1 2 3))", "(1 2 3)")] + // List Manipulation + #[case("(first (list 1 2 3))", "1")] + #[case("(last (list 1 2 3))", "3")] + #[case("(nth (list 1 2 3) 1)", "2")] + #[case("(push-front (list 1 2 3) 0)", "(0 1 2 3)")] + #[case("(push-back (list 1 2 3) 4)", "(1 2 3 4)")] + #[case("(pop-front (list 1 2 3))", "(2 3)")] + #[case("(pop-back (list 1 2 3))", "(1 2)")] + #[case("(push-back [1 2 3] 4)", "[1 2 3 4]")] + #[case("(pop-back [1 2 3 4])", "[1 2 3]")] fn test_evaluator(#[case] input: &str, #[case] expected: &str) { dbg!(input);