/* 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; fn isnumeric(arg: &Ctr) -> bool { match arg { Ctr::Integer(_) => true, Ctr::Float(_) => true, _ => false, } } pub const ADD_DOCSTRING: &str = "traverses over N args, which must all evaluate to an Integer or Float. Adds each arg up to a final result. WARNING: does not acocunt for under/overflows. Consult source code for a better understanding of how extreme values will be handled."; pub fn add_callback(ast: &Seg, _: &mut SymTable) -> Result { let mut res = Ctr::Integer(0); let mut culprit: Ctr = Ctr::None; let type_consistent = ast.circuit(&mut |c: &Ctr| -> bool { if !isnumeric(c) { culprit = c.clone(); false } else { res = res.clone() + c.clone(); true } }); if !type_consistent { Err(format!("{} is not a number!", culprit)) } else { Ok(res) } } pub const SUB_DOCSTRING: &str = "traverses over N args, which must all evaluate to an Integer or Float. Subtracts each arg from the first leading to a final result. WARNING: does not acocunt for under/overflows. Consult source code for a better understanding of how extreme values will be handled."; pub fn sub_callback(ast: &Seg, _: &mut SymTable) -> Result { if !isnumeric(&*ast.car) { return Err(format!("{} is not a number", &*ast.car)); } let mut res = *ast.car.clone(); let mut culprit: Ctr = Ctr::None; if let Ctr::Seg(ref subsequent_operands) = *ast.cdr { let type_consistent = subsequent_operands.circuit(&mut |c: &Ctr| -> bool { if !isnumeric(c) { culprit = c.clone(); false } else { res = res.clone() - c.clone(); true } }); if !type_consistent { Err(format!("{} is not a number", culprit)) } else { Ok(res) } } else { Err("requires at least two operands".to_string()) } } pub const DIV_DOCSTRING: &str = "takes two args, which must both evaluate to an Integer or Float. divides arg1 by arg2. WARNING: does not acocunt for under/overflows or float precision. Consult source code for a better understanding of how extreme values will be handled."; pub fn div_callback(ast: &Seg, _: &mut SymTable) -> Result { let first = *ast.car.clone(); if !isnumeric(&first) { return Err("first argument must be numeric".to_string()); } let second: Ctr; if let Ctr::Seg(ref s) = *ast.cdr { second = *s.car.clone(); if !isnumeric(&second) { return Err("second argument must be numeric".to_string()); } Ok(first / second) } else { Err("impossible error: needs two arguments".to_string()) } } pub const MUL_DOCSTRING: &str = "traverses over N args, which must all evaluate to an Integer or Float. Multiplies each arg up to a final result. WARNING: does not acocunt for under/overflows. Consult source code for a better understanding of how extreme values will be handled."; pub fn mul_callback(ast: &Seg, _: &mut SymTable) -> Result { let mut res = Ctr::Integer(1); let mut culprit: Ctr = Ctr::None; let type_consistent = ast.circuit(&mut |c: &Ctr| -> bool { if !isnumeric(c) { culprit = c.clone(); false } else { res = res.clone() * c.clone(); true } }); if !type_consistent { Err(format!("{} is not a number!", culprit)) } else { Ok(res) } } pub const INTCAST_DOCSTRING: &str = "takes a single arg and attempts to cast it to an Integer. This will work for a float or a potentially a string. If the cast to Integer fails, it will return Nothing and print an error. Casting a float to an int will drop its decimal."; pub fn intcast_callback(ast: &Seg, _: &mut SymTable) -> Result { // special case for float if let Ctr::Float(f) = *ast.car { Ok(Ctr::Integer(f as i128)) } else if let Ctr::String(ref s) = *ast.car { let int = str::parse::(s); if int.is_err() { Err(int.err().unwrap().to_string()) } else { Ok(Ctr::Integer(int.ok().unwrap())) } } else { Err("int cast only takes a float or a string".to_string()) } } pub const FLOATCAST_DOCSTRING: &str = "takes a single arg and attempts to cast it to a float. This will work for an integer or potentially a string. If the cast to integer fails, this function will return nothing and print an error. Casting an integer to a float can result in bad behaviour since float nodes are based on 64bit floats and int nodes are based on 128 bit integers."; pub fn floatcast_callback(ast: &Seg, _: &mut SymTable) -> Result { // special case for float if let Ctr::Integer(i) = *ast.car { Ok(Ctr::Float(i as f64)) } else if let Ctr::String(ref s) = *ast.car { let int = str::parse::(&s); if int.is_err() { Err(int.err().unwrap().to_string()) } else { Ok(Ctr::Float(int.ok().unwrap())) } } else { Err("float cast only takes an integer or a string".to_string()) } } pub const EXP_DOCSTRING: &str = "Takes two args, both expected to be numeric. Returns the first arg to the power of the second arg. Does not handle overflow or underflow. PANIC CASES: - arg1 is a float and arg2 is greater than an int32 - an integer exceeding the size of a float64 is raised to a float power - an integer is rased to the power of another integer exceeding the max size of an unsigned 32bit integer"; pub fn exp_callback(ast: &Seg, _: &mut SymTable) -> Result { let first = *ast.car.clone(); if !isnumeric(&first) { return Err("first argument must be numeric".to_string()); } let second: Ctr; if let Ctr::Seg(ref s) = *ast.cdr { second = *s.car.clone(); } else { return Err("impossible error: needs two arguments".to_string()); } if !isnumeric(&second) { return Err("second argument must be numeric".to_string()); } match first { Ctr::Float(lf) => match second { Ctr::Float(rf) => Ok(Ctr::Float(f64::powf(lf, rf))), Ctr::Integer(ri) => Ok(Ctr::Float(f64::powi(lf, ri as i32))), _ => Err("exp not implemented for these arguments".to_string()), }, Ctr::Integer(li) => match second { Ctr::Float(rf) => Ok(Ctr::Float(f64::powf(li as f64, rf))), Ctr::Integer(ri) => Ok(Ctr::Integer(li.pow(ri as u32))), _ => Err("exp not implemented for these arguments".to_string()), }, _ => Err("exp not implemented for these arguments".to_string()), } } pub const MOD_DOCSTRING: &str = "Takes two args, both expected to be numeric. Returns a list of two values: the modulus and the remainder. Example: (mod 5 3) -> (1 2) PANIC CASES: - A float is modulo an integer larger than a max f64 - An integer larger than a max f64 is modulo a float "; pub fn mod_callback(ast: &Seg, _: &mut SymTable) -> Result { let first = *ast.car.clone(); if !isnumeric(&first) { return Err("first argument must be numeric".to_string()); } let second: Ctr; if let Ctr::Seg(ref s) = *ast.cdr { second = *s.car.clone(); } else { return Err("impossible error: needs two arguments".to_string()); } if !isnumeric(&second) { return Err("second argument must be numeric".to_string()); } let mut ret = Seg::new(); match first { Ctr::Float(lf) => match second { Ctr::Float(rf) => { ret.append(Box::new(Ctr::Integer((lf / rf) as i128))); ret.append(Box::new(Ctr::Integer((lf % rf) as i128))); } Ctr::Integer(ri) => { ret.append(Box::new(Ctr::Integer((lf / ri as f64) as i128))); ret.append(Box::new(Ctr::Integer((lf % ri as f64) as i128))); } _ => return Err("mod not implemented for these arguments".to_string()), }, Ctr::Integer(li) => match second { Ctr::Float(rf) => { ret.append(Box::new(Ctr::Integer((li as f64 / rf) as i128))); ret.append(Box::new(Ctr::Integer((li as f64 % rf) as i128))); } Ctr::Integer(ri) => { ret.append(Box::new(Ctr::Integer(li / ri))); ret.append(Box::new(Ctr::Integer(li % ri))); } _ => return Err("mod not implemented for these arguments".to_string()), }, _ => return Err("mod not implemented for these arguments".to_string()), } Ok(Ctr::Seg(ret)) }