moros/src/usr/lisp/env.rs

181 lines
8.4 KiB
Rust

use super::FUNCTIONS;
use super::primitive;
use super::eval::BUILT_INS;
use super::eval::eval_args;
use super::{Err, Exp, Number};
use crate::{could_not, expected};
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cell::RefCell;
use core::f64::consts::PI;
#[derive(Clone)]
pub struct Env {
pub data: BTreeMap<String, Exp>,
pub outer: Option<Rc<RefCell<Env>>>,
}
pub fn default_env() -> Rc<RefCell<Env>> {
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
data.insert("pi".to_string(), Exp::Num(Number::from(PI)));
data.insert("=".to_string(), Exp::Primitive(primitive::lisp_eq));
data.insert(">".to_string(), Exp::Primitive(primitive::lisp_gt));
data.insert(">=".to_string(), Exp::Primitive(primitive::lisp_gte));
data.insert("<".to_string(), Exp::Primitive(primitive::lisp_lt));
data.insert("<=".to_string(), Exp::Primitive(primitive::lisp_lte));
data.insert("*".to_string(), Exp::Primitive(primitive::lisp_mul));
data.insert("+".to_string(), Exp::Primitive(primitive::lisp_add));
data.insert("-".to_string(), Exp::Primitive(primitive::lisp_sub));
data.insert("/".to_string(), Exp::Primitive(primitive::lisp_div));
data.insert("^".to_string(), Exp::Primitive(primitive::lisp_exp));
data.insert("<<".to_string(), Exp::Primitive(primitive::lisp_shl));
data.insert(">>".to_string(), Exp::Primitive(primitive::lisp_shr));
data.insert("rem".to_string(), Exp::Primitive(primitive::lisp_rem));
data.insert("cos".to_string(), Exp::Primitive(primitive::lisp_cos));
data.insert("acos".to_string(), Exp::Primitive(primitive::lisp_acos));
data.insert("asin".to_string(), Exp::Primitive(primitive::lisp_asin));
data.insert("atan".to_string(), Exp::Primitive(primitive::lisp_atan));
data.insert("sin".to_string(), Exp::Primitive(primitive::lisp_sin));
data.insert("tan".to_string(), Exp::Primitive(primitive::lisp_tan));
data.insert("trunc".to_string(), Exp::Primitive(primitive::lisp_trunc));
data.insert("system".to_string(), Exp::Primitive(primitive::lisp_system));
data.insert("string".to_string(), Exp::Primitive(primitive::lisp_string));
data.insert("string->binary".to_string(), Exp::Primitive(primitive::lisp_string_binary));
data.insert("binary->string".to_string(), Exp::Primitive(primitive::lisp_binary_string));
data.insert("binary->number".to_string(), Exp::Primitive(primitive::lisp_binary_number));
data.insert("number->binary".to_string(), Exp::Primitive(primitive::lisp_number_binary));
data.insert("string->number".to_string(), Exp::Primitive(primitive::lisp_string_number));
data.insert("type".to_string(), Exp::Primitive(primitive::lisp_type));
data.insert("parse".to_string(), Exp::Primitive(primitive::lisp_parse));
data.insert("list".to_string(), Exp::Primitive(primitive::lisp_list));
data.insert("sort".to_string(), Exp::Primitive(primitive::lisp_sort));
data.insert("unique".to_string(), Exp::Primitive(primitive::lisp_unique));
data.insert("nth".to_string(), Exp::Primitive(primitive::lisp_nth));
data.insert("contains?".to_string(), Exp::Primitive(primitive::lisp_contains));
data.insert("slice".to_string(), Exp::Primitive(primitive::lisp_slice));
data.insert("chunks".to_string(), Exp::Primitive(primitive::lisp_chunks));
data.insert("length".to_string(), Exp::Primitive(primitive::lisp_length));
data.insert("concat".to_string(), Exp::Primitive(primitive::lisp_concat));
data.insert("number.type".to_string(), Exp::Primitive(primitive::lisp_number_type));
data.insert("regex.find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
data.insert("string.split".to_string(), Exp::Primitive(primitive::lisp_string_split));
data.insert("string.trim".to_string(), Exp::Primitive(primitive::lisp_string_trim));
data.insert("file.size".to_string(), Exp::Primitive(primitive::lisp_file_size));
data.insert("file.open".to_string(), Exp::Primitive(primitive::lisp_file_open));
data.insert("file.read".to_string(), Exp::Primitive(primitive::lisp_file_read));
data.insert("file.write".to_string(), Exp::Primitive(primitive::lisp_file_write));
data.insert("file.close".to_string(), Exp::Primitive(primitive::lisp_file_close));
// Setup autocompletion
*FUNCTIONS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();
Rc::new(RefCell::new(Env { data, outer: None }))
}
pub fn env_keys(env: &Rc<RefCell<Env>>) -> Result<Vec<String>, Err> {
let env = env.borrow_mut();
let mut keys: Vec<String> = env.data.keys().cloned().collect();
if let Some(outer_env) = &env.outer {
keys.extend_from_slice(&env_keys(outer_env)?);
}
Ok(keys)
}
pub fn env_get(key: &str, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
let env = env.borrow_mut();
match env.data.get(key) {
Some(exp) => Ok(exp.clone()),
None => {
match &env.outer {
Some(outer_env) => env_get(key, outer_env.borrow()),
None => could_not!("find symbol '{}'", key),
}
}
}
}
pub fn env_set(key: &str, val: Exp, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
let mut env = env.borrow_mut();
match env.data.get(key) {
Some(_) => {
env.data.insert(key.to_string(), val.clone());
Ok(val)
}
None => {
match &env.outer {
Some(outer_env) => env_set(key, val, outer_env.borrow()),
None => could_not!("find symbol '{}'", key),
}
}
}
}
enum InnerEnv { Function, Macro }
fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
let mut args = match kind {
InnerEnv::Function => eval_args(args, outer)?,
InnerEnv::Macro => args.to_vec(),
};
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
match params {
Exp::Sym(s) => {
data.insert(s.clone(), Exp::List(args));
}
Exp::List(list) => {
let mut list = list.to_vec();
let n = list.len();
let m = args.len();
let mut is_variadic = false;
if n > 0 {
if let Exp::List(l) = &list[n - 1] {
if l.len() == 2 && l[0] == Exp::Sym("splice".to_string()) {
if let Exp::Sym(_) = &l[1] {
is_variadic = true;
list[n - 1] = l[1].clone();
if n <= m {
let rest = args.drain((n - 1)..).collect();
args.push(Exp::List(rest));
}
}
}
}
}
let m = args.len();
if n != m {
let s = if n != 1 { "s" } else { "" };
let a = if is_variadic { "at least " } else { "" };
return expected!("{}{} argument{}, got {}", a, n, s, m);
}
for (exp, arg) in list.iter().zip(args.iter()) {
if let Exp::Sym(s) = exp {
data.insert(s.clone(), arg.clone());
} else {
return expected!("params to be a list of symbols");
}
}
}
_ => return expected!("params to be a list"),
}
Ok(Rc::new(RefCell::new(Env { data, outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))) })))
}
pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
inner_env(InnerEnv::Function, params, args, outer)
}
pub fn macro_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
inner_env(InnerEnv::Macro, params, args, outer)
}