feat: lisp/vec manipulation

first, last, nth, push, pop
This commit is contained in:
Roman Godmaire 2024-05-13 10:07:44 -04:00
parent 5f7f7e4aed
commit 6a83d228bb
3 changed files with 165 additions and 8 deletions

View file

@ -138,7 +138,7 @@ pub(super) fn core() -> HashMap<String, Value> {
}), }),
), ),
// Collections // Collections
("list", NativeFunc(|args| Node::List(args.into()))), ("list", NativeFunc(Node::List)),
( (
"list?", "list?",
NativeFunc(|args| { NativeFunc(|args| {
@ -151,7 +151,7 @@ pub(super) fn core() -> HashMap<String, Value> {
Node::Boolean(false) Node::Boolean(false)
}), }),
), ),
("vector", NativeFunc(Node::Vector)), ("vector", NativeFunc(|args| Node::Vector(args.into()))),
( (
"vector?", "vector?",
NativeFunc(|args| { NativeFunc(|args| {
@ -202,8 +202,9 @@ pub(super) fn core() -> HashMap<String, Value> {
"empty?", "empty?",
NativeFunc(|args| { NativeFunc(|args| {
arg_count!(1, args.len()); 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::List(list) => Node::Boolean(list.is_empty()),
Node::Vector(vec) => Node::Boolean(vec.is_empty()), Node::Vector(vec) => Node::Boolean(vec.is_empty()),
Node::Map(map) => Node::Boolean(map.is_empty()), Node::Map(map) => Node::Boolean(map.is_empty()),
@ -218,8 +219,9 @@ pub(super) fn core() -> HashMap<String, Value> {
"count", "count",
NativeFunc(|args| { NativeFunc(|args| {
arg_count!(1, args.len()); 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::List(list) => Node::Int(list.len() as i64),
Node::Vector(vec) => Node::Int(vec.len() as i64), Node::Vector(vec) => Node::Int(vec.len() as i64),
Node::Map(map) => Node::Int(map.len() as i64), Node::Map(map) => Node::Int(map.len() as i64),
@ -240,6 +242,151 @@ pub(super) fn core() -> HashMap<String, Value> {
Node::String(val.to_string()) 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() .into_iter()
.map(|(k, v)| (k.to_string(), Value::NativeFunc(v))) .map(|(k, v)| (k.to_string(), Value::NativeFunc(v)))

View file

@ -1,5 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::{HashMap, VecDeque};
use std::rc::Rc; use std::rc::Rc;
use crate::Node; use crate::Node;
@ -10,10 +10,10 @@ mod macros;
mod standard; mod standard;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NativeFunc(fn(args: Vec<Node>) -> Node); pub struct NativeFunc(fn(args: VecDeque<Node>) -> Node);
impl NativeFunc { impl NativeFunc {
pub fn call(&self, args: Vec<Node>) -> Node { pub fn call(&self, args: VecDeque<Node>) -> Node {
(self.0)(args) (self.0)(args)
} }
} }

View file

@ -102,7 +102,7 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
let args = list let args = list
.into_iter() .into_iter()
.map(|node| eval_node(env, node)) .map(|node| eval_node(env, node))
.collect::<Result<Vec<Node>>>()?; .collect::<Result<VecDeque<Node>>>()?;
func.call(args) func.call(args)
} }
@ -420,6 +420,16 @@ mod test {
// Strings // Strings
#[case("(str (+ 1 2))", "3")] #[case("(str (+ 1 2))", "3")]
#[case("(str (list 1 2 3))", "(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) { fn test_evaluator(#[case] input: &str, #[case] expected: &str) {
dbg!(input); dbg!(input);