/* Copyright (C) 2021 Aidan Hahn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ use crate::segment::{Ctr, Seg}; use crate::sym::SymTable; pub const APPEND_DOCSTRING: &str = "traverses any number of arguments collecting them into a list. If the first argument is a list, all other arguments are added sequentially to the end of the list contained in the first argument."; pub fn append_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { let mut temp = s.clone(); if let Ctr::Seg(ref list) = *ast.cdr { list.circuit(&mut |c: &Ctr| -> bool { temp.append(Box::new(c.clone())); true }); } else { temp.append(Box::new(*ast.cdr.clone())); } Ok(Ctr::Seg(temp)) } else { let mut temp = Seg::new(); let mut car_iter = &ast.car; let mut cdr_iter = &ast.cdr; loop { temp.append(car_iter.clone()); if let Ctr::Seg(ref s) = **cdr_iter { car_iter = &s.car; cdr_iter = &s.cdr; } else { break; } } Ok(Ctr::Seg(temp)) } } pub const LEN_DOCSTRING: &str = "Takes a single argument, expected to be a list. Returns the length of said list. Interpreter will panic if the length of the list is greater than the max value of a signed 128 bit integer"; pub fn len_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { if let Ctr::None = *s.car { Ok(Ctr::Integer(0)) } else { Ok(Ctr::Integer(s.len() as i128)) } } else { Err("impossible condition: argument to len not a list".to_string()) } } pub const CAR_DOCSTRING: &str = "Takes a single argument, expected to be a list. Returns a copy of the first value in that list. If the list is empty, returns an err to avoid passing back a null value."; pub fn car_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { if let Ctr::None = *s.car { Err("argument is empty".to_string()) } else { Ok(*s.car.clone()) } } else { Err("impossible condition: argument to car not a list".to_string()) } } pub const CDR_DOCSTRING: &str = "Takes a single argument, expected to be a list. Returns a copy of the last value in that list. If the list is empty, returns an err to avoid passing back a null value."; pub fn cdr_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { let mut ret = &s.car; let mut iter = &s.cdr; while let Ctr::Seg(ref next) = **iter { ret = &next.car; iter = &next.cdr; } if let Ctr::None = **ret { Err("argument is empty".to_string()) } else { Ok(*ret.clone()) } } else { Err("impossible condition: argument to cdr not a list".to_string()) } } pub const POP_DOCSTRING: &str = "Takes a single argument, expected to be a list. Returns a list containing the first element, and the rest of the elements. Example: (pop (1 2 3)) -> (1 (2 3)). The head can then be accessed by car and the rest can be accessed by cdr."; pub fn pop_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { if s.len() < 1 { return Err("cannot pop from empty list".to_string()); } let mut ret = Seg::new(); ret.append(s.car.clone()); if let Ctr::Seg(ref s2) = *s.cdr { ret.append(Box::new(Ctr::Seg(s2.clone()))); } else { ret.append(Box::new(Ctr::Seg(Seg::new()))); } Ok(Ctr::Seg(ret)) } else { Err("Impossible condition: arg not a list".to_string()) } } pub const DEQUEUE_DOCSTRING: &str = "Takes a single argument, expected to be a list. Returns a list containing the last element, and the rest of the elements. Example: (dq (1 2 3)) -> (3 (1 2)). The last element can then be accessed by car and the rest can be accessed by cdr."; pub fn dequeue_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { if s.len() < 1 { return Err("cannot dequeue from empty list".to_string()); } let mut rest = Seg::new(); let mut iter = s; while *iter.cdr != Ctr::None { rest.append(Box::new(*iter.car.clone())); if let Ctr::Seg(ref next) = *iter.cdr { iter = next; } else { break; } } let mut ret = Seg::new(); match *iter.car { Ctr::Seg(ref s) => { ret.append(s.car.clone()); } _ => ret.append(iter.car.clone()), } ret.append(Box::new(Ctr::Seg(rest))); Ok(Ctr::Seg(ret)) } else { Err("Impossible condition: arg not a list".to_string()) } } pub const REVERSE_DOCSTRING: &str = "Takes a single argument, expected to be a list. Returns the same list, but in reverse order."; pub fn reverse_callback(ast: &Seg, _syms: &mut SymTable) -> Result { if let Ctr::Seg(ref s) = *ast.car { let mut ret = Seg::new(); s.circuit_reverse(&mut |arg: &Ctr| -> bool { ret.append(Box::new(arg.clone())); true }); Ok(Ctr::Seg(ret)) } else { Err("Impossible condition: arg not a list".to_string()) } }