/* 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::segment::{Ctr, Seg, Type};
use crate::run::{run_callback, RUN_DOCSTRING};
use crate::sym::{Args, SymTable, Symbol, ValueType};
use crate::error::Traceback;
use std::rc::Rc;
use std::cell::RefCell;
use std::env::vars;
pub mod posix;
pub mod append;
pub mod boolean;
pub mod control;
pub mod decl;
pub mod math;
pub mod strings;
pub const CONSOLE_XDIM_VNAME: &str = "_RELISH_WIDTH";
pub const CONSOLE_YDIM_VNAME: &str = "_RELISH_HEIGHT";
pub const POSIX_CFG_VNAME: &str = "CFG_RELISH_POSIX";
pub const MODENV_CFG_VNAME: &str = "CFG_RELISH_ENV";
pub const L_PROMPT_VNAME: &str = "CFG_RELISH_L_PROMPT";
pub const R_PROMPT_VNAME: &str = "CFG_RELISH_R_PROMPT";
pub const PROMPT_DELIM_VNAME: &str = "CFG_RELISH_PROMPT_DELIMITER";
pub const CFG_FILE_VNAME: &str = "RELISH_CFG_FILE";
pub const RELISH_DEFAULT_CONS_HEIGHT: i16 = 24;
pub const RELISH_DEFAULT_CONS_WIDTH: i16 = 80;
fn l_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result {
Ok(Ctr::String(">".to_string()))
}
fn r_prompt_default_callback(_: &Seg, _: &mut SymTable) -> Result {
Ok(Ctr::String(String::new()))
}
fn prompt_delimiter_default_callback(_: &Seg, _: &mut SymTable) -> Result {
Ok(Ctr::String("λ ".to_string()))
}
/// static_stdlib
/// inserts all stdlib functions that can be inserted without
/// any kind of further configuration data into a symtable
pub fn static_stdlib(syms: &mut SymTable) -> Result<(), String> {
syms.insert(
"cons".to_string(),
Symbol {
name: String::from("cons"),
args: Args::Infinite,
conditional_branches: false,
docs: append::CONS_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::cons_callback)),
..Default::default()
},
);
syms.insert(
"echo".to_string(),
Symbol {
name: String::from("echo"),
args: Args::Infinite,
conditional_branches: false,
docs: strings::ECHO_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::echo_callback)),
..Default::default()
},
);
syms.insert(
"concat".to_string(),
Symbol {
name: String::from("concat"),
args: Args::Infinite,
conditional_branches: false,
docs: strings::CONCAT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::concat_callback)),
..Default::default()
},
);
syms.insert(
"substr?".to_string(),
Symbol {
name: String::from("substr?"),
args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false,
docs: strings::SUBSTR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::substr_callback)),
..Default::default()
},
);
syms.insert(
"split".to_string(),
Symbol {
name: String::from("split"),
args: Args::Strict(vec![Type::String, Type::String]),
conditional_branches: false,
docs: strings::SPLIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::split_callback)),
..Default::default()
},
);
syms.insert(
"strlen".to_string(),
Symbol {
name: String::from("strlen"),
args: Args::Lazy(1),
conditional_branches: false,
docs: strings::STRLEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::strlen_callback)),
..Default::default()
},
);
syms.insert(
"string".to_string(),
Symbol {
name: String::from("string"),
args: Args::Lazy(1),
conditional_branches: false,
docs: strings::STRCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::strcast_callback)),
..Default::default()
},
);
syms.insert(
"if".to_string(),
Symbol {
name: String::from("if"),
args: Args::Lazy(3),
conditional_branches: true,
docs: control::IF_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::if_callback)),
..Default::default()
},
);
syms.insert(
"let".to_string(),
Symbol {
name: String::from("let"),
args: Args::Infinite,
conditional_branches: true,
docs: control::LET_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::let_callback)),
..Default::default()
},
);
syms.insert(
"while".to_string(),
Symbol {
name: String::from("while"),
args: Args::Infinite,
conditional_branches: true,
docs: control::WHILE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::while_callback)),
..Default::default()
},
);
syms.insert(
"circuit".to_string(),
Symbol {
name: String::from("circuit"),
args: Args::Infinite,
conditional_branches: true,
docs: control::CIRCUIT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(control::circuit_callback)),
..Default::default()
},
);
syms.insert(
"and".to_string(),
Symbol {
name: String::from("and"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::AND_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::and_callback)),
..Default::default()
},
);
syms.insert(
"bool".to_string(),
Symbol {
name: String::from("bool"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::BOOLCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::boolcast_callback)),
..Default::default()
},
);
syms.insert(
"or".to_string(),
Symbol {
name: String::from("or"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::OR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::or_callback)),
..Default::default()
},
);
syms.insert(
"not".to_string(),
Symbol {
name: String::from("not"),
args: Args::Strict(vec![Type::Bool]),
conditional_branches: false,
docs: boolean::NOT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::not_callback)),
..Default::default()
},
);
syms.insert(
"eq?".to_string(),
Symbol {
name: String::from("eq?"),
args: Args::Infinite,
conditional_branches: false,
docs: boolean::ISEQ_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::iseq_callback)),
..Default::default()
},
);
syms.insert(
"toggle".to_string(),
Symbol {
name: String::from("toggle"),
args: Args::Lazy(1),
conditional_branches: true,
docs: boolean::TOGGLE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(boolean::toggle_callback)),
..Default::default()
},
);
syms.insert(
"help".to_string(),
Symbol {
name: String::from("help"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
docs: decl::HELP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::help_callback)),
..Default::default()
},
);
syms.insert(
"set?".to_string(),
Symbol {
name: String::from("set?"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: true,
docs: decl::ISSET_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::isset_callback)),
..Default::default()
},
);
syms.insert(
"env".to_string(),
Symbol {
name: String::from("env"),
args: Args::None,
conditional_branches: false,
docs: decl::ENV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::env_callback)),
..Default::default()
},
);
syms.insert(
"add".to_string(),
Symbol {
name: String::from("add"),
args: Args::Infinite,
conditional_branches: false,
docs: math::ADD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::add_callback)),
..Default::default()
},
);
syms.insert(
"sub".to_string(),
Symbol {
name: String::from("sub"),
args: Args::Infinite,
conditional_branches: false,
docs: math::SUB_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::sub_callback)),
..Default::default()
},
);
syms.insert(
"div".to_string(),
Symbol {
name: String::from("div"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::DIV_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::div_callback)),
..Default::default()
},
);
syms.insert(
"mul".to_string(),
Symbol {
name: String::from("mul"),
args: Args::Infinite,
conditional_branches: false,
docs: math::MUL_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::mul_callback)),
..Default::default()
},
);
syms.insert(
"int".to_string(),
Symbol {
name: String::from("int"),
args: Args::Lazy(1),
conditional_branches: false,
docs: math::INTCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::intcast_callback)),
..Default::default()
},
);
syms.insert(
"float".to_string(),
Symbol {
name: String::from("float"),
args: Args::Lazy(1),
conditional_branches: false,
docs: math::FLOATCAST_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::floatcast_callback)),
..Default::default()
},
);
syms.insert(
"len".to_string(),
Symbol {
name: String::from("len"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::LEN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::len_callback)),
..Default::default()
},
);
syms.insert(
"car".to_string(),
Symbol {
name: String::from("car"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::CAR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::car_callback)),
..Default::default()
},
);
syms.insert(
"cdr".to_string(),
Symbol {
name: String::from("cdr"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::CDR_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::cdr_callback)),
..Default::default()
},
);
syms.insert(
"pop".to_string(),
Symbol {
name: String::from("pop"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::POP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::pop_callback)),
..Default::default()
},
);
syms.insert(
"dq".to_string(),
Symbol {
name: String::from("dequeue"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::DEQUEUE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::dequeue_callback)),
..Default::default()
},
);
syms.insert(
"reverse".to_string(),
Symbol {
name: String::from("reverse"),
args: Args::Strict(vec![Type::Seg]),
conditional_branches: false,
docs: append::REVERSE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(append::reverse_callback)),
..Default::default()
},
);
syms.insert(
"exp".to_string(),
Symbol {
name: String::from("exp"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::EXP_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::exp_callback)),
..Default::default()
},
);
syms.insert(
"mod".to_string(),
Symbol {
name: String::from("mod"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::MOD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::mod_callback)),
..Default::default()
},
);
syms.insert(
"gt?".to_string(),
Symbol {
name: String::from("gt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISGT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::isgt_callback)),
..Default::default()
},
);
syms.insert(
"lt?".to_string(),
Symbol {
name: String::from("lt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISLT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::islt_callback)),
..Default::default()
},
);
syms.insert(
"gte?".to_string(),
Symbol {
name: String::from("gt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISGTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::isgte_callback)),
..Default::default()
},
);
syms.insert(
"lte?".to_string(),
Symbol {
name: String::from("lt?"),
args: Args::Lazy(2),
conditional_branches: false,
docs: math::ISLTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::islte_callback)),
..Default::default()
},
);
syms.insert(
"inc".to_string(),
Symbol {
name: String::from("inc"),
args: Args::Lazy(1),
conditional_branches: true,
docs: math::INC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::inc_callback)),
..Default::default()
},
);
syms.insert(
"dec".to_string(),
Symbol {
name: String::from("dec"),
args: Args::Lazy(1),
conditional_branches: true,
docs: math::DEC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(math::dec_callback)),
..Default::default()
},
);
syms.insert(
"quote".to_string(),
Symbol {
name: String::from("quote"),
args: Args::Lazy(1),
conditional_branches: true,
docs: decl::QUOTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::quote_callback)),
..Default::default()
},
);
syms.insert(
"q".to_string(),
Symbol {
name: String::from("quote"),
args: Args::Lazy(1),
conditional_branches: true,
docs: decl::QUOTE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::quote_callback)),
..Default::default()
},
);
syms.insert(
"eval".to_string(),
Symbol {
name: String::from("eval"),
args: Args::Lazy(1),
conditional_branches: true,
docs: decl::EVAL_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::eval_callback)),
..Default::default()
},
);
syms.insert(
"lambda".to_string(),
Symbol {
name: String::from("lambda"),
args: Args::Lazy(2),
conditional_branches: true,
docs: decl::LAMBDA_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::lambda_callback)),
..Default::default()
}
);
syms.insert(
"get-doc".to_string(),
Symbol {
name: String::from("get-doc"),
args: Args::Strict(vec![Type::Symbol]),
conditional_branches: false,
docs: decl::GETDOC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::getdoc_callback)),
..Default::default()
}
);
syms.insert(
"set-doc".to_string(),
Symbol {
name: String::from("get-doc"),
args: Args::Strict(vec![Type::Symbol, Type::String]),
conditional_branches: false,
docs: decl::SETDOC_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(decl::setdoc_callback)),
..Default::default()
}
);
syms.insert(
"call".to_string(),
Symbol {
name: String::from("call"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: RUN_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(run_callback)),
..Default::default()
}
);
syms.insert(
"input".to_string(),
Symbol {
name: String::from("input"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: strings::INPUT_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(strings::input_callback)),
..Default::default()
}
);
Ok(())
}
/// dynamic_stdlib
/// takes configuration data and uses it to insert dynamic
/// callbacks with configuration into a symtable
pub fn dynamic_stdlib(syms: &mut SymTable, shell: Option>>) -> Result<(), String> {
//get CFG_RELISH_ENV from syms
let env_cfg_user_form = syms
.call_symbol(&MODENV_CFG_VNAME.to_string(), &Seg::new(), true)
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None))
.to_string()
.eq("true");
// this also depends on the value of CFG_RELISH_ENV
syms.insert(
"def".to_string(),
Symbol {
name: String::from("define"),
args: Args::Infinite,
conditional_branches: true,
docs: decl::STORE_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(
move |ast: &Seg, syms: &mut SymTable| -> Result {
decl::store_callback(ast, syms, env_cfg_user_form)
},
)),
..Default::default()
},
);
// This should be replaced by actual compiler conditionals in the future
if let Some(shell_state) = shell {
let posix_cfg_user_form = syms
.call_symbol(&POSIX_CFG_VNAME.to_string(), &Seg::new(), true)
.unwrap_or_else(|_: Traceback| Box::new(Ctr::None))
.to_string()
.eq("true");
if posix_cfg_user_form {
posix::load_posix_shell(syms, shell_state);
syms.insert(
"cd".to_string(),
Symbol {
name: String::from("cd"),
args: Args::Strict(vec![Type::String]),
conditional_branches: false,
docs: posix::CD_DOCSTRING.to_string(),
value: ValueType::Internal(Rc::new(posix::cd_callback)),
..Default::default()
},
);
}
}
Ok(())
}
pub fn load_environment(syms: &mut SymTable) {
for (key, value) in vars() {
syms.insert(
key.clone(),
Symbol{
name: key,
args: Args::None,
conditional_branches: false,
docs: String::from("from env vars at time of load"),
value: ValueType::VarForm(Box::new(Ctr::String(value))),
..Default::default()
}
);
}
}
pub fn load_defaults(syms: &mut SymTable) {
syms.insert(
POSIX_CFG_VNAME.to_string(),
Symbol {
name: String::from(POSIX_CFG_VNAME),
args: Args::None,
conditional_branches: false,
docs: "variable holding whether or not POSIX job control functions are to be loaded.
checked at shell startup by configuration daemon. not used afterwards.
default value: false".to_string(),
value: ValueType::VarForm(Box::new(Ctr::Bool(false))),
..Default::default()
},
);
syms.insert(
MODENV_CFG_VNAME.to_string(),
Symbol {
name: String::from(MODENV_CFG_VNAME),
args: Args::None,
conditional_branches: false,
docs: "variable holding whether or not vars and other symbols should be linked to process environment variables.
If set/defined all calls to def will result in additions or subtractions from user environment variables.
checked at shell startup by configuration daemon. not used afterwards.
default value: 1 (set)
".to_string(),
value: ValueType::VarForm(Box::new(Ctr::Bool(true))),
..Default::default()
},
);
syms.insert(
L_PROMPT_VNAME.to_string(),
Symbol {
name: String::from(L_PROMPT_VNAME),
args: Args::None,
conditional_branches: false,
docs: "function called to output prompt on left hand. this function is called with no arguments."
.to_string(),
value: ValueType::Internal(Rc::new(l_prompt_default_callback)),
..Default::default()
},
);
syms.insert(
R_PROMPT_VNAME.to_string(),
Symbol {
name: String::from(R_PROMPT_VNAME),
args: Args::None,
conditional_branches: false,
docs: "function called to output prompt on right hand. this function is called with no arguments."
.to_string(),
value: ValueType::Internal(Rc::new(r_prompt_default_callback)),
..Default::default()
},
);
syms.insert(
PROMPT_DELIM_VNAME.to_string(),
Symbol {
name: String::from(PROMPT_DELIM_VNAME),
args: Args::None,
conditional_branches: false,
docs: "function called to output prompt delimiter. this function is called with no arguments."
.to_string(),
value: ValueType::Internal(Rc::new(prompt_delimiter_default_callback)),
..Default::default()
},
);
syms.insert(
String::from(CONSOLE_XDIM_VNAME),
Symbol::from_ast(
&String::from(CONSOLE_XDIM_VNAME),
&String::from("Length of current console"),
&Seg::from_mono(Box::new(
Ctr::Integer(RELISH_DEFAULT_CONS_WIDTH.into())
)),
None,
)
);
syms.insert(
String::from(CONSOLE_YDIM_VNAME),
Symbol::from_ast(
&String::from(CONSOLE_YDIM_VNAME),
&String::from("Height of current console"),
&Seg::from_mono(Box::new(
Ctr::Integer(RELISH_DEFAULT_CONS_HEIGHT.into())
)),
None,
)
);
}