/* relish: versatile lisp shell
* 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::eval::eval;
use crate::error::{Traceback, start_trace};
use crate::segment::{Ctr, Seg, Type};
use crate::stdlib::{CONSOLE_XDIM_VNAME, RELISH_DEFAULT_CONS_WIDTH};
use crate::sym::{SymTable, Symbol, UserFn, ValueType, Args};
use std::env;
use std::rc::Rc;
const QUOTE_DOCSTRING: &str = "takes a single unevaluated tree and returns it as it is: unevaluated.";
fn quote_callback(ast: &Seg, _syms: &mut SymTable) -> Result {
if ast.len() > 1 {
Err(start_trace(("quote", "do not quote more than one thing at a time").into()))
} else {
Ok(*ast.car.clone())
}
}
const EVAL_DOCSTRING: &str = "takes an unevaluated argument and evaluates it.
Specifically, does one pass of the tree simplification algorithm.
If you have a variable referencing another variable you will get the
referenced variable.";
fn eval_callback(ast: &Seg, syms: &mut SymTable) -> Result {
if ast.len() > 1 {
Err(start_trace(
("eval", "do not eval more than one thing at a time")
.into()))
} else {
match *ast.car {
Ctr::Seg(ref s) => {
match eval(s, syms) {
Err(e) => Err(e.with_trace(
("eval", "evaluation failure")
.into())),
Ok(s) => if let Ctr::Seg(ref inner) = *s {
match eval(inner, syms) {
Err(e) => Err(e.with_trace(
("eval", "evaluation failure")
.into())),
Ok(s) => Ok(*s),
}
} else {
Ok(*s)
},
}
}
Ctr::Symbol(ref sym) => {
let intermediate = syms.call_symbol(sym, &Seg::new(), true);
if let Err(e) = intermediate {
return Err(e.with_trace(
("eval", "evaluation failure")
.into()))
}
let res = *intermediate?;
if let Ctr::Seg(ref s) = res {
match eval(s, syms) {
Err(e) => Err(e.with_trace(
("eval", "evaluation failure")
.into())),
Ok(s) => Ok(*s),
}
} else {
Ok(res)
}
},
_ => Ok(*ast.car.clone())
}
}
}
const HELP_DOCSTRING: &str = "prints help text for a given symbol. Expects only one argument.";
fn help_callback(ast: &Seg, syms: &mut SymTable) -> Result {
if ast.len() != 1 {
return Err(start_trace(("help", "expected one input").into()));
}
if let Ctr::Symbol(ref symbol) = *ast.car {
if let Some(sym) = syms.get(symbol) {
let args_str: String;
if let ValueType::VarForm(_) = sym.value {
args_str = "(its a variable)".to_string();
} else {
args_str = sym.args.to_string();
}
println!(
"NAME: {0}\n
ARGS: {1}\n
DOCUMENTATION:\n
{2}\n
CURRENT VALUE AND/OR BODY:
{3}",
sym.name, args_str, sym.docs, sym.value
);
} else {
return Err(start_trace(("help", format!("{symbol} is undefined")).into()));
}
} else {
return Err(start_trace(("help", "expected input to be a symbol").into()));
}
Ok(Ctr::None)
}
const ISSET_DOCSTRING: &str = "accepts a single argument: a symbol.
returns true or false according to whether or not the symbol is found in the symbol table.";
fn isset_callback(ast: &Seg, syms: &mut SymTable) -> Result {
if ast.len() != 1 {
Err(start_trace(("set?", "expcted one input").into()))
} else if let Ctr::Symbol(ref symbol) = *ast.car {
Ok(Ctr::Bool(syms.get(symbol).is_some()))
} else {
Err(start_trace(("set?", "expected argument to be a input").into()))
}
}
const ENV_DOCSTRING: &str = "takes no arguments
prints out all available symbols and their associated values";
fn env_callback(_ast: &Seg, syms: &mut SymTable) -> Result {
// get width of current output
let xdim: i128;
if let Ctr::Integer(dim) = *syms
.call_symbol(&CONSOLE_XDIM_VNAME.to_string(), &Seg::new(), true)
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None)) {
xdim = dim;
} else {
println!("{} contains non integer value, defaulting to {}",
CONSOLE_XDIM_VNAME, RELISH_DEFAULT_CONS_WIDTH);
xdim = RELISH_DEFAULT_CONS_WIDTH as i128;
}
let mut v_col_len = 0;
let mut f_col_len = 0;
let mut functions = vec![];
let mut variables = vec![];
for (name, val) in syms.iter() {
if let ValueType::VarForm(l) = &val.value {
let token: String = match l.to_type() {
Type::Lambda => format!("{}: ", name),
Type::Seg => format!("{}: