From e0aaf3c8b0fc8063533f83839ae3da406624b62c Mon Sep 17 00:00:00 2001 From: Adam McDaniel Date: Sun, 17 Nov 2024 03:35:17 -0500 Subject: [PATCH 1/3] Added objects to language --- examples/code/fact.lisp | 8 +-- examples/code/theorem-prover.lisp | 0 examples/expected-good/fact.txt | 6 +- examples/expected-good/sort.txt | 2 +- src/lib.rs | 34 ++++++++-- src/main.rs | 105 +++++++++++++++++++++++++++--- src/parser.rs | 47 +++++++++---- 7 files changed, 166 insertions(+), 36 deletions(-) create mode 100644 examples/code/theorem-prover.lisp diff --git a/examples/code/fact.lisp b/examples/code/fact.lisp index f347574..951f39a 100644 --- a/examples/code/fact.lisp +++ b/examples/code/fact.lisp @@ -5,8 +5,8 @@ n * (fact n - 1))) (defun stirlings (n) (if n <= 0 1 - (* (sqrt 2 * 3.14159265358979323846 * n) - ((n / 2.71828182845904523536) ^ n)))) + (sqrt 2 * 3.14159265358979323846 * n) + * (pow n / 2.71828182845904523536 n))) (define cbrt (lambda (x) (^ x (/ 1 3.0)))) (define qurt (lambda (x) (^ x (/ 1 4.0)))) @@ -28,8 +28,8 @@ (defun is-even (n) (= (% n 2) 0)) (defun is-odd (n) (= (% n 2) 1)) - (println (map (\ (k v) (list k (square v))) #[x 5 y 10])) - (println (filter (\ (k v) (is-even v)) #[x 5 y 10])) + (println (map (\ (k v) (list k (square v))) [x 5 y 10])) + (println (filter (\ (k v) (is-even v)) [x 5 y 10])) (define l (range 1 10)) (defun fastfact (n) (apply (eval '*) (range 1 n))) diff --git a/examples/code/theorem-prover.lisp b/examples/code/theorem-prover.lisp new file mode 100644 index 0000000..e69de29 diff --git a/examples/expected-good/fact.txt b/examples/expected-good/fact.txt index c9b7758..04f45a8 100644 --- a/examples/expected-good/fact.txt +++ b/examples/expected-good/fact.txt @@ -1,7 +1,7 @@ 36 testing 3628800 15! -#[x 25 y 100] -#[y 10] +[x 25 y 100] +[y 10] Factorial of 4: 24 even: (2 4 6 8 10) odd: (1 3 5 7 9) -Stirling's approximation for 5! = 118.0191679575901 \ No newline at end of file +Stirling's approximation for 5! = 118.0191679575901 diff --git a/examples/expected-good/sort.txt b/examples/expected-good/sort.txt index bc2b8bb..9ff6a4b 100644 --- a/examples/expected-good/sort.txt +++ b/examples/expected-good/sort.txt @@ -1,4 +1,4 @@ 5! = 120 Stirling's approx. for 5! = 118.019297959714 Unsorted list: (5 3 7 2 8 1 9 4 6) -Sorted list: (1 2 3 4 5 6 7 8 9) \ No newline at end of file +Sorted list: (1 2 3 4 5 6 7 8 9) diff --git a/src/lib.rs b/src/lib.rs index f075e3f..809b335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -455,7 +455,7 @@ impl Env { let func = args.remove(0); let func = self.eval(func); - match func { + match func.strip_object() { Function(env, params, body) => { // saved_bindings = self.bindings.clone(); is_in_new_env = true; @@ -464,11 +464,15 @@ impl Env { } if params.len() != args.len() { - return Expr::Err(Box::new(Expr::String(format!( - "Expected {} arguments, got {}", - params.len(), - args.len() - )))); + if params.len() == 0 && args.len() == 1 { + // Do nothing + } else { + return Expr::Err(Box::new(Expr::String(format!( + "Expected {} arguments, got {}", + params.len(), + args.len() + )))); + } } let args = args @@ -710,6 +714,7 @@ pub enum Expr { /// it returns the expression itself, without evaluating it. This is useful for defining /// special forms, or for returning unevaluated expressions from functions, like symbols. Quote(Box), + /// An error. /// /// When an error occurs during evaluation, this is used to wrap an error value @@ -724,10 +729,14 @@ pub enum Expr { /// Internally, the function also keeps track of the environment in which it was defined, /// which allows it to capture bindings to variables defined outside the function. Function(Option>, Vec, Box), + /// A builtin function. /// /// This is used to represent a function that is defined in Rust, and can be called from lisp. Builtin(Builtin), + + /// An object. + Object(Arc>), } /// Convert a String to an Expr conveniently. @@ -738,6 +747,7 @@ impl From for Expr { Self::String(s) } } + /// Convert a &str to an Expr conveniently. /// /// This will return a Lisp expression that represents the string, not a symbol. @@ -884,6 +894,14 @@ impl Expr { Self::List(result) } + /// Strip the object from an expression, returning the inner expression. + fn strip_object(&self) -> Self { + match self { + Self::Object(o) => o.read().unwrap().clone(), + _ => self.clone(), + } + } + /// Parse a string into a Lisp expression. /// /// If the string is a valid Lisp expression, it will return the parsed expression. @@ -1008,6 +1026,7 @@ impl PartialOrd for Expr { } (Bool(b1), Bool(b2)) => b1.partial_cmp(b2), (Many(d1), Many(d2)) => d1.partial_cmp(d2), + (Object(r1), Object(r2)) => r1.read().unwrap().partial_cmp(&r2.read().unwrap()), _ => Option::None, } } @@ -1046,6 +1065,7 @@ impl Hash for Expr { Err(_) => 11, Function(_, _, _) => 12, Builtin(_) => 13, + Object(_) => 14, }); match self { @@ -1066,6 +1086,7 @@ impl Hash for Expr { body.hash(state); } Builtin(f) => (f as *const _ as usize).hash(state), + Object(r) => r.read().unwrap().hash(state), } } } @@ -1136,6 +1157,7 @@ impl Display for Expr { write!(f, ") {})", body) } Builtin(b) => write!(f, "", b.name), + Object(r) => write!(f, "", r.read().unwrap()), } } } diff --git a/src/main.rs b/src/main.rs index b075ef8..8eb72eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,10 @@ use rustyline::error::ReadlineError; use rustyline::DefaultEditor; use sage_lisp::*; // use sage::{frontend, lir::Compile, parse::*, targets::CompiledTarget, vm::*}; -use std::io::BufRead; +use std::{ + io::BufRead, + sync::{Arc, RwLock}, +}; #[derive(Parser)] @@ -194,6 +197,34 @@ fn make_env() -> Env { } }); + env.bind_builtin("new", |env, exprs| { + // Create a new object from the value passed + let value = env.eval(exprs[0].clone()); + Expr::Object(Arc::new(RwLock::new(value))) + }); + fn set(env: &mut Env, exprs: Vec) -> Expr { + // Set the value of an object + let object = env.eval(exprs[0].clone()); + let value = env.eval(exprs[1].clone()); + if let Expr::Object(object) = object { + *object.write().unwrap() = value.clone(); + value + } else { + Expr::error(format!("Invalid object {object} set {value}")) + } + } + env.bind_builtin("<-", set); + + env.bind_builtin("$", |env, exprs| { + // Get the value of an object + let object = env.eval(exprs[0].clone()); + if let Expr::Object(object) = object { + object.read().unwrap().clone() + } else { + Expr::error(format!("Invalid object {object} get")) + } + }); + env.bind_builtin("define", |env, exprs| { let name = exprs[0].clone(); let value = env.eval(exprs[1].clone()); @@ -531,17 +562,33 @@ fn make_env() -> Env { new_env.eval(body) }); - env.bind_builtin("get", |env, expr| { + fn get(env: &mut Env, expr: Vec) -> Expr { let a = env.eval(expr[0].clone()); let b = env.eval(expr[1].clone()); match (a, b) { + (Expr::Object(obj), member) => { + let val = obj.read().unwrap(); + match get(env, vec![val.clone(), member.clone()]) { + Expr::None => { + // Create a new object with the member as the value + let new_obj = Expr::Object(Arc::new(RwLock::new(Expr::None))); + let set_obj = set_attr(env, vec![val.clone(), member.clone(), new_obj.clone()]); + drop(val); + // Add the member to the object + // Write the new object to the object + *obj.write().unwrap() = set_obj; + new_obj + } + other => other, + } + + } (Expr::String(a), Expr::Int(b)) => { Expr::String(a.chars().nth(b as usize).unwrap_or('\0').to_string()) } (Expr::List(a), Expr::Int(b)) => a.get(b as usize).cloned().unwrap_or(Expr::None), (Expr::Map(a), Expr::Symbol(b)) => { - // a.get(&b).cloned().unwrap_or(Expr::None) a.get(&Expr::Symbol(b.clone())).cloned().unwrap_or_else(|| { a.get(&Expr::String(b.name().to_owned())) .cloned() @@ -552,10 +599,52 @@ fn make_env() -> Env { (Expr::Tree(a), b) => a.get(&b).cloned().unwrap_or(Expr::None), (a, b) => return Expr::error(format!("Invalid expr get {} {}", a, b)), } - }); - env.alias("get", "@"); + } + env.bind_builtin("get", get); + env.alias("get", "."); + + fn method(env: &mut Env, expr: Vec) -> Expr { + // Apply a method with a self parameter + let obj = env.eval(expr[0].clone()); + let method_name = expr[1].clone(); + + // Get the param count + let method = get(env, vec![obj.clone(), method_name.clone()]); + let param_count = match &method { + Expr::Object(obj) => { + if let Expr::Function(_, params, _) = &*obj.read().unwrap() { + params.len() + } else { + return Expr::error(format!("Invalid method {method}")); + } + } + Expr::Function(_, params, _) => params.len(), + _ => return Expr::error(format!("Invalid method {method}")), + }; + // Return a function that takes the rest of the arguments, + // and applies the method with the object as the first argument. + let env = env.clone(); + + let mut args = vec![obj.clone()]; + let mut params = vec![]; + for i in 1..param_count { + let name = Symbol::from(format!("arg{}", i - 2)); + args.push(Expr::Symbol(name.clone())); + params.push(Expr::Symbol(name)); + } - env.bind_builtin("set", |env, expr| { + let body = method.apply(&args); + + let f = Expr::Function(None, params, Box::new(body)); + f + + // let method = get(env, vec![obj.clone(), method_name]); + // // Apply the method with the object as the first argument, and the rest of the arguments. + // let mut args = vec![obj]; + // args.extend(expr[2..].iter().cloned()); + } + env.bind_builtin("@", method); + fn set_attr(env: &mut Env, expr: Vec) -> Expr { let a = env.eval(expr[0].clone()); let b = env.eval(expr[1].clone()); let c = env.eval(expr[2].clone()); @@ -590,7 +679,8 @@ fn make_env() -> Env { } (a, b) => return Expr::error(format!("Invalid expr set {} {} {}", a, b, c)), } - }); + } + env.bind_builtin("set", set_attr); env.bind_builtin("zip", |env, expr| { let a = env.eval(expr[0].clone()); @@ -983,7 +1073,6 @@ fn main() { } Err(ReadlineError::Interrupted) => { println!("CTRL-C"); - break; } Err(ReadlineError::Eof) => { println!("CTRL-D"); diff --git a/src/parser.rs b/src/parser.rs index 051883c..4b469bd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -190,10 +190,21 @@ pub fn parse_expr<'a, E: ParseError<&'a str> + ContextError<&'a str>>( ) -> IResult<&'a str, Expr, E> { preceded( multispace0, - terminated(alt((parse_compare, parse_atom)), multispace0), + terminated(alt((parse_assign, parse_compare, parse_atom)), multispace0), )(input) } +fn parse_assign<'a, E: ParseError<&'a str> + ContextError<&'a str>>( + input: &'a str, +) -> IResult<&'a str, Expr, E> { + let (input, lhs) = parse_access(input)?; + let (input, _) = multispace0(input)?; + let (input, op) = tag("<-")(input)?; + let (input, _) = multispace0(input)?; + let (input, rhs) = parse_expr(input)?; + Ok((input, Expr::symbol("<-").apply(&[lhs, rhs]))) +} + fn parse_compare<'a, E: ParseError<&'a str> + ContextError<&'a str>>( input: &'a str, ) -> IResult<&'a str, Expr, E> { @@ -270,7 +281,7 @@ fn parse_mul<'a, E: ParseError<&'a str> + ContextError<&'a str>>( fn parse_pow<'a, E: ParseError<&'a str> + ContextError<&'a str>>( input: &'a str, ) -> IResult<&'a str, Expr, E> { - let (input, lhs) = parse_access(input)?; + let (input, lhs) = parse_get(input)?; let (input, _) = multispace0(input)?; let (input, op) = opt(one_of("^"))(input)?; if let Some(op) = op { @@ -287,33 +298,41 @@ fn parse_pow<'a, E: ParseError<&'a str> + ContextError<&'a str>>( } } +fn parse_get<'a, E: ParseError<&'a str> + ContextError<&'a str>>( + input: &'a str, +) -> IResult<&'a str, Expr, E> { + // Parse a sigil `$` which dereferences an object + let (input, sigil) = opt(tag("$"))(input)?; + // Parse the expression to dereference + let (input, expr) = parse_access(input)?; + if sigil.is_none() { + return Ok((input, expr)); + } + // Return the dereferenced expression + Ok((input, Expr::symbol('$').apply(&[expr]))) +} + fn parse_access<'a, E: ParseError<&'a str> + ContextError<&'a str>>( input: &'a str, ) -> IResult<&'a str, Expr, E> { let (input, lhs) = parse_atom(input)?; let (input, _) = multispace0(input)?; - let (input, op) = opt(one_of("@"))(input)?; - if op.is_some() { + let (input, op) = opt(one_of("@."))(input)?; + if let Some(op) = op { let (input, rhs) = parse_atom(input)?; - // Ok((input, match op { - // '@' => Expr::symbol('@').apply(&[lhs, rhs]), - // _ => unreachable!(), - // })) - let mut result = Expr::symbol('@').apply(&[lhs, rhs]); + let mut result = Expr::symbol(op).apply(&[lhs, rhs]); let (mut input, _) = multispace0(input)?; // See if there's another access - while let Ok((i, _)) = tag::<&str, &str, E>("@")(input) { + while let Ok((i, op)) = one_of::<&str, &str, E>("@.")(input) { let (i, rhs) = parse_atom(i)?; - result = Expr::symbol('@').apply(&[result.clone(), rhs]); + result = Expr::symbol(op).apply(&[result.clone(), rhs]); let (i, _) = multispace0(i)?; input = i; } - // println!("Result: {}", result); - Ok((input, result)) } else { Ok((input, lhs)) @@ -329,9 +348,9 @@ fn parse_quote<'a, E: ParseError<&'a str> + ContextError<&'a str>>( fn is_symbol_char(c: char) -> bool { c.is_alphanumeric() || c == '_' + || c == '$' || c == '?' || c == '!' - || c == '.' || c == '-' || c == '+' || c == '*' From b7f45d1c8c38000cf07ce37276578fb1457cca19 Mon Sep 17 00:00:00 2001 From: Adam McDaniel Date: Tue, 19 Nov 2024 22:53:58 -0500 Subject: [PATCH 2/3] Added optional extensions which include oop patterns, modules, system info, and lots of functionality! --- Cargo.lock | 140 ++- Cargo.toml | 1 + examples/code/fact.lisp | 8 + examples/code/hello-world.lisp | 5 + examples/code/method.lisp | 9 + examples/code/sort.lisp | 17 +- examples/code/struct.lisp | 35 + examples/code/theorem-prover.lisp | 0 src/extensions/arithmetic.rs | 399 +++++++ src/extensions/environment.rs | 74 ++ src/extensions/filesystem.rs | 183 +++ src/extensions/format.rs | 254 ++++ src/extensions/functional.rs | 199 ++++ src/extensions/io.rs | 25 + src/extensions/list.rs | 535 +++++++++ src/extensions/mod.rs | 28 + src/extensions/modules.rs | 36 + src/extensions/oop.rs | 268 +++++ src/extensions/procedural.rs | 130 ++ src/extensions/random.rs | 56 + src/extensions/system.rs | 127 ++ src/lib.rs | 35 +- src/main.rs | 1826 ++++++++++++++--------------- 23 files changed, 3441 insertions(+), 949 deletions(-) create mode 100644 examples/code/hello-world.lisp create mode 100644 examples/code/method.lisp create mode 100644 examples/code/struct.lisp delete mode 100644 examples/code/theorem-prover.lisp create mode 100644 src/extensions/arithmetic.rs create mode 100644 src/extensions/environment.rs create mode 100644 src/extensions/filesystem.rs create mode 100644 src/extensions/format.rs create mode 100644 src/extensions/functional.rs create mode 100644 src/extensions/io.rs create mode 100644 src/extensions/list.rs create mode 100644 src/extensions/mod.rs create mode 100644 src/extensions/modules.rs create mode 100644 src/extensions/oop.rs create mode 100644 src/extensions/procedural.rs create mode 100644 src/extensions/random.rs create mode 100644 src/extensions/system.rs diff --git a/Cargo.lock b/Cargo.lock index 8213b4f..9469fa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -288,12 +288,60 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "endian-type" version = "0.1.2" @@ -410,6 +458,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "home" version = "0.5.9" @@ -445,6 +499,17 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys", +] + [[package]] name = "itertools" version = "0.10.5" @@ -481,6 +546,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "libyml" version = "0.0.3" @@ -609,6 +684,20 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -687,6 +776,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.5" @@ -729,6 +829,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "rustyline" version = "14.0.0" @@ -767,6 +873,7 @@ dependencies = [ "env_logger", "lazy_static", "nom", + "prettytable-rs", "rand", "rustyline", "serde", @@ -867,12 +974,43 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "textwrap" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +[[package]] +name = "thiserror" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinytemplate" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 91bdb2d..8478121 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ serde = "1.0.204" serde_yml = "0.0.10" serde_json = "1.0.120" env_logger = "0.11.3" +prettytable-rs = "0.10.0" [dev-dependencies] criterion = "0.4" diff --git a/examples/code/fact.lisp b/examples/code/fact.lisp index 951f39a..0902053 100644 --- a/examples/code/fact.lisp +++ b/examples/code/fact.lisp @@ -1,4 +1,12 @@ { + (mod.use math) + (mod.use io) + (mod.use fn) + (mod.use env) + (mod.use proc) + (mod.use list) + (mod.use fmt) + (defun fact (n) (if n <= 0 1 diff --git a/examples/code/hello-world.lisp b/examples/code/hello-world.lisp new file mode 100644 index 0000000..ee3eebe --- /dev/null +++ b/examples/code/hello-world.lisp @@ -0,0 +1,5 @@ +{ + (mod.use io) + + (println "Hello, World!") +} \ No newline at end of file diff --git a/examples/code/method.lisp b/examples/code/method.lisp new file mode 100644 index 0000000..c7be32e --- /dev/null +++ b/examples/code/method.lisp @@ -0,0 +1,9 @@ +{ + (define x (new #[])) + + x.method <- (lambda (self test) (println "Hello, World! " self.a " " test)) + + (x.method ()) + x.a <- 100 + (x.method ()) +} \ No newline at end of file diff --git a/examples/code/sort.lisp b/examples/code/sort.lisp index 9917f8e..1a819d1 100644 --- a/examples/code/sort.lisp +++ b/examples/code/sort.lisp @@ -1,5 +1,13 @@ -(do +{ + (mod.use math) + (mod.use io) + (mod.use fn) + (mod.use env) + (mod.use proc) + (mod.use list) + (mod.use fmt) + (defun fact (n) (if (<= n 0) 1 (* n (fact (- n 1))))) @@ -15,8 +23,8 @@ (println "Stirling's approx. for 5! = " (stirlings 5)) (defun quicksort (lst) - (if (<= (len lst) 1) lst { - (define pivot (get lst (/ (len lst) 2))) + (if (<= (length lst) 1) lst { + (define pivot (get lst (/ (length lst) 2))) (define less (filter (\(x) (< x pivot)) lst)) (define equal (filter (\(x) (= x pivot)) lst)) (define greater (filter (\(x) (> x pivot)) lst)) @@ -24,4 +32,5 @@ (define test-list (list 5 3 7 2 8 1 9 4 6)) (println "Unsorted list: " test-list) - (println "Sorted list: " (quicksort test-list))) \ No newline at end of file + (println "Sorted list: " (quicksort test-list)) +} \ No newline at end of file diff --git a/examples/code/struct.lisp b/examples/code/struct.lisp new file mode 100644 index 0000000..d590c13 --- /dev/null +++ b/examples/code/struct.lisp @@ -0,0 +1,35 @@ +{ + (oop.struct Point + (new (self a b) { + self.x <- a + self.y <- b + }) + + (defun move (self dx dy) { + self.x <- (math.sum $self.x dx) + self.y <- (math.sum $self.y dy) + }) + + (defun print (self) { + (io.println (self.show)) + }) + + (defun debug (self) { + (io.println (self.repr)) + }) + + (defun show (self) { + (fmt.format "Point({}, {})" $self.x $self.y) + }) + + (defun repr (self) { + (fmt.show self) + })) + + (env.define p (Point 10 20)) + + (p.debug) + (p.print) + (p.move 5 5) + (p.print) +} \ No newline at end of file diff --git a/examples/code/theorem-prover.lisp b/examples/code/theorem-prover.lisp deleted file mode 100644 index e69de29..0000000 diff --git a/src/extensions/arithmetic.rs b/src/extensions/arithmetic.rs new file mode 100644 index 0000000..7932e2a --- /dev/null +++ b/src/extensions/arithmetic.rs @@ -0,0 +1,399 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("range", range); + env.bind_builtin("sum", sum); + env.bind_builtin("+", sum); + env.bind_builtin("difference", difference); + env.bind_builtin("-", difference); + env.bind_builtin("product", product); + env.bind_builtin("*", product); + env.bind_builtin("quotient", quotient); + env.bind_builtin("/", quotient); + env.bind_builtin("remainder", remainder); + env.bind_builtin("%", remainder); + env.bind_builtin("=", equality); + env.bind_builtin("!=", inequality); + env.bind_builtin("<", less_than); + env.bind_builtin("<=", less_than_or_equal); + env.bind_builtin(">", greater_than); + env.bind_builtin(">=", greater_than_or_equal); + env.bind_builtin("pow", raise_to_power); + env.bind_builtin("sqrt", square_root); + env.bind_builtin("abs", absolute_value); + env.bind_builtin("round", round); + env.bind_builtin("floor", floor); + env.bind_builtin("ceiling", ceiling); + env.bind_builtin("truncate", truncate); + env.bind_builtin("abs_diff", absolute_difference); + env.bind_builtin("max", maximum); + env.bind_builtin("min", minimum); + + env.bind_builtin("&&", boolean_and); + env.bind_builtin("||", boolean_or); + env.bind_builtin("!", boolean_not); + env.bind_builtin("&", bitwise_and); + env.bind_builtin("|", bitwise_or); + env.bind_builtin("^", bitwise_xor); + env.bind_builtin("~", bitwise_not); + env.bind_builtin("<<", left_shift); + env.bind_builtin(">>", arithmetic_right_shift); + env.bind_builtin(">>>", logical_right_shift); +} + +fn range_to_list(start: i64, end: i64) -> Expr { + let mut list = vec![]; + for i in start..end { + list.push(Expr::Int(i)); + } + Expr::List(list) +} + +fn range(env: &mut Env, low_and_high: Vec) -> Expr { + let low = env.eval(low_and_high.get(0).cloned().unwrap_or(Expr::Int(0))); + let high = env.eval(low_and_high.get(1).cloned().unwrap_or(Expr::Int(0))); + + match (low, high) { + (Expr::Int(low), Expr::Int(high)) => range_to_list(low, high), + (low, high) => Expr::error(format!("Invalid range {}..{}", low, high)), + } +} + + +fn boolean_and(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Bool(a), Expr::Bool(b)) => Expr::Bool(a && b), + (a, b) => Expr::error(format!("Invalid expr {} && {}", a, b)), + } +} + +fn boolean_or(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Bool(a), Expr::Bool(b)) => Expr::Bool(a || b), + (a, b) => Expr::error(format!("Invalid expr {} || {}", a, b)), + } +} + +fn boolean_not(env: &mut Env, expr: Vec) -> Expr { + let a = env.eval(expr[0].clone()); + match a { + Expr::Bool(a) => Expr::Bool(!a), + a => Expr::error(format!("Invalid expr !{}", a)), + } +} + +fn bitwise_and(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int(a & b), + (a, b) => Expr::error(format!("Invalid expr {} & {}", a, b)), + } +} + +fn bitwise_or(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int(a | b), + (a, b) => Expr::error(format!("Invalid expr {} | {}", a, b)), + } +} + +fn bitwise_xor(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int(a ^ b), + (a, b) => Expr::error(format!("Invalid expr {} ^ {}", a, b)), + } +} + +fn bitwise_not(env: &mut Env, expr: Vec) -> Expr { + let a = env.eval(expr[0].clone()); + match a { + Expr::Int(a) => Expr::Int(!a), + a => Expr::error(format!("Invalid expr ~{}", a)), + } +} + +fn left_shift(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int(a << b), + (a, b) => Expr::error(format!("Invalid expr {} << {}", a, b)), + } +} + +fn logical_right_shift(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int((a as u64 >> b) as i64), + (a, b) => Expr::error(format!("Invalid expr {} >> {}", a, b)), + } +} + +fn arithmetic_right_shift(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int(a >> b), + (a, b) => Expr::error(format!("Invalid expr {} >>> {}", a, b)), + } +} + +fn sum(env: &mut Env, exprs: Vec) -> Expr { + let mut sum = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + // sum += env.eval(e); + match (sum, e) { + (Expr::None, b) => sum = b, + (Expr::Int(a), Expr::Int(b)) => sum = Expr::Int(a + b), + (Expr::Float(a), Expr::Float(b)) => sum = Expr::Float(a + b), + (Expr::Int(a), Expr::Float(b)) => sum = Expr::Float(a as f64 + b), + (Expr::Float(a), Expr::Int(b)) => sum = Expr::Float(a + b as f64), + (Expr::String(a), Expr::String(b)) => sum = Expr::String(format!("{}{}", a, b)), + (Expr::List(a), Expr::List(b)) => { + let mut list = a.clone(); + list.extend(b); + sum = Expr::List(list); + } + (Expr::List(a), b) => { + let mut list = a.clone(); + list.push(b); + sum = Expr::List(list); + } + (a, b) => return Expr::error(format!("Invalid expr {} + {}", a, b)), + } + } + sum +} + +fn difference(env: &mut Env, exprs: Vec) -> Expr { + let mut diff = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + match (diff, e) { + (Expr::None, b) => diff = b, + (Expr::Int(a), Expr::Int(b)) => diff = Expr::Int(a - b), + (Expr::Float(a), Expr::Float(b)) => diff = Expr::Float(a - b), + (Expr::Int(a), Expr::Float(b)) => diff = Expr::Float(a as f64 - b), + (Expr::Float(a), Expr::Int(b)) => diff = Expr::Float(a - b as f64), + (a, b) => return Expr::error(format!("Invalid expr {} - {}", a, b)), + } + } + diff +} + +fn product(env: &mut Env, exprs: Vec) -> Expr { + let mut product = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + match (product, e) { + (Expr::None, b) => product = b, + (Expr::Int(a), Expr::Int(b)) => product = Expr::Int(a * b), + (Expr::Float(a), Expr::Float(b)) => product = Expr::Float(a * b), + (Expr::Int(a), Expr::Float(b)) => product = Expr::Float(a as f64 * b), + (Expr::Float(a), Expr::Int(b)) => product = Expr::Float(a * b as f64), + (Expr::List(a), Expr::Int(b)) => { + let mut list = a.clone(); + for _ in 0..b { + list.extend(a.clone()); + } + product = Expr::List(list); + } + (a, b) => return Expr::error(format!("Invalid expr {} * {}", a, b)), + } + } + product +} + +fn quotient(env: &mut Env, exprs: Vec) -> Expr { + let mut quotient = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + match (quotient, e) { + (Expr::None, b) => quotient = b, + (Expr::Int(a), Expr::Int(b)) => quotient = Expr::Int(a / b), + (Expr::Float(a), Expr::Float(b)) => quotient = Expr::Float(a / b), + (Expr::Int(a), Expr::Float(b)) => quotient = Expr::Float(a as f64 / b), + (Expr::Float(a), Expr::Int(b)) => quotient = Expr::Float(a / b as f64), + (a, b) => return Expr::error(format!("Invalid expr {} / {}", a, b)), + } + } + quotient +} + +fn remainder(env: &mut Env, exprs: Vec) -> Expr { + let mut quotient = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + match (quotient, e) { + (Expr::None, b) => quotient = b, + (Expr::Int(a), Expr::Int(b)) => quotient = Expr::Int(a % b), + (Expr::Float(a), Expr::Float(b)) => quotient = Expr::Float(a % b), + (Expr::Int(a), Expr::Float(b)) => quotient = Expr::Float(a as f64 % b), + (Expr::Float(a), Expr::Int(b)) => quotient = Expr::Float(a % b as f64), + (a, b) => return Expr::error(format!("Invalid expr {} % {}", a, b)), + } + } + quotient +} + +fn equality(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + Expr::Bool(a == b) +} + +fn inequality(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + Expr::Bool(a != b) +} + +fn less_than(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + Expr::Bool(a < b) +} + +fn less_than_or_equal(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + Expr::Bool(a <= b) +} + +fn greater_than(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + Expr::Bool(a > b) +} + +fn greater_than_or_equal(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + + Expr::Bool(a >= b) +} + +fn raise_to_power(env: &mut Env, exprs: Vec) -> Expr { + let base = env.eval(exprs[0].clone()); + let exponent = env.eval(exprs[1].clone()); + match (base, exponent) { + (Expr::Int(base), Expr::Int(exponent)) => Expr::Float((base as f64).powf(exponent as f64)), + (Expr::Float(base), Expr::Float(exponent)) => Expr::Float(base.powf(exponent)), + (Expr::Int(base), Expr::Float(exponent)) => Expr::Float((base as f64).powf(exponent)), + (Expr::Float(base), Expr::Int(exponent)) => Expr::Float(base.powf(exponent as f64)), + (base, exponent) => Expr::error(format!("Invalid base {base} and exponent {exponent}")) + } +} + +fn square_root(env: &mut Env, expr: Vec) -> Expr { + let square = env.eval(expr[0].clone()); + match square { + Expr::Int(square) => Expr::Float((square as f64).sqrt()), + Expr::Float(square) => Expr::Float(square.sqrt()), + val => Expr::error(format!("Invalid square root of {val}")) + } +} + +fn absolute_value(env: &mut Env, expr: Vec) -> Expr { + let n = env.eval(expr[0].clone()); + match n { + Expr::Int(n) => Expr::Int(n.abs()), + Expr::Float(n) => Expr::Float(n.abs()), + n => Expr::error(format!("Invalid absolute value of {n}")) + } +} + +fn round(env: &mut Env, expr: Vec) -> Expr { + let n = env.eval(expr[0].clone()); + match n { + Expr::Int(n) => Expr::Int(n), + Expr::Float(n) => Expr::Int(n.round() as i64), + n => Expr::error(format!("Invalid value to round {n}")) + } +} + +fn floor(env: &mut Env, expr: Vec) -> Expr { + let n = env.eval(expr[0].clone()); + match n { + Expr::Int(n) => Expr::Int(n), + Expr::Float(n) => Expr::Int(n.floor() as i64), + n => Expr::error(format!("Invalid value to floor {n}")) + } +} + +fn ceiling(env: &mut Env, expr: Vec) -> Expr { + let n = env.eval(expr[0].clone()); + match n { + Expr::Int(n) => Expr::Int(n), + Expr::Float(n) => Expr::Int(n.ceil() as i64), + n => Expr::error(format!("Invalid value to ceiling {n}")) + } +} + +fn truncate(env: &mut Env, expr: Vec) -> Expr { + let n = env.eval(expr[0].clone()); + match n { + Expr::Int(n) => Expr::Int(n), + Expr::Float(n) => Expr::Int(n.trunc() as i64), + n => Expr::error(format!("Invalid value to truncate {n}")) + } +} + +fn absolute_difference(env: &mut Env, exprs: Vec) -> Expr { + let a = env.eval(exprs[0].clone()); + let b = env.eval(exprs[1].clone()); + match (a, b) { + (Expr::Int(a), Expr::Int(b)) => Expr::Int((a - b).abs()), + (Expr::Float(a), Expr::Float(b)) => Expr::Float((a - b).abs()), + (Expr::Int(a), Expr::Float(b)) => Expr::Float((a as f64 - b).abs()), + (Expr::Float(a), Expr::Int(b)) => Expr::Float((a - b as f64).abs()), + (a, b) => Expr::error(format!("Invalid expr {} - {}", a, b)), + } +} + +fn maximum(env: &mut Env, exprs: Vec) -> Expr { + let mut max = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + if max < e { + max = e; + } + } + max +} + +fn minimum(env: &mut Env, exprs: Vec) -> Expr { + let mut min = Expr::default(); + for e in exprs { + let e = env.eval(e.clone()); + if min > e { + min = e; + } + } + min +} + diff --git a/src/extensions/environment.rs b/src/extensions/environment.rs new file mode 100644 index 0000000..1c487ca --- /dev/null +++ b/src/extensions/environment.rs @@ -0,0 +1,74 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("env", env_as_map); + env.bind_builtin("define", define_binding); + env.bind_builtin("undefine", undefine_binding); + env.bind_builtin("defun", define_function); + env.bind_builtin("let", let_bindings_in_new_environment); +} + +fn env_as_map(env: &mut Env, _exprs: Vec) -> Expr { + return Expr::Map(env.get_bindings()); +} + +fn define_binding(env: &mut Env, exprs: Vec) -> Expr { + let name = exprs[0].clone(); + let value = env.eval(exprs[1].clone()); + env.bind(name, value.clone()); + value +} + +fn undefine_binding(env: &mut Env, expr: Vec) -> Expr { + let name = expr[0].clone(); + env.unbind(&name); + Expr::None +} + +fn define_function(env: &mut Env, args: Vec) -> Expr { + if args.len() != 3 { + return Expr::error(format!("Invalid function definition {:?}", args)); + } + + let name = args[0].clone(); + let params = args[1].clone(); + let body = args[2].clone(); + if let Expr::List(params) = params { + let f = env.eval(Expr::Function(None, params, Box::new(body))); + env.bind(name, f); + Expr::None + } else { + return Expr::error(format!("Invalid params {:?}", params)); + } +} + +fn let_bindings_in_new_environment(env: &mut Env, args: Vec) -> Expr { + let mut new_env = env.clone(); + let bindings = args[0].clone(); + let body = args[1].clone(); + match bindings { + Expr::List(bindings) => { + for binding in bindings { + if let Expr::List(binding) = binding { + let name = binding[0].clone(); + let value = env.eval(binding[1].clone()); + new_env.bind(name, value); + } else { + return Expr::error(format!("Invalid let-binding {binding}")); + } + } + } + Expr::Map(bindings) => { + for (name, value) in bindings { + new_env.bind(name, env.eval(value)); + } + } + Expr::Tree(bindings) => { + for (name, value) in bindings { + new_env.bind(name, env.eval(value)); + } + } + bindings => return Expr::error(format!("Invalid let-bindings {bindings}")), + } + new_env.eval(body) +} \ No newline at end of file diff --git a/src/extensions/filesystem.rs b/src/extensions/filesystem.rs new file mode 100644 index 0000000..d2f1dfb --- /dev/null +++ b/src/extensions/filesystem.rs @@ -0,0 +1,183 @@ +use std::collections::HashMap; + +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("write", write_to_file); + env.bind_builtin("read", read_from_file); + env.bind_builtin("remove", remove_file); + env.bind_builtin("create-dir", create_dir); + env.bind_builtin("remove-dir", remove_dir); + env.bind_builtin("rename", rename_file); + env.bind_builtin("copy", copy_file); + env.bind_builtin("metadata", metadata); + env.bind_builtin("list-dir", list_dir); + env.bind_builtin("canonicalize", canonicalize_path); +} + +fn write_to_file(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let data = env.eval(args[1].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + let data = match data { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid data {:?}", data)), + }; + + match std::fs::write(path, data) { + Ok(_) => Expr::None, + Err(e) => Expr::error(format!("Error writing to file: {:?}", e)), + } +} + +fn read_from_file(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::read_to_string(path) { + Ok(data) => Expr::String(data), + Err(e) => Expr::error(format!("Error reading from file: {:?}", e)), + } +} + +fn remove_file(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::remove_file(path) { + Ok(_) => Expr::None, + Err(e) => Expr::error(format!("Error removing file: {:?}", e)), + } +} + +fn create_dir(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::create_dir(path) { + Ok(_) => Expr::None, + Err(e) => Expr::error(format!("Error creating directory: {:?}", e)), + } +} + +fn remove_dir(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::remove_dir(path) { + Ok(_) => Expr::None, + Err(e) => Expr::error(format!("Error removing directory: {:?}", e)), + } +} + +fn rename_file(env: &mut Env, args: Vec) -> Expr { + let old_path = env.eval(args[0].clone()); + let new_path = env.eval(args[1].clone()); + let old_path = match old_path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid old path {:?}", old_path)), + }; + + let new_path = match new_path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid new path {:?}", new_path)), + }; + + match std::fs::rename(old_path, new_path) { + Ok(_) => Expr::None, + Err(e) => Expr::error(format!("Error renaming file: {:?}", e)), + } +} + +fn copy_file(env: &mut Env, args: Vec) -> Expr { + let old_path = env.eval(args[0].clone()); + let new_path = env.eval(args[1].clone()); + let old_path = match old_path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid old path {:?}", old_path)), + }; + + let new_path = match new_path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid new path {:?}", new_path)), + }; + + match std::fs::copy(old_path, new_path) { + Ok(_) => Expr::None, + Err(e) => Expr::error(format!("Error copying file: {:?}", e)), + } +} + +fn metadata(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::metadata(path) { + Ok(metadata) => { + let mut map = HashMap::new(); + map.insert(Expr::Symbol("is_dir".into()), Expr::Bool(metadata.is_dir())); + map.insert(Expr::Symbol("is_file".into()), Expr::Bool(metadata.is_file())); + map.insert(Expr::Symbol("len".into()), Expr::Int(metadata.len() as i64)); + map.insert(Expr::Symbol("created".into()), Expr::Int(metadata.created().unwrap().elapsed().unwrap().as_secs() as i64)); + map.insert(Expr::Symbol("modified".into()), Expr::Int(metadata.modified().unwrap().elapsed().unwrap().as_secs() as i64)); + Expr::Map(map) + } + Err(e) => Expr::error(format!("Error getting metadata: {:?}", e)), + } +} + +fn list_dir(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::read_dir(path) { + Ok(entries) => { + let mut list = Vec::new(); + for entry in entries { + let entry = entry.unwrap(); + let mut map = HashMap::new(); + map.insert(Expr::Symbol("path".into()), Expr::String(entry.path().to_str().unwrap().to_string())); + map.insert(Expr::Symbol("file_name".into()), Expr::String(entry.file_name().to_str().unwrap().to_string())); + list.push(Expr::Map(map)); + } + Expr::List(list) + } + Err(e) => Expr::error(format!("Error listing directory: {:?}", e)), + } +} + +fn canonicalize_path(env: &mut Env, args: Vec) -> Expr { + let path = env.eval(args[0].clone()); + let path = match path { + Expr::String(s) => s, + _ => return Expr::error(format!("Invalid path {:?}", path)), + }; + + match std::fs::canonicalize(path) { + Ok(path) => Expr::String(path.to_str().unwrap().to_string()), + Err(e) => Expr::error(format!("Error canonicalizing path: {:?}", e)), + } +} + diff --git a/src/extensions/format.rs b/src/extensions/format.rs new file mode 100644 index 0000000..579ad47 --- /dev/null +++ b/src/extensions/format.rs @@ -0,0 +1,254 @@ +use std::{io::Write, collections::{HashMap, BTreeMap}}; + +use lazy_static::lazy_static; +use prettytable::{cell, row, format::{TableFormat, LinePosition, LineSeparator}}; + +use crate::{Env, Expr, Symbol}; + +use super::env_to_map; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("format", format); + env.bind_builtin("show", show); +} + + +fn format(env: &mut Env, expr: Vec) -> Expr { + let format = env.eval(expr[0].clone()); + // Collect the args + let args = expr[1..].to_vec(); + + let mut format = match format { + Expr::String(s) => s, + e => return Expr::error(format!("Invalid format {e}")), + }; + + // Find all of the format specifiers. + let mut specifiers = vec![]; + for (i, c) in format.chars().enumerate() { + if c == '{' { + let mut j = i + 1; + while j < format.len() { + if format.chars().nth(j).unwrap() == '}' { + break; + } + j += 1; + } + specifiers.push(format[i + 1..j].to_owned()); + } + } + + // Replace the named specifiers with variables in the scope. + for name in &specifiers { + if name.is_empty() { + continue; + } + let name = Expr::Symbol(Symbol::new(name)); + + let value = env.eval(name.clone()); + let specifier = format!("{{{name}}}"); + match value { + Expr::String(s) => { + format = format.replacen(&specifier, &s, 1); + } + other => { + format = format.replacen(&specifier, &other.to_string(), 1); + } + } + } + + // Replace the empty specifiers with the args. + let mut i = 0; + for name in &specifiers { + if !name.is_empty() { + continue; + } + if i >= args.len() { + return Expr::error("Too few arguments"); + } + let specifier = format!("{{}}"); + let value = env.eval(args[i].clone()); + match value { + Expr::String(s) => { + format = format.replacen(&specifier, &s, 1); + } + other => { + format = format.replacen(&specifier, &other.to_string(), 1); + } + } + // format = format.replacen("{}", &args[i].to_string(), 1); + i += 1; + } + + if i < args.len() { + return Expr::error("Too many arguments"); + } + + Expr::String(format) +} + +lazy_static! { + static ref TABLE_FORMAT: TableFormat = { + let mut fmt = TableFormat::new(); + fmt.borders('│'); + fmt.column_separator('│'); + fmt.separator(LinePosition::Top, LineSeparator::new('═', '╤', '╒', '╕')); + fmt.separator(LinePosition::Title, LineSeparator::new('═', '╪', '╞', '╡')); + fmt.separator(LinePosition::Intern, LineSeparator::new('─', '┼', '├', '┤')); + fmt.separator(LinePosition::Bottom, LineSeparator::new('─', '┴', '└', '┘')); + + fmt + }; + + static ref LIST_FORMAT: TableFormat = { + let mut fmt = TableFormat::new(); + fmt.borders('┃'); + fmt.column_separator('┃'); + fmt.separator(LinePosition::Top, LineSeparator::new('━', '┳', '┏', '┓')); + fmt.separator(LinePosition::Title, LineSeparator::new('━', '╋', '┣', '┫')); + fmt.separator(LinePosition::Intern, LineSeparator::new('━', '╋', '┣', '┫')); + fmt.separator(LinePosition::Bottom, LineSeparator::new('━', '┻', '┗', '┛')); + + fmt + }; + + static ref FUNCTION_FORMAT: TableFormat = { + let mut fmt = TableFormat::new(); + fmt.borders('│'); + fmt.column_separator('│'); + fmt.separator(LinePosition::Top, LineSeparator::new('═', '╤', '╒', '╕')); + fmt.separator(LinePosition::Title, LineSeparator::new('═', '╪', '╞', '╡')); + fmt.separator(LinePosition::Intern, LineSeparator::new('─', '┼', '├', '┤')); + fmt.separator(LinePosition::Bottom, LineSeparator::new('─', '┴', '└', '┘')); + + fmt + }; +} + +fn test(env: &mut Env, _args: Vec) -> Expr { + // map_to_table(env.get_bindings(), 80).printstd(); + + let table = map_to_table(env.get_bindings(), 80); + // Set the column separator to a pipe + table.printstd(); + + Expr::None +} + +fn show(env: &mut Env, expr: Vec) -> Expr { + if expr.len() != 1 { + return Expr::error("show: expected 1 argument"); + } + + let expr = env.eval(expr[0].clone()); + let mut result = vec![]; + match expr { + Expr::Map(map) => { + map_to_table(map, 80).print(&mut result).expect("Error printing map"); + } + Expr::Tree(tree) => { + tree_to_table(tree, 80).print(&mut result).expect("Error printing tree"); + } + Expr::List(list) => { + list_to_table(list, 80).print(&mut result).expect("Error printing list"); + } + expr => { + result.write_all(expr_to_cell(expr, 80).to_string().as_bytes()).unwrap(); + } + } + Expr::String(String::from_utf8(result).unwrap()) +} + +fn fit_string(x: impl ToString, max_width: usize) -> String { + let s = x.to_string(); + let mut result = String::new(); + let mut width = 0; + for c in s.chars() { + match c { + '\n' => { + result.push(c); + width = 0; + } + _ => { + if width >= max_width { + result.push('\n'); + width = 0; + } + result.push(c); + width += 1; + } + } + } + result +} + +fn expr_to_cell(expr: Expr, max_width: usize) -> prettytable::Cell { + match &expr { + Expr::Object(o) => { + let mut table = prettytable::Table::new(); + table.set_titles(row![bFWBC->"Object"]); + + table.add_row(row![expr_to_cell(o.read().unwrap().clone(), max_width - 4)]); + table.set_format(*TABLE_FORMAT); + + cell!(table) + }, + Expr::String(s) => cell!(fit_string(format!("{s:?}"), max_width)), + Expr::Int(i) => cell!(i), + Expr::Bool(b) => cell!(b), + Expr::List(l) => cell!(list_to_table(l.clone(), max_width - 4)), + Expr::Map(m) => cell!(map_to_table(m.clone(), max_width - 4)), + Expr::Tree(m) => cell!(tree_to_table(m.clone(), max_width - 4)), + Expr::Function(env, params, body) => cell!(function_to_table((env, params.clone(), *body.clone()), max_width - 4)), + _ => cell!(fit_string(expr, max_width)), + } +} + +fn function_to_table(func: (&Option>, Vec, Expr), max_width: usize) -> prettytable::Table { + let mut table = prettytable::Table::new(); + table.set_titles(row![bFWBR->"Function"]); + + let (env, args, body) = func; + let env = env.as_ref().map(|env| env_to_map(env)); + // table.add_row(row![bFWBM->"Environment", expr_to_cell(env.unwrap_or(Expr::None), max_width-4)]); + table.add_row(row![bFWBM->"Arguments", expr_to_cell(Expr::List(args), max_width-4)]); + table.add_row(row![bFWBM->"Body", expr_to_cell(body, max_width-4)]); + table.set_format(*FUNCTION_FORMAT); + table +} + +fn list_to_table(list: Vec, max_width: usize) -> prettytable::Table { + let mut table = prettytable::Table::new(); + table.set_titles(row![bFWBM->"List"]); + + for e in list { + table.add_row(row![expr_to_cell(e, max_width-4)]); + } + + table.set_format(*LIST_FORMAT); + table +} + +fn tree_to_table(map: BTreeMap, max_width: usize) -> prettytable::Table { + let mut table = prettytable::Table::new(); + table.set_titles(row![bBG->"TreeMap Key", bBG->"TreeMap Value"]); + + for (key, value) in map { + table.add_row(row![expr_to_cell(key, max_width-4), expr_to_cell(value, max_width-4)]); + } + + table.set_format(*TABLE_FORMAT); + table +} + +fn map_to_table(map: HashMap, max_width: usize) -> prettytable::Table { + let mut table = prettytable::Table::new(); + table.set_titles(row![bFWBM->"HashMap Key", bFWBM->"HashMap Value"]); + + for (key, value) in map { + table.add_row(row![expr_to_cell(key, max_width-4), expr_to_cell(value, max_width-4)]); + } + + table.set_format(*TABLE_FORMAT); + table +} \ No newline at end of file diff --git a/src/extensions/functional.rs b/src/extensions/functional.rs new file mode 100644 index 0000000..4be682b --- /dev/null +++ b/src/extensions/functional.rs @@ -0,0 +1,199 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("eval", eval); + env.bind_builtin("quote", quote); + env.bind_builtin("map", map_container); + env.bind_builtin("filter", filter_container); + env.bind_builtin("reduce", reduce_container); + env.bind_builtin("fn", anonymous_function); + env.bind_builtin("\\", anonymous_function); + env.bind_builtin("lambda", anonymous_function); + env.bind_builtin("apply", apply_function); +} + +fn eval(env: &mut Env, expr: Vec) -> Expr { + if expr.is_empty() { + return Expr::error(format!("Eval requires an argument")); + } else if expr.len() > 1 { + return Expr::error(format!("Eval requires only one argument")); + } + let e = env.eval(expr[0].clone()); + env.eval(e) +} + +fn quote(_env: &mut Env, expr: Vec) -> Expr { + if expr.is_empty() { + return Expr::error(format!("Quote requires an argument")); + } else if expr.len() > 1 { + return Expr::error(format!("Quote requires only one argument")); + } + expr[0].clone() +} + +pub(crate) fn anonymous_function(env: &mut Env, params_and_body: Vec) -> Expr { + if params_and_body.len() != 2 { + return Expr::error(format!("Invalid function definition {:?}", params_and_body)); + } + let params = params_and_body[0].clone(); + let body = params_and_body[1].clone(); + if let Expr::List(params) = params { + Expr::Function(Some(Box::new(env.clone())), params, Box::new(body)) + } else { + return Expr::error(format!("Invalid params {:?}", params)); + } +} + +fn apply_function(env: &mut Env, function_and_args: Vec) -> Expr { + if function_and_args.len() != 2 { + return Expr::error(format!("Invalid function apply {:?}", function_and_args)); + } + let f = env.eval(function_and_args[0].clone()); + let args = env.eval(function_and_args[1].clone()); + if let Expr::List(args) = args { + match f { + Expr::Function(Some(mut env), params, body) => { + let mut new_env = env.clone(); + for (param, arg) in params.iter().zip(args.iter()) { + new_env.bind(param.clone(), env.eval(arg.clone())); + } + new_env.eval(*body.clone()) + } + Expr::Function(None, params, body) => { + let mut new_env = env.clone(); + for (param, arg) in params.iter().zip(args.iter()) { + new_env.bind(param.clone(), env.eval(arg.clone())); + } + new_env.eval(*body.clone()) + } + Expr::Builtin(f) => f.apply(&mut env.clone(), args), + f => Expr::error(format!("Invalid function {f} apply {}", Expr::from(args))), + } + } else { + Expr::error(format!("Invalid function {f} apply {}", Expr::from(args))) + } +} + +fn map_container(env: &mut Env, exprs: Vec) -> Expr { + if exprs.len() != 2 { + return Expr::error(format!("Invalid number of map arguments {:?}", exprs)); + } + let f = env.eval(exprs[0].clone()); + let container = env.eval(exprs[1].clone()); + match container { + Expr::List(container) => { + let mut list = vec![]; + for e in container { + list.push(env.eval(Expr::List(vec![f.clone(), e]))); + } + Expr::List(list) + } + Expr::Map(container) => { + let mut map = std::collections::HashMap::new(); + for (k, v) in container { + let pair = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + if let Expr::List(pair) = pair { + map.insert(pair[0].clone(), pair[1].clone()); + } else { + return Expr::error(format!("Invalid pair {}", pair)); + } + } + Expr::Map(map) + } + Expr::Tree(container) => { + let mut tree = std::collections::BTreeMap::new(); + for (k, v) in container { + let pair = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + if let Expr::List(pair) = pair { + tree.insert(pair[0].clone(), pair[1].clone()); + } else { + return Expr::error(format!("Invalid pair {}", pair)); + } + } + Expr::Tree(tree) + } + container => return Expr::error(format!("Invalid container for map {container}")), + } +} + +fn filter_container(env: &mut Env, exprs: Vec) -> Expr { + if exprs.len() != 2 { + return Expr::error(format!("Invalid number of filter arguments {:?}", exprs)); + } + let f = env.eval(exprs[0].clone()); + let container = env.eval(exprs[1].clone()); + + match container { + Expr::List(container) => { + let mut list = vec![]; + for e in container { + if env.eval(Expr::List(vec![f.clone(), e.clone()])) == Expr::Bool(true) { + list.push(e); + } + } + Expr::List(list) + } + Expr::Map(container) => { + let mut map = std::collections::HashMap::new(); + for (k, v) in container { + let x = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + if x == Expr::Bool(true) { + map.insert(k, v); + } + } + Expr::Map(map) + } + Expr::Tree(container) => { + let mut tree = std::collections::BTreeMap::new(); + for (k, v) in container { + let x = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + if x == Expr::Bool(true) { + tree.insert(k, v); + } + } + Expr::Tree(tree) + } + container => return Expr::error(format!("Invalid container for filter {container}")), + } +} + +fn reduce_container(env: &mut Env, exprs: Vec) -> Expr { + if exprs.len() < 2 { + return Expr::error(format!("Invalid number of reduce arguments {:?}", exprs)); + } + if exprs.len() > 3 { + return Expr::error(format!("Invalid number of reduce arguments {:?}", exprs)); + } + let f = env.eval(exprs[0].clone()); + let container = env.eval(exprs[1].clone()); + let accumulator_init = if exprs.len() > 2 { + env.eval(exprs[2].clone()) + } else { + Expr::None + }; + + match container { + Expr::List(container) => { + let mut acc = accumulator_init; + for e in container { + acc = env.eval(Expr::List(vec![f.clone(), acc, e])); + } + acc + } + Expr::Map(container) => { + let mut acc = accumulator_init; + for (k, v) in container { + acc = env.eval(Expr::List(vec![f.clone(), acc, k, v])); + } + acc + } + Expr::Tree(container) => { + let mut acc = accumulator_init; + for (k, v) in container { + acc = env.eval(Expr::List(vec![f.clone(), acc, k, v])); + } + acc + } + container => return Expr::error(format!("Invalid container for reduce {container}")), + } +} \ No newline at end of file diff --git a/src/extensions/io.rs b/src/extensions/io.rs new file mode 100644 index 0000000..a935a35 --- /dev/null +++ b/src/extensions/io.rs @@ -0,0 +1,25 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("println", println); + env.bind_builtin("print", print); +} + +fn println(env: &mut Env, exprs: Vec) -> Expr { + print(env, exprs); + println!(); + Expr::None +} + +fn print(env: &mut Env, exprs: Vec) -> Expr { + for e in exprs { + let e = env.eval(e.clone()); + + match e { + Expr::String(s) => print!("{}", s), + Expr::Symbol(s) => print!("{}", s.name()), + _ => print!("{}", e), + } + } + Expr::None +} \ No newline at end of file diff --git a/src/extensions/list.rs b/src/extensions/list.rs new file mode 100644 index 0000000..dc24dd8 --- /dev/null +++ b/src/extensions/list.rs @@ -0,0 +1,535 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("list", list); + env.bind_builtin("get", list_get); + env.bind_builtin("set", list_set); + env.bind_builtin("length", list_length); + env.bind_builtin("push", list_push); + env.bind_builtin("pop", list_pop); + env.bind_builtin("insert", list_insert); + env.bind_builtin("remove", list_remove); + env.bind_builtin("reverse", list_reverse); + env.bind_builtin("extend", list_extend); + env.bind_builtin("split", list_split); + env.bind_builtin("sort", list_sort); + env.bind_builtin("sort-desc", list_sort_desc); + env.bind_builtin("head", list_head); + env.bind_builtin("tail", list_tail); + env.bind_builtin("zip", list_zip); +} + +/// Create a list from the given elements. +/// +/// # Example +/// +/// ```lisp +/// (list 1 2 3) +/// ``` +/// +/// ## Output +/// +/// ``` +/// (list 1 2 3) +/// ``` +fn list(env: &mut Env, args: Vec) -> Expr { + let mut list = vec![]; + for e in args { + list.push(env.eval(e.clone())); + } + Expr::List(list) +} + +/// Get an element from a list at the specified index. +/// +/// # Example +/// +/// ```lisp +/// (get (list 1 2 3) 1) +/// ``` +/// +/// ## Output +/// +/// ``` +/// 2 +/// ``` +fn list_get(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Get the index + let index = match env.eval(args.get(1).cloned().unwrap_or(Expr::None)) { + Expr::Int(index) => index as usize, + _ => return Expr::None, + }; + + // Check if the container is a list + let list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Get the element at the specified index + list.get(index).cloned().unwrap_or(Expr::None) +} + +/// Set an element in a list at the specified index. +/// +/// # Example +/// +/// ```lisp +/// (set (list 1 2 3) 1 4) +/// ``` +/// +/// ## Output +/// +/// ``` +/// (list 1 4 3) +/// ``` +fn list_set(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Get the index + let index = match env.eval(args.get(1).cloned().unwrap_or(Expr::None)) { + Expr::Int(index) => index as usize, + _ => return Expr::None, + }; + + // Get the element to set + let element = env.eval(args.get(2).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Set the element at the specified index + list.get_mut(index).map(|e| *e = element); + + Expr::List(list) +} + +/// Get the length of a list. +/// +/// # Example +/// +/// ```lisp +/// (length (list 1 2 3)) +/// ``` +/// +/// ## Output +/// +/// ``` +/// 3 +/// ``` +fn list_length(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Get the length of the list + Expr::Int(list.len() as i64) +} + +/// Pushes an element to the end of a list. +/// +/// # Example +/// +/// ```lisp +/// (push (list 1 2 3) 4) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 1 2 3 4) +/// ``` +fn list_push(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Get the element to push + let element = env.eval(args.get(1).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Push the element to the list + list.push(element); + + Expr::List(list) +} + +/// Removes the last element from a list. +/// +/// # Example +/// +/// ```lisp +/// (pop (list 1 2 3)) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 1 2) +/// ``` +fn list_pop(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Pop the last element from the list + list.pop(); + Expr::List(list) +} + + +/// Inserts an element at the specified index in a list. +/// +/// # Example +/// +/// ```lisp +/// (insert (list 1 2 3) 1 4) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 1 4 2 3) +/// ``` +fn list_insert(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Get the index + let index = match args.get(1) { + Some(Expr::Int(index)) => *index as usize, + _ => return Expr::None, + }; + + // Get the element to insert + let element = env.eval(args.get(2).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Insert the element at the specified index + list.insert(index, element); + + Expr::List(list) +} + +/// Removes an element at the specified index in a list. +/// +/// # Example +/// +/// ```lisp +/// (remove (list 1 2 3) 1) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 1 3) +/// ``` +fn list_remove(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Get the index + let index = match args.get(1) { + Some(Expr::Int(index)) => *index as usize, + _ => return Expr::None, + }; + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Remove the element at the specified index + list.remove(index); + + Expr::List(list) +} + + + +/// Reverses the elements of a list. +/// +/// # Example +/// +/// ```lisp +/// (reverse (list 1 2 3)) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 3 2 1) +/// ``` +fn list_reverse(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Reverse the list + list.reverse(); + + Expr::List(list) +} + +/// Extend a list with the elements of another list. +/// +/// # Example +/// +/// ```lisp +/// (extend (list 1 2) (list 3 4)) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 1 2 3 4) +/// ``` +fn list_extend(env: &mut Env, args: Vec) -> Expr { + // Get the first list + let container1 = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + let container2 = env.eval(args.get(1).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list1 = match container1 { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + let mut list2 = match container2 { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Extend the first list with the elements of the second list + list1.append(&mut list2); + + Expr::List(list1) +} + +/// Splits a list into two lists at the specified index. +/// +/// # Example +/// +/// ```lisp +/// (split (list 1 2 3 4 5) 2) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// ((list 1 2) (list 3 4 5)) +/// ``` +/// +/// # Example +/// +/// ```lisp +/// (split (list 1 2 3 4 5) 0) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// ((list) (list 1 2 3 4 5)) +/// ``` +fn list_split(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Get the index + let index = match args.get(1) { + Some(Expr::Int(index)) => *index as usize, + _ => return Expr::None, + }; + + // Check if the container is a list + let list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Split the list at the specified index + let (list1, list2) = list.split_at(index); + + Expr::List(vec![Expr::List(list1.to_vec()), Expr::List(list2.to_vec())]) +} + +/// Sorts a list in ascending order. +/// +/// # Example +/// +/// ```lisp +/// (sort (list 3 1 2)) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 1 2 3) +/// ``` +fn list_sort(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Sort the list + list.sort(); + + Expr::List(list) +} + +/// Sorts a list in descending order. +/// +/// # Example +/// +/// ```lisp +/// (sort-desc (list 3 1 2)) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// (list 3 2 1) +/// ``` +fn list_sort_desc(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let mut list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Sort the list + list.sort_by(|a, b| b.cmp(a)); + + Expr::List(list) +} + +/// Get the head element of a list +/// +/// # Example +/// +/// ```lisp +/// (head (list 1 2 3)) +/// ``` +/// +/// ## Output +/// +/// ```lisp +/// 1 +/// ``` +fn list_head(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Get the head element of the list + list.first().cloned().unwrap_or(Expr::None) +} + +/// Get the tail of a list +/// +/// # Example +/// +/// ```lisp +/// (tail (list 1 2 3)) +/// ``` +/// +/// ## Output +/// +/// ``` +/// (list 2 3) +/// ``` +fn list_tail(env: &mut Env, args: Vec) -> Expr { + // Get the list + let container = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let list = match container { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Get the tail of the list + let tail = list.iter().skip(1).cloned().collect::>(); + + Expr::List(tail) +} + + +/// Zip two lists into a list of pairs. +/// +/// # Example +/// +/// ```lisp +/// (zip (list 1 2 3) (list 4 5 6)) +/// ``` +/// +/// ## Output +/// +/// ``` +/// (list (list 1 4) (list 2 5) (list 3 6)) +/// ``` +fn list_zip(env: &mut Env, args: Vec) -> Expr { + // Get the first list + let container1 = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + let container2 = env.eval(args.get(1).cloned().unwrap_or(Expr::None)); + + // Check if the container is a list + let list1 = match container1 { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + let list2 = match container2 { + Expr::List(list) => list.clone(), + _ => return Expr::None, + }; + + // Zip the two lists + let zipped = list1.iter().zip(list2.iter()).map(|(a, b)| Expr::List(vec![a.clone(), b.clone()])).collect::>(); + Expr::List(zipped) +} diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs new file mode 100644 index 0000000..aa9e282 --- /dev/null +++ b/src/extensions/mod.rs @@ -0,0 +1,28 @@ +pub mod oop; +pub mod arithmetic; +pub mod functional; +pub mod environment; +pub mod system; +pub mod filesystem; +pub mod random; +pub mod list; +pub mod procedural; +pub mod io; +pub mod format; +pub mod modules; + +pub fn env_to_map(env: &crate::Env) -> crate::Expr { + crate::Expr::Map(env.get_bindings()) +} + +pub fn as_module(add_bindings: fn(&mut crate::Env)) -> crate::Expr { + let mut env = crate::Env::new(); + add_bindings(&mut env); + env_to_map(&mut env) +} + +pub fn add_module(global_env: &mut crate::Env, module_name: impl ToString, add_bindings: fn(&mut crate::Env)) { + let module = as_module(add_bindings); + // Add the module to the global environment + global_env.bind(crate::Expr::Symbol(module_name.to_string().into()), module); +} \ No newline at end of file diff --git a/src/extensions/modules.rs b/src/extensions/modules.rs new file mode 100644 index 0000000..f8d4b63 --- /dev/null +++ b/src/extensions/modules.rs @@ -0,0 +1,36 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("use", use_module); +} + +fn use_module(env: &mut Env, exprs: Vec) -> Expr { + // Get the module + let module = match env.eval(exprs.get(0).cloned().unwrap_or_default()) { + Expr::Map(m) => m, + Expr::Tree(m) => m.into_iter().collect(), + _ => return Expr::error("use: expected a module".to_string()), + }; + + // If there are specific symbols to import, import them + for expr in exprs.iter().skip(1) { + match expr { + Expr::Symbol(name) => { + if let Some(value) = module.get(&Expr::symbol(name)) { + env.bind(Expr::Symbol(name.clone()), value.clone()); + } else { + return Expr::error(format!("use: symbol {} not found", name)); + } + }, + _ => return Expr::error("use: expected a symbol".to_string()), + } + } + + if exprs.len() == 1 { + for (name, value) in module { + env.bind(name, value); + } + } + + Expr::None +} \ No newline at end of file diff --git a/src/extensions/oop.rs b/src/extensions/oop.rs new file mode 100644 index 0000000..b3538f5 --- /dev/null +++ b/src/extensions/oop.rs @@ -0,0 +1,268 @@ + +use crate::{Env, Expr, Symbol, extensions::functional::anonymous_function}; +use std::{collections::HashMap, sync::{Arc, RwLock}}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("struct", struct_definition); + env.bind_builtin("new", new_obj); + env.bind_builtin("get", get_attr); + env.bind_builtin("<-", write_obj); + env.bind_builtin("$", read_obj); +} + +fn member_to_function(defun: Vec) -> (Symbol, Vec, Expr) { + let name = if let Expr::Symbol(name) = defun.get(1).unwrap_or(&Expr::None) { + name.clone() + } else { + Symbol::from("init") + }; + let mut params = vec![]; + + // Get the params from the first list + if let Expr::List(params_expr) = defun.get(if name == &"init" {1} else {2}).unwrap_or(&Expr::None) { + // println!("Params: {:?}", params_expr); + for param in params_expr { + if let Expr::Symbol(_name) = param { + params.push(param.clone()); + } else { + return (name, vec![], Expr::error(format!("Invalid param {:?}", param))); + } + } + } + + // Get the body from the second list + let body_expr = defun.get(if name == &"init" {2} else {3}).unwrap_or(&Expr::None); + // println!("Body: {:?}", body_expr); + + (name, params.clone(), body_expr.clone()) +} + +fn function_to_lambda(member: (Symbol, Vec, Expr)) -> Expr { + let (_name, params, body) = member; + Expr::Function(None, params, Box::new(body)) +} + +fn add_members_to_obj(mut obj: Expr, members: Vec<(Symbol, Expr)>) -> Expr { + for member in members { + let (name, value) = member; + // obj = set_attr(env, vec![obj, Expr::Symbol(name), value]); + obj = Expr::apply( + &Expr::builtin("set", set_attr), + &vec![ + obj, + Expr::Symbol(name), + value + ] + ); + } + obj +} + +fn struct_definition(env: &mut Env, exprs: Vec) -> Expr { + // Define a new struct + let name = exprs.get(0).unwrap_or(&Expr::None); + + // A struct is a function that returns a new object. + // The object is populated with a series of `defun` calls. + + // Get the list of `defun` calls + let mut members = vec![]; + let mut constructor = None; + for expr in exprs.iter().skip(1) { + if let Expr::List(exprs) = expr { + if let Some(Expr::Symbol(name)) = exprs.get(0) { + if name == &"defun" { + members.push(exprs.clone()); + } else if name == &"new" { + constructor = Some(exprs.clone()); + } else { + return Expr::error(format!("Invalid struct member {:?}", exprs)); + } + } + } + } + + // Create a new function that returns a new object, and calls the init + // function if it exists. + + let mut object_members = vec![]; + for member in members { + let (name, params, body) = member_to_function(member); + // object.insert(params[0].clone(), Expr::Function(None, params, Box::new(body))); + object_members.push(( + name, + params, + body + )); + } + + let mut constructor_params = vec![]; + let mut constructor_body = Expr::None; + + if let Some(constructor) = constructor { + let (name, params, body) = member_to_function(constructor); + constructor_params = params; + constructor_body = body; + } + + let object = add_members_to_obj( + Expr::apply( + &Expr::builtin("new", new_obj), + &vec![ + Expr::Map(Default::default()) + ] + ), + object_members.into_iter().map(|(name, params, body)| { + (name.clone(), function_to_lambda((name, params, body))) + }).collect() + ); + + // Call init + let constructor = Expr::Function(None, constructor_params[1..].to_vec(), { + // Call the constructor + Expr::apply( + &anonymous_function(env, vec![ + Expr::List(vec![constructor_params[0].clone()]), + Expr::many(vec![ + constructor_body, + constructor_params[0].clone() + ]).into() + ]), + &vec![ + object.clone(), + ] + ).into() + }); + + // constructor + + // Bind the struct to the environment + env.bind(name.clone(), constructor); + + Expr::None +} + +fn new_obj(env: &mut Env, exprs: Vec) -> Expr { + let value = env.eval(exprs.get(0).cloned().unwrap_or_default()); + Expr::Object(Arc::new(RwLock::new(value))) +} + +pub fn read_obj(env: &mut Env, exprs: Vec) -> Expr { + // Get the value of an object + let object = env.eval(exprs[0].clone()); + if let Expr::Object(object) = object { + object.read().unwrap().clone() + } else { + Expr::error(format!("Invalid object {object} get")) + } +} + +pub fn write_obj(env: &mut Env, exprs: Vec) -> Expr { + // Set the value of an object + let object = env.eval(exprs[0].clone()); + let value = env.eval(exprs[1].clone()); + if let Expr::Object(object) = object { + *object.write().unwrap() = value.clone(); + value + } else { + Expr::error(format!("Invalid object {object} set {value}")) + } +} + +pub fn get_attr(env: &mut Env, expr: Vec) -> Expr { + let a = env.eval(expr[0].clone()); + let b = expr[1].clone(); + + match (a, b) { + (Expr::Object(obj), member) => { + let val = obj.read().unwrap(); + match get_attr(env, vec![val.clone(), member.clone()]) { + Expr::None => { + // Create a new object with the member as the value + let new_obj = Expr::Object(Arc::new(RwLock::new(Expr::None))); + let set_obj = set_attr(env, vec![val.clone(), member.clone(), new_obj.clone()]); + drop(val); + // Add the member to the object + // Write the new object to the object + *obj.write().unwrap() = set_obj; + new_obj + } + other => { + match other.strip_object() { + Expr::Function(_, params, _) => { + + // Apply a method with a self parameter + let obj = env.eval(expr[0].clone()); + + // Get the param count + let method = other; + let param_count = params.len(); + // Return a function that takes the rest of the arguments, + // and applies the method with the object as the first argument. + + let mut args = vec![obj.clone()]; + let mut params = vec![]; + for i in 1..param_count { + let name = Symbol::from(format!("arg{}", i)); + args.push(Expr::Symbol(name.clone())); + params.push(Expr::Symbol(name)); + } + + let body = method.apply(&args); + + let f = Expr::Function(None, params, Box::new(body)); + f + } + _ => other + } + } + } + } + (Expr::Map(a), Expr::Symbol(b)) => { + a.get(&Expr::Symbol(b.clone())).cloned().unwrap_or_else(|| { + a.get(&Expr::String(b.name().to_owned())) + .cloned() + .unwrap_or(Expr::None) + }) + } + (Expr::Map(a), b) => a.get(&b).cloned().unwrap_or(Expr::None), + (Expr::Tree(a), Expr::Symbol(b)) => { + a.get(&Expr::Symbol(b.clone())).cloned().unwrap_or_else(|| { + a.get(&Expr::String(b.name().to_owned())) + .cloned() + .unwrap_or(Expr::None) + }) + } + (Expr::Tree(a), b) => a.get(&b).cloned().unwrap_or(Expr::None), + + (a, b) => return Expr::error(format!("Invalid expr get {} {}", a, b)), + } +} + + + +fn set_attr(env: &mut Env, expr: Vec) -> Expr { + let a = env.eval(expr[0].clone()); + let b = env.eval(expr[1].clone()); + let c = env.eval(expr[2].clone()); + + let result = match (a, b) { + (Expr::Object(obj), member) => { + let val = obj.read().unwrap(); + let new_val = set_attr(env, vec![val.clone(), member.clone(), c.clone()]); + drop(val); + *obj.write().unwrap() = new_val.clone(); + Expr::Object(obj) + } + (Expr::Map(mut a), b) => { + a.insert(b, c); + Expr::Map(a) + } + (Expr::Tree(mut a), b) => { + a.insert(b, c); + Expr::Tree(a) + } + (a, b) => return Expr::error(format!("Invalid expr set {} {} {}", a, b, c)), + }; + result +} \ No newline at end of file diff --git a/src/extensions/procedural.rs b/src/extensions/procedural.rs new file mode 100644 index 0000000..183c96e --- /dev/null +++ b/src/extensions/procedural.rs @@ -0,0 +1,130 @@ +use crate::{Env, Expr}; + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("while", while_loop); + env.bind_builtin("for", for_loop); + env.bind_lazy_builtin("if", if_statement); +} + +/// Perform a loop while a condition is true. +/// +/// # Example +/// +/// ```lisp +/// (while (< i 10) { +/// (define i (+ i 1)) +/// +/// (print i) +/// } +/// ``` +/// +/// # Syntax +/// +/// ```lisp +/// (while ) +/// ``` +/// +/// # Remarks +/// +/// The condition expression is evaluated before each iteration of the loop. +/// The body expression is evaluated only if the condition is true. +fn while_loop(env: &mut Env, args: Vec) -> Expr { + if args.len() != 2 { + return Expr::error("while: expected 2 arguments".to_string()); + } + + let condition = args.get(0).unwrap(); + let body = args.get(1).unwrap(); + + let mut result = Expr::None; + while env.eval(condition.clone()) == Expr::Bool(true) { + result = env.eval(body.clone()); + } + + result +} + +/// Perform a loop iterating over a list of elements. +/// +/// # Example +/// +/// ```lisp +/// (for i in (range 10) { +/// (print i) +/// }) +/// ``` +/// +/// # Output +/// +/// ``` +/// 0 +/// 1 +/// 2 +/// 3 +/// 4 +/// 5 +/// 6 +/// 7 +/// 8 +/// 9 +/// ``` +fn for_loop(env: &mut Env, args: Vec) -> Expr { + if args.len() != 4 { + return Expr::error("for: expected 2 arguments".to_string()); + } + + let variable = args.get(0).unwrap(); + let iterable = env.eval(args.get(2).cloned().unwrap_or(Expr::None)); + let body = args.get(3).unwrap(); + + let mut result = Expr::None; + if let Expr::List(elements) = iterable { + for element in elements { + env.bind(variable.clone(), element); + // Evaluate the body of the loop + result = env.eval(body.clone()); + } + } else { + return Expr::error(format!("for: expected a list, found {iterable:?}")); + } + + result +} + + +/// Conditional if-else expression. +/// +/// # Example +/// +/// ```lisp +/// (define x 5) +/// (if (< x 10) { +/// (println "x is less than 10") +/// } { +/// (println "x is greater than or equal to 10") +/// }) +/// ``` +/// +/// ## Output +/// +/// ``` +/// x is less than 10 +/// ``` +fn if_statement(env: &mut Env, exprs: Vec) -> Expr { + let cond = env.eval(exprs[0].clone()); + let then = exprs[1].clone(); + if exprs.len() < 3 { + if cond == Expr::Bool(true) { + then + } else { + Expr::None + } + } else { + let else_ = exprs[2].clone(); + if cond == Expr::Bool(true) { + then + } else { + else_ + } + } +} \ No newline at end of file diff --git a/src/extensions/random.rs b/src/extensions/random.rs new file mode 100644 index 0000000..e4b87b8 --- /dev/null +++ b/src/extensions/random.rs @@ -0,0 +1,56 @@ +use crate::Env; + +#[cfg(feature = "rand")] +use crate::Expr; +#[cfg(feature = "rand")] +use rand::Rng; + + +#[cfg(feature = "rand")] +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("integer", random_integer); + env.bind_builtin("choice", random_choice); + env.bind_builtin("shuffle", random_shuffle); +} + +#[cfg(not(feature = "rand"))] +pub fn add_bindings(_env: &mut Env) { + eprintln!("Warning: Random extension not available. Please enable the 'rand' feature."); +} + +#[cfg(feature = "rand")] +fn random_integer(env: &mut Env, args: Vec) -> Expr { + let min = match env.eval(args.get(0).cloned().unwrap_or(Expr::None)) { + Expr::Int(min) => min, + _ => i64::MIN, + }; + let max = match env.eval(args.get(1).cloned().unwrap_or(Expr::None)) { + Expr::Int(max) => max, + _ => i64::MAX, + }; + Expr::Int(rand::thread_rng().gen_range(min..=max)) +} + +#[cfg(feature = "rand")] +fn random_choice(env: &mut Env, args: Vec) -> Expr { + let mut rng = rand::thread_rng(); + + let collection = env.eval(args.get(0).cloned().unwrap_or(Expr::None)); + + match collection { + Expr::List(list) => { + let index = rng.gen_range(0..list.len()); + list.get(index).cloned().unwrap_or(Expr::None) + }, + _ => Expr::error(format!("Expected a list, found {:?}", collection)), + } +} + +#[cfg(feature = "rand")] +fn random_shuffle(env: &mut Env, args: Vec) -> Expr { + use rand::prelude::SliceRandom; + let mut rng = rand::thread_rng(); + let mut args = args.clone(); + args.shuffle(&mut rng); + Expr::List(args) +} \ No newline at end of file diff --git a/src/extensions/system.rs b/src/extensions/system.rs new file mode 100644 index 0000000..876fcaf --- /dev/null +++ b/src/extensions/system.rs @@ -0,0 +1,127 @@ +use crate::{Env, Expr}; + + +pub fn add_bindings(env: &mut Env) { + env.bind_builtin("exit", exit_program); + env.bind_builtin("os", get_system_os_name); + env.bind_builtin("arch", get_system_architecture); + env.bind_builtin("family", get_system_family); + env.bind_builtin("env", get_system_env); + env.bind_builtin("args", get_system_args); + env.bind_builtin("cwd", get_system_cwd); + env.bind_builtin("home", get_system_home); + env.bind_builtin("user", get_system_user); + env.bind_builtin("username", get_system_username); + env.bind_builtin("host", get_system_host); + env.bind_builtin("shell", get_system_shell); + env.bind_builtin("version", get_system_version); + env.bind_builtin("path", get_system_path); + env.bind_builtin("temp", get_system_temp); + env.bind_builtin("shell", shell_out); + env.bind_builtin("pid", get_system_pid); +} + +fn exit_program(env: &mut Env, args: Vec) -> Expr { + if args.is_empty() { + std::process::exit(0); + } + + match env.eval(args[0].clone()) { + Expr::Int(i) => std::process::exit(i as i32), + Expr::String(s) => { + eprintln!("{s}"); + std::process::exit(1); + } + e => { + eprintln!("{e}"); + std::process::exit(1); + } + } +} + +fn get_system_os_name(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::consts::OS.to_string()); +} + +fn get_system_architecture(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::consts::ARCH.to_string()); +} + +fn get_system_family(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::consts::FAMILY.to_string()); +} + +fn get_system_env(_env: &mut Env, _args: Vec) -> Expr { + return Expr::Map(std::env::vars().map(|(k, v)| (Expr::String(k), Expr::String(v))).collect()); +} + +fn get_system_args(_env: &mut Env, _args: Vec) -> Expr { + // Get the arguments supplied to the program + let args: Vec = std::env::args().map(Expr::String).collect(); + return Expr::List(args); +} + +fn get_system_cwd(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::current_dir().unwrap().to_str().unwrap().to_string()); +} + +fn get_system_home(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("HOME").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_user(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("USER").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_username(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("USERNAME").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_host(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("HOSTNAME").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_shell(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("SHELL").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_version(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("VERSION").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_path(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::var("PATH").unwrap_or_else(|_| "".to_string())); +} + +fn get_system_temp(_env: &mut Env, _args: Vec) -> Expr { + return Expr::String(std::env::temp_dir().to_str().unwrap().to_string()); +} + +// Run a shell command and return the output +fn shell_out(env: &mut Env, args: Vec) -> Expr { + let command = env.eval(args[0].clone()); + let command = match command { + Expr::String(s) => s, + e => return Expr::error(format!("Invalid command {:?}", e)), + }; + + let output = std::process::Command::new("sh") + .arg("-c") + .arg(command) + .output() + .expect("failed to execute process"); + + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + + // If non-zero exit code, return an error + if !output.status.success() { + return Expr::error(format!("{stderr}")); + } + + return Expr::String(stdout); +} + +fn get_system_pid(_env: &mut Env, _args: Vec) -> Expr { + return Expr::Int(std::process::id() as i64); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 809b335..71fc966 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,6 +175,8 @@ use lazy_static::lazy_static; mod parser; pub use parser::*; +pub mod extensions; + /////////////////////////////////////////////////////////////// // SYMBOLS AND SYMBOL TABLE @@ -270,6 +272,17 @@ impl PartialEq for Symbol { } } +/// Add partial equality for symbols and strings +/// +/// This allows you to compare a symbol to a string using the `==` operator. +impl> PartialEq for Symbol { + #[inline] + fn eq(&self, other: &T) -> bool { + *self.0 == other.as_ref() + } +} + + /// Compare two symbols for ordering. /// /// If the two symbols are the same object in memory, they are equal. @@ -284,6 +297,14 @@ impl PartialOrd for Symbol { } } +/// Print a symbol as regular output +impl Display for Symbol { + #[inline] + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!(f, "{}", self.0) + } +} + /// Print a symbol as debug output /// /// Since a symbol is meant to be an identifier, it is printed as a normal string. @@ -894,14 +915,26 @@ impl Expr { Self::List(result) } + /// Create a builtin function from a Rust function pointer. + #[inline] + pub fn builtin(name: &'static str, f: fn(&mut Env, Vec) -> Self) -> Self { + Self::Builtin(Builtin::new(f, name)) + } + /// Strip the object from an expression, returning the inner expression. - fn strip_object(&self) -> Self { + pub fn strip_object(&self) -> Self { match self { Self::Object(o) => o.read().unwrap().clone(), _ => self.clone(), } } + /// Create a many expression from a list of expressions. + #[inline] + pub fn many(exprs: Vec) -> Self { + Self::Many(Arc::new(exprs)) + } + /// Parse a string into a Lisp expression. /// /// If the string is a valid Lisp expression, it will return the parsed expression. diff --git a/src/main.rs b/src/main.rs index 8eb72eb..8d9c7a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,989 +22,929 @@ pub struct Program { fn make_env() -> Env { let mut env = Env::new(); - env.bind_builtin("env", |env, args| { - // Get the env as a map - if args.is_empty() { - return Expr::Map(env.get_bindings()); - } - let a = env.eval(args[0].clone()); - env.get(&a).cloned().unwrap_or(Expr::None) - }); - - env.bind_builtin("+", |env, exprs| { - let mut sum = Expr::default(); - for e in exprs { - let e = env.eval(e.clone()); - // sum += env.eval(e); - match (sum, e) { - (Expr::None, b) => sum = b, - (Expr::Int(a), Expr::Int(b)) => sum = Expr::Int(a + b), - (Expr::Float(a), Expr::Float(b)) => sum = Expr::Float(a + b), - (Expr::Int(a), Expr::Float(b)) => sum = Expr::Float(a as f64 + b), - (Expr::Float(a), Expr::Int(b)) => sum = Expr::Float(a + b as f64), - (Expr::String(a), Expr::String(b)) => sum = Expr::String(format!("{}{}", a, b)), - (Expr::List(a), Expr::List(b)) => { - let mut list = a.clone(); - list.extend(b); - sum = Expr::List(list); - } - (Expr::List(a), b) => { - let mut list = a.clone(); - list.push(b); - sum = Expr::List(list); - } - (a, b) => return Expr::error(format!("Invalid expr {} + {}", a, b)), - } - } - sum - }); - env.alias("+", "add"); - - env.bind_builtin("-", |env, exprs| { - let mut diff = Expr::default(); - for e in exprs { - let e = env.eval(e.clone()); - match (diff, e) { - (Expr::None, b) => diff = b, - (Expr::Int(a), Expr::Int(b)) => diff = Expr::Int(a - b), - (Expr::Float(a), Expr::Float(b)) => diff = Expr::Float(a - b), - (Expr::Int(a), Expr::Float(b)) => diff = Expr::Float(a as f64 - b), - (Expr::Float(a), Expr::Int(b)) => diff = Expr::Float(a - b as f64), - (a, b) => return Expr::error(format!("Invalid expr {} - {}", a, b)), - } - } - diff - }); - env.alias("-", "sub"); - - env.bind_builtin("*", |env, exprs| { - let mut product = Expr::default(); - for e in exprs { - let e = env.eval(e.clone()); - match (product, e) { - (Expr::None, b) => product = b, - (Expr::Int(a), Expr::Int(b)) => product = Expr::Int(a * b), - (Expr::Float(a), Expr::Float(b)) => product = Expr::Float(a * b), - (Expr::Int(a), Expr::Float(b)) => product = Expr::Float(a as f64 * b), - (Expr::Float(a), Expr::Int(b)) => product = Expr::Float(a * b as f64), - (Expr::List(a), Expr::Int(b)) => { - let mut list = a.clone(); - for _ in 0..b { - list.extend(a.clone()); - } - product = Expr::List(list); - } - (a, b) => return Expr::error(format!("Invalid expr {} * {}", a, b)), - } - } - product - }); - env.alias("*", "mul"); - - env.bind_builtin("/", |env, exprs| { - let mut quotient = Expr::default(); - for e in exprs { - let e = env.eval(e.clone()); - match (quotient, e) { - (Expr::None, b) => quotient = b, - (Expr::Int(a), Expr::Int(b)) => quotient = Expr::Int(a / b), - (Expr::Float(a), Expr::Float(b)) => quotient = Expr::Float(a / b), - (Expr::Int(a), Expr::Float(b)) => quotient = Expr::Float(a as f64 / b), - (Expr::Float(a), Expr::Int(b)) => quotient = Expr::Float(a / b as f64), - (a, b) => return Expr::error(format!("Invalid expr {} / {}", a, b)), - } - } - quotient - }); - env.alias("/", "div"); - - env.bind_builtin("%", |env, exprs| { - let mut quotient = Expr::default(); - for e in exprs { - let e = env.eval(e.clone()); - match (quotient, e) { - (Expr::None, b) => quotient = b, - (Expr::Int(a), Expr::Int(b)) => quotient = Expr::Int(a % b), - (Expr::Float(a), Expr::Float(b)) => quotient = Expr::Float(a % b), - (Expr::Int(a), Expr::Float(b)) => quotient = Expr::Float(a as f64 % b), - (Expr::Float(a), Expr::Int(b)) => quotient = Expr::Float(a % b as f64), - (a, b) => return Expr::error(format!("Invalid expr {} % {}", a, b)), - } - } - quotient - }); - env.alias("%", "rem"); + use sage_lisp::extensions::*; + // arithmetic::add_bindings(&mut env); + // environment::add_bindings(&mut env); + // filesystem::add_bindings(&mut env); + // iterative::add_bindings(&mut env); + // list::add_bindings(&mut env); + // random::add_bindings(&mut env); + // system::add_bindings(&mut env); + // io::add_bindings(&mut env); + + add_module(&mut env, "math", arithmetic::add_bindings); + add_module(&mut env, "env", environment::add_bindings); + add_module(&mut env, "fs", filesystem::add_bindings); + add_module(&mut env, "fn", functional::add_bindings); + add_module(&mut env, "proc", procedural::add_bindings); + add_module(&mut env, "list", list::add_bindings); + add_module(&mut env, "rand", random::add_bindings); + add_module(&mut env, "sys", system::add_bindings); + add_module(&mut env, "io", io::add_bindings); + add_module(&mut env, "fmt", format::add_bindings); + add_module(&mut env, "oop", oop::add_bindings); + add_module(&mut env, "mod", modules::add_bindings); + + env.bind_builtin("<-", sage_lisp::extensions::oop::write_obj); + env.bind_builtin("$", sage_lisp::extensions::oop::read_obj); + env.bind_builtin(".", sage_lisp::extensions::oop::get_attr); + + // env.bind_builtin("env", |env, args| { + // // Get the env as a map + // if args.is_empty() { + // return Expr::Map(env.get_bindings()); + // } + // let a = env.eval(args[0].clone()); + // env.get(&a).cloned().unwrap_or(Expr::None) + // }); + + // env.bind_builtin("+", |env, exprs| { + // let mut sum = Expr::default(); + // for e in exprs { + // let e = env.eval(e.clone()); + // // sum += env.eval(e); + // match (sum, e) { + // (Expr::None, b) => sum = b, + // (Expr::Int(a), Expr::Int(b)) => sum = Expr::Int(a + b), + // (Expr::Float(a), Expr::Float(b)) => sum = Expr::Float(a + b), + // (Expr::Int(a), Expr::Float(b)) => sum = Expr::Float(a as f64 + b), + // (Expr::Float(a), Expr::Int(b)) => sum = Expr::Float(a + b as f64), + // (Expr::String(a), Expr::String(b)) => sum = Expr::String(format!("{}{}", a, b)), + // (Expr::List(a), Expr::List(b)) => { + // let mut list = a.clone(); + // list.extend(b); + // sum = Expr::List(list); + // } + // (Expr::List(a), b) => { + // let mut list = a.clone(); + // list.push(b); + // sum = Expr::List(list); + // } + // (a, b) => return Expr::error(format!("Invalid expr {} + {}", a, b)), + // } + // } + // sum + // }); + // env.alias("+", "add"); + + // env.bind_builtin("-", |env, exprs| { + // let mut diff = Expr::default(); + // for e in exprs { + // let e = env.eval(e.clone()); + // match (diff, e) { + // (Expr::None, b) => diff = b, + // (Expr::Int(a), Expr::Int(b)) => diff = Expr::Int(a - b), + // (Expr::Float(a), Expr::Float(b)) => diff = Expr::Float(a - b), + // (Expr::Int(a), Expr::Float(b)) => diff = Expr::Float(a as f64 - b), + // (Expr::Float(a), Expr::Int(b)) => diff = Expr::Float(a - b as f64), + // (a, b) => return Expr::error(format!("Invalid expr {} - {}", a, b)), + // } + // } + // diff + // }); + // env.alias("-", "sub"); + + // env.bind_builtin("*", |env, exprs| { + // let mut product = Expr::default(); + // for e in exprs { + // let e = env.eval(e.clone()); + // match (product, e) { + // (Expr::None, b) => product = b, + // (Expr::Int(a), Expr::Int(b)) => product = Expr::Int(a * b), + // (Expr::Float(a), Expr::Float(b)) => product = Expr::Float(a * b), + // (Expr::Int(a), Expr::Float(b)) => product = Expr::Float(a as f64 * b), + // (Expr::Float(a), Expr::Int(b)) => product = Expr::Float(a * b as f64), + // (Expr::List(a), Expr::Int(b)) => { + // let mut list = a.clone(); + // for _ in 0..b { + // list.extend(a.clone()); + // } + // product = Expr::List(list); + // } + // (a, b) => return Expr::error(format!("Invalid expr {} * {}", a, b)), + // } + // } + // product + // }); + // env.alias("*", "mul"); + + // env.bind_builtin("/", |env, exprs| { + // let mut quotient = Expr::default(); + // for e in exprs { + // let e = env.eval(e.clone()); + // match (quotient, e) { + // (Expr::None, b) => quotient = b, + // (Expr::Int(a), Expr::Int(b)) => quotient = Expr::Int(a / b), + // (Expr::Float(a), Expr::Float(b)) => quotient = Expr::Float(a / b), + // (Expr::Int(a), Expr::Float(b)) => quotient = Expr::Float(a as f64 / b), + // (Expr::Float(a), Expr::Int(b)) => quotient = Expr::Float(a / b as f64), + // (a, b) => return Expr::error(format!("Invalid expr {} / {}", a, b)), + // } + // } + // quotient + // }); + // env.alias("/", "div"); + + // env.bind_builtin("%", |env, exprs| { + // let mut quotient = Expr::default(); + // for e in exprs { + // let e = env.eval(e.clone()); + // match (quotient, e) { + // (Expr::None, b) => quotient = b, + // (Expr::Int(a), Expr::Int(b)) => quotient = Expr::Int(a % b), + // (Expr::Float(a), Expr::Float(b)) => quotient = Expr::Float(a % b), + // (Expr::Int(a), Expr::Float(b)) => quotient = Expr::Float(a as f64 % b), + // (Expr::Float(a), Expr::Int(b)) => quotient = Expr::Float(a % b as f64), + // (a, b) => return Expr::error(format!("Invalid expr {} % {}", a, b)), + // } + // } + // quotient + // }); + // env.alias("%", "rem"); - env.bind_builtin("=", |env, exprs| { - let a = env.eval(exprs[0].clone()); - let b = env.eval(exprs[1].clone()); + // env.bind_builtin("=", |env, exprs| { + // let a = env.eval(exprs[0].clone()); + // let b = env.eval(exprs[1].clone()); - Expr::Bool(a == b) - }); - env.alias("=", "=="); + // Expr::Bool(a == b) + // }); + // env.alias("=", "=="); - env.bind_builtin("!=", |env, exprs| { - let a = env.eval(exprs[0].clone()); - let b = env.eval(exprs[1].clone()); + // env.bind_builtin("!=", |env, exprs| { + // let a = env.eval(exprs[0].clone()); + // let b = env.eval(exprs[1].clone()); - Expr::Bool(a != b) - }); + // Expr::Bool(a != b) + // }); - env.bind_builtin("<=", |env, exprs| { - let a = env.eval(exprs[0].clone()); - let b = env.eval(exprs[1].clone()); + // env.bind_builtin("<=", |env, exprs| { + // let a = env.eval(exprs[0].clone()); + // let b = env.eval(exprs[1].clone()); - Expr::Bool(a <= b) - }); + // Expr::Bool(a <= b) + // }); - env.bind_builtin(">=", |env, exprs| { - let a = env.eval(exprs[0].clone()); - let b = env.eval(exprs[1].clone()); + // env.bind_builtin(">=", |env, exprs| { + // let a = env.eval(exprs[0].clone()); + // let b = env.eval(exprs[1].clone()); - Expr::Bool(a >= b) - }); + // Expr::Bool(a >= b) + // }); - env.bind_builtin("<", |env, exprs| { - let a = env.eval(exprs[0].clone()); - let b = env.eval(exprs[1].clone()); + // env.bind_builtin("<", |env, exprs| { + // let a = env.eval(exprs[0].clone()); + // let b = env.eval(exprs[1].clone()); - Expr::Bool(a < b) - }); + // Expr::Bool(a < b) + // }); - env.bind_builtin(">", |env, exprs| { - let a = env.eval(exprs[0].clone()); - let b = env.eval(exprs[1].clone()); + // env.bind_builtin(">", |env, exprs| { + // let a = env.eval(exprs[0].clone()); + // let b = env.eval(exprs[1].clone()); - Expr::Bool(a > b) - }); + // Expr::Bool(a > b) + // }); - env.bind_lazy_builtin("if", |env, exprs| { - let cond = env.eval(exprs[0].clone()); - let then = exprs[1].clone(); - if exprs.len() < 3 { - if cond == Expr::Bool(true) { - then - } else { - Expr::None - } - } else { - let else_ = exprs[2].clone(); - if cond == Expr::Bool(true) { - then - } else { - else_ - } - } - }); - - env.bind_builtin("new", |env, exprs| { - // Create a new object from the value passed - let value = env.eval(exprs[0].clone()); - Expr::Object(Arc::new(RwLock::new(value))) - }); - fn set(env: &mut Env, exprs: Vec) -> Expr { - // Set the value of an object - let object = env.eval(exprs[0].clone()); - let value = env.eval(exprs[1].clone()); - if let Expr::Object(object) = object { - *object.write().unwrap() = value.clone(); - value - } else { - Expr::error(format!("Invalid object {object} set {value}")) - } - } - env.bind_builtin("<-", set); - - env.bind_builtin("$", |env, exprs| { - // Get the value of an object - let object = env.eval(exprs[0].clone()); - if let Expr::Object(object) = object { - object.read().unwrap().clone() - } else { - Expr::error(format!("Invalid object {object} get")) - } - }); - - env.bind_builtin("define", |env, exprs| { - let name = exprs[0].clone(); - let value = env.eval(exprs[1].clone()); - env.bind(name, value); - Expr::None - }); - - env.bind_builtin("undefine", |env, exprs| { - let name = exprs[0].clone(); - env.unbind(&name); - Expr::None - }); - - env.bind_builtin("defun", |env, args| { - let name = args[0].clone(); - let params = args[1].clone(); - let body = args[2].clone(); - if let Expr::List(params) = params { - let f = env.eval(Expr::Function(None, params, Box::new(body))); - env.bind(name, f); - Expr::None - } else { - return Expr::error(format!("Invalid params {:?}", params)); - } - }); + // env.bind_lazy_builtin("if", |env, exprs| { + // let cond = env.eval(exprs[0].clone()); + // let then = exprs[1].clone(); + // if exprs.len() < 3 { + // if cond == Expr::Bool(true) { + // then + // } else { + // Expr::None + // } + // } else { + // let else_ = exprs[2].clone(); + // if cond == Expr::Bool(true) { + // then + // } else { + // else_ + // } + // } + // }); - env.bind_builtin("println", |env, exprs| { - for e in exprs { - let e = env.eval(e.clone()); + // env.bind_builtin("new", |env, exprs| { + // // Create a new object from the value passed + // let value = env.eval(exprs[0].clone()); + // Expr::Object(Arc::new(RwLock::new(value))) + // }); - match e { - Expr::String(s) => print!("{}", s), - Expr::Symbol(s) => print!("{}", s.name()), - _ => print!("{}", e), - } - } - println!(); - Expr::None - }); + // env.bind_builtin("$", |env, exprs| { + // // Get the value of an object + // let object = env.eval(exprs[0].clone()); + // if let Expr::Object(object) = object { + // object.read().unwrap().clone() + // } else { + // Expr::error(format!("Invalid object {object} get")) + // } + // }); + + // env.bind_builtin("define", |env, exprs| { + // let name = exprs[0].clone(); + // let value = env.eval(exprs[1].clone()); + // env.bind(name, value); + // Expr::None + // }); - // env.bind_builtin("do", |env, exprs| { - // let mut result = Expr::default(); + // env.bind_builtin("undefine", |env, exprs| { + // let name = exprs[0].clone(); + // env.unbind(&name); + // Expr::None + // }); + + // env.bind_builtin("defun", |env, args| { + // let name = args[0].clone(); + // let params = args[1].clone(); + // let body = args[2].clone(); + // if let Expr::List(params) = params { + // let f = env.eval(Expr::Function(None, params, Box::new(body))); + // env.bind(name, f); + // Expr::None + // } else { + // return Expr::error(format!("Invalid params {:?}", params)); + // } + // }); + + // env.bind_builtin("println", |env, exprs| { // for e in exprs { - // result = env.eval(e.clone()); + // let e = env.eval(e.clone()); + + // match e { + // Expr::String(s) => print!("{}", s), + // Expr::Symbol(s) => print!("{}", s.name()), + // _ => print!("{}", e), + // } // } - // result + // println!(); + // Expr::None // }); - env.bind_lazy_builtin("do", |_env, exprs| { - use std::sync::Arc; - Expr::Many(Arc::new(Vec::from(exprs))) - }); - - env.bind_builtin("sqrt", |env, expr| { - let e = env.eval(expr[0].clone()); - match e { - Expr::Int(i) => Expr::Float((i as f64).sqrt()), - Expr::Float(f) => Expr::Float(f.sqrt()), - e => Expr::error(format!("Invalid expr sqrt {}", e)), - } - }); - - env.bind_builtin("^", |env, expr| { - let a = env.eval(expr[0].clone()); - let b = env.eval(expr[1].clone()); - match (a, b) { - (Expr::Int(a), Expr::Int(b)) => Expr::Float((a as f64).powf(b as f64)), - (Expr::Float(a), Expr::Float(b)) => Expr::Float(a.powf(b)), - (Expr::Int(a), Expr::Float(b)) => Expr::Float((a as f64).powf(b)), - (Expr::Float(a), Expr::Int(b)) => Expr::Float(a.powf(b as f64)), - (a, b) => Expr::error(format!("Invalid expr {} ^ {}", a, b)), - } - }); + // // env.bind_builtin("do", |env, exprs| { + // // let mut result = Expr::default(); + // // for e in exprs { + // // result = env.eval(e.clone()); + // // } + // // result + // // }); + + // env.bind_lazy_builtin("do", |_env, exprs| { + // use std::sync::Arc; + // Expr::Many(Arc::new(Vec::from(exprs))) + // }); - env.alias("^", "pow"); + // env.bind_builtin("sqrt", |env, expr| { + // let e = env.eval(expr[0].clone()); + // match e { + // Expr::Int(i) => Expr::Float((i as f64).sqrt()), + // Expr::Float(f) => Expr::Float(f.sqrt()), + // e => Expr::error(format!("Invalid expr sqrt {}", e)), + // } + // }); - let lambda = |env: &mut Env, expr: Vec| { - let params = expr[0].clone(); - let body = expr[1].clone(); - if let Expr::List(params) = params { - Expr::Function(Some(Box::new(env.clone())), params, Box::new(body)) - } else { - return Expr::error(format!("Invalid params {:?}", params)); - } - }; - env.bind_builtin("lambda", lambda); - env.bind_builtin("\\", lambda); - - env.bind_builtin("apply", |env, expr| { - let f = env.eval(expr[0].clone()); - let args = env.eval(expr[1].clone()); - if let Expr::List(args) = args { - match f { - Expr::Function(Some(mut env), params, body) => { - let mut new_env = env.clone(); - for (param, arg) in params.iter().zip(args.iter()) { - new_env.bind(param.clone(), env.eval(arg.clone())); - } - new_env.eval(*body.clone()) - } - Expr::Function(None, params, body) => { - let mut new_env = env.clone(); - for (param, arg) in params.iter().zip(args.iter()) { - new_env.bind(param.clone(), env.eval(arg.clone())); - } - new_env.eval(*body.clone()) - } - Expr::Builtin(f) => f.apply(&mut env.clone(), args), - f => Expr::error(format!("Invalid function {f} apply {}", Expr::from(args))), - } - } else { - Expr::error(format!("Invalid function {f} apply {}", Expr::from(args))) - } - }); - - env.bind_builtin("cons", |env, expr| { - let a = env.eval(expr[0].clone()); - let b = env.eval(expr[1].clone()); - // Create a new list with a as the head and b as the tail. - if let Expr::List(b) = b { - let mut list = vec![a]; - list.extend(b); - Expr::List(list) - } else if b == Expr::None { - Expr::List(vec![a]) - } else { - Expr::List(vec![a, b]) - } - }); - let head = |env: &mut Env, expr: Vec| { - let a = env.eval(expr[0].clone()); - if let Expr::List(a) = a { - a[0].clone() - } else { - Expr::error(format!("Invalid head {a}")) - } - }; - let tail = |env: &mut Env, expr: Vec| { - let a = env.eval(expr[0].clone()); - if let Expr::List(a) = a { - Expr::List(a[1..].to_vec()) - } else { - Expr::error(format!("Invalid tail {a}")) - } - }; + // env.bind_builtin("^", |env, expr| { + // let a = env.eval(expr[0].clone()); + // let b = env.eval(expr[1].clone()); + // match (a, b) { + // (Expr::Int(a), Expr::Int(b)) => Expr::Float((a as f64).powf(b as f64)), + // (Expr::Float(a), Expr::Float(b)) => Expr::Float(a.powf(b)), + // (Expr::Int(a), Expr::Float(b)) => Expr::Float((a as f64).powf(b)), + // (Expr::Float(a), Expr::Int(b)) => Expr::Float(a.powf(b as f64)), + // (a, b) => Expr::error(format!("Invalid expr {} ^ {}", a, b)), + // } + // }); - env.bind_builtin("car", head); - env.bind_builtin("head", head); - env.bind_builtin("cdr", tail); - env.bind_builtin("tail", tail); - - let format = |env: &mut Env, expr: Vec| { - let format = env.eval(expr[0].clone()); - // Collect the args - let args = expr[1..].to_vec(); - - let mut format = match format { - Expr::String(s) => s, - e => return Expr::error(format!("Invalid format {e}")), - }; - - // Find all of the format specifiers. - let mut specifiers = vec![]; - for (i, c) in format.chars().enumerate() { - if c == '{' { - let mut j = i + 1; - while j < format.len() { - if format.chars().nth(j).unwrap() == '}' { - break; - } - j += 1; - } - specifiers.push(format[i + 1..j].to_owned()); - } - } + // env.alias("^", "pow"); - // Replace the named specifiers with variables in the scope. - for name in &specifiers { - if name.is_empty() { - continue; - } - let name = Expr::Symbol(Symbol::new(name)); + // let lambda = |env: &mut Env, expr: Vec| { + // let params = expr[0].clone(); + // let body = expr[1].clone(); + // if let Expr::List(params) = params { + // Expr::Function(Some(Box::new(env.clone())), params, Box::new(body)) + // } else { + // return Expr::error(format!("Invalid params {:?}", params)); + // } + // }; + // env.bind_builtin("lambda", lambda); + // env.bind_builtin("\\", lambda); + + // env.bind_builtin("apply", |env, expr| { + // let f = env.eval(expr[0].clone()); + // let args = env.eval(expr[1].clone()); + // if let Expr::List(args) = args { + // match f { + // Expr::Function(Some(mut env), params, body) => { + // let mut new_env = env.clone(); + // for (param, arg) in params.iter().zip(args.iter()) { + // new_env.bind(param.clone(), env.eval(arg.clone())); + // } + // new_env.eval(*body.clone()) + // } + // Expr::Function(None, params, body) => { + // let mut new_env = env.clone(); + // for (param, arg) in params.iter().zip(args.iter()) { + // new_env.bind(param.clone(), env.eval(arg.clone())); + // } + // new_env.eval(*body.clone()) + // } + // Expr::Builtin(f) => f.apply(&mut env.clone(), args), + // f => Expr::error(format!("Invalid function {f} apply {}", Expr::from(args))), + // } + // } else { + // Expr::error(format!("Invalid function {f} apply {}", Expr::from(args))) + // } + // }); - let value = env.eval(name.clone()); - let specifier = format!("{{{name}}}"); - match value { - Expr::String(s) => { - format = format.replacen(&specifier, &s, 1); - } - other => { - format = format.replacen(&specifier, &other.to_string(), 1); - } - } - } + // env.bind_builtin("cons", |env, expr| { + // let a = env.eval(expr[0].clone()); + // let b = env.eval(expr[1].clone()); + // // Create a new list with a as the head and b as the tail. + // if let Expr::List(b) = b { + // let mut list = vec![a]; + // list.extend(b); + // Expr::List(list) + // } else if b == Expr::None { + // Expr::List(vec![a]) + // } else { + // Expr::List(vec![a, b]) + // } + // }); + // let head = |env: &mut Env, expr: Vec| { + // let a = env.eval(expr[0].clone()); + // if let Expr::List(a) = a { + // a[0].clone() + // } else { + // Expr::error(format!("Invalid head {a}")) + // } + // }; + // let tail = |env: &mut Env, expr: Vec| { + // let a = env.eval(expr[0].clone()); + // if let Expr::List(a) = a { + // Expr::List(a[1..].to_vec()) + // } else { + // Expr::error(format!("Invalid tail {a}")) + // } + // }; + + // env.bind_builtin("car", head); + // env.bind_builtin("head", head); + // env.bind_builtin("cdr", tail); + // env.bind_builtin("tail", tail); + + // let format = |env: &mut Env, expr: Vec| { + // let format = env.eval(expr[0].clone()); + // // Collect the args + // let args = expr[1..].to_vec(); + + // let mut format = match format { + // Expr::String(s) => s, + // e => return Expr::error(format!("Invalid format {e}")), + // }; + + // // Find all of the format specifiers. + // let mut specifiers = vec![]; + // for (i, c) in format.chars().enumerate() { + // if c == '{' { + // let mut j = i + 1; + // while j < format.len() { + // if format.chars().nth(j).unwrap() == '}' { + // break; + // } + // j += 1; + // } + // specifiers.push(format[i + 1..j].to_owned()); + // } + // } - // Replace the empty specifiers with the args. - let mut i = 0; - for name in &specifiers { - if !name.is_empty() { - continue; - } - if i >= args.len() { - return Expr::error("Too few arguments"); - } - let specifier = format!("{{}}"); - let value = env.eval(args[i].clone()); - match value { - Expr::String(s) => { - format = format.replacen(&specifier, &s, 1); - } - other => { - format = format.replacen(&specifier, &other.to_string(), 1); - } - } - // format = format.replacen("{}", &args[i].to_string(), 1); - i += 1; - } + // // Replace the named specifiers with variables in the scope. + // for name in &specifiers { + // if name.is_empty() { + // continue; + // } + // let name = Expr::Symbol(Symbol::new(name)); + + // let value = env.eval(name.clone()); + // let specifier = format!("{{{name}}}"); + // match value { + // Expr::String(s) => { + // format = format.replacen(&specifier, &s, 1); + // } + // other => { + // format = format.replacen(&specifier, &other.to_string(), 1); + // } + // } + // } - if i < args.len() { - return Expr::error("Too many arguments"); - } + // // Replace the empty specifiers with the args. + // let mut i = 0; + // for name in &specifiers { + // if !name.is_empty() { + // continue; + // } + // if i >= args.len() { + // return Expr::error("Too few arguments"); + // } + // let specifier = format!("{{}}"); + // let value = env.eval(args[i].clone()); + // match value { + // Expr::String(s) => { + // format = format.replacen(&specifier, &s, 1); + // } + // other => { + // format = format.replacen(&specifier, &other.to_string(), 1); + // } + // } + // // format = format.replacen("{}", &args[i].to_string(), 1); + // i += 1; + // } - Expr::String(format) - }; + // if i < args.len() { + // return Expr::error("Too many arguments"); + // } - env.bind_builtin("format", format); + // Expr::String(format) + // }; - env.bind_builtin("list", |env, expr| { - let mut list = vec![]; - for e in expr { - list.push(env.eval(e.clone())); - } - Expr::List(list) - }); - - env.bind_builtin("append", |env, expr| { - let mut list = vec![]; - for e in expr { - let e = env.eval(e.clone()); - if let Expr::List(l) = e { - list.extend(l); - } else { - return Expr::error(format!("Invalid append {e}")); - } - } - Expr::List(list) - }); - - env.bind_builtin("eval", |env, expr| { - let e = env.eval(expr[0].clone()); - env.eval(e) - }); - - env.bind_builtin("exit", |env, expr| match env.eval(expr[0].clone()) { - Expr::Int(i) => std::process::exit(i as i32), - Expr::String(s) => { - eprintln!("{s}"); - std::process::exit(1); - } - e => { - eprintln!("{e}"); - std::process::exit(1); - } - }); + // env.bind_builtin("format", format); - env.bind_builtin("quote", |_env, expr| expr[0].clone()); + // env.bind_builtin("list", |env, expr| { + // let mut list = vec![]; + // for e in expr { + // list.push(env.eval(e.clone())); + // } + // Expr::List(list) + // }); - env.bind_builtin("or", |env, expr| { - for e in expr { - let e = env.eval(e.clone()); - if e == Expr::Bool(true) { - return Expr::Bool(true); - } - } - Expr::Bool(false) - }); - - env.bind_builtin("and", |env, expr| { - for e in expr { - let e = env.eval(e.clone()); - if e == Expr::Bool(false) { - return Expr::Bool(false); - } - } - Expr::Bool(true) - }); - - env.bind_builtin("not", |env, expr| { - let e = env.eval(expr[0].clone()); - match e { - Expr::Bool(b) => Expr::Bool(!b), - e => return Expr::error(format!("Invalid not {e}")), - } - }); - - env.bind_builtin("len", |env, expr| { - let e = env.eval(expr[0].clone()); - match e { - Expr::String(s) => Expr::Int(s.len() as i64), - Expr::List(l) => Expr::Int(l.len() as i64), - Expr::Map(m) => Expr::Int(m.len() as i64), - Expr::Tree(t) => Expr::Int(t.len() as i64), - e => Expr::error(format!("Invalid len {e}")), - } - }); - - env.bind_builtin("let", |env, expr| { - let mut new_env = env.clone(); - let bindings = expr[0].clone(); - let body = expr[1].clone(); - match bindings { - Expr::List(bindings) => { - for binding in bindings { - if let Expr::List(binding) = binding { - let name = binding[0].clone(); - let value = env.eval(binding[1].clone()); - new_env.bind(name, value); - } else { - return Expr::error(format!("Invalid binding {binding}")); - } - } - } - Expr::Map(bindings) => { - for (name, value) in bindings { - new_env.bind(name, env.eval(value)); - } - } - Expr::Tree(bindings) => { - for (name, value) in bindings { - new_env.bind(name, env.eval(value)); - } - } - bindings => return Expr::error(format!("Invalid bindings {bindings}")), - } - new_env.eval(body) - }); - - fn get(env: &mut Env, expr: Vec) -> Expr { - let a = env.eval(expr[0].clone()); - let b = env.eval(expr[1].clone()); - - match (a, b) { - (Expr::Object(obj), member) => { - let val = obj.read().unwrap(); - match get(env, vec![val.clone(), member.clone()]) { - Expr::None => { - // Create a new object with the member as the value - let new_obj = Expr::Object(Arc::new(RwLock::new(Expr::None))); - let set_obj = set_attr(env, vec![val.clone(), member.clone(), new_obj.clone()]); - drop(val); - // Add the member to the object - // Write the new object to the object - *obj.write().unwrap() = set_obj; - new_obj - } - other => other, - } - - } - (Expr::String(a), Expr::Int(b)) => { - Expr::String(a.chars().nth(b as usize).unwrap_or('\0').to_string()) - } - (Expr::List(a), Expr::Int(b)) => a.get(b as usize).cloned().unwrap_or(Expr::None), - (Expr::Map(a), Expr::Symbol(b)) => { - a.get(&Expr::Symbol(b.clone())).cloned().unwrap_or_else(|| { - a.get(&Expr::String(b.name().to_owned())) - .cloned() - .unwrap_or(Expr::None) - }) - } - (Expr::Map(a), b) => a.get(&b).cloned().unwrap_or(Expr::None), - (Expr::Tree(a), b) => a.get(&b).cloned().unwrap_or(Expr::None), - (a, b) => return Expr::error(format!("Invalid expr get {} {}", a, b)), - } - } - env.bind_builtin("get", get); - env.alias("get", "."); - - fn method(env: &mut Env, expr: Vec) -> Expr { - // Apply a method with a self parameter - let obj = env.eval(expr[0].clone()); - let method_name = expr[1].clone(); - - // Get the param count - let method = get(env, vec![obj.clone(), method_name.clone()]); - let param_count = match &method { - Expr::Object(obj) => { - if let Expr::Function(_, params, _) = &*obj.read().unwrap() { - params.len() - } else { - return Expr::error(format!("Invalid method {method}")); - } - } - Expr::Function(_, params, _) => params.len(), - _ => return Expr::error(format!("Invalid method {method}")), - }; - // Return a function that takes the rest of the arguments, - // and applies the method with the object as the first argument. - let env = env.clone(); - - let mut args = vec![obj.clone()]; - let mut params = vec![]; - for i in 1..param_count { - let name = Symbol::from(format!("arg{}", i - 2)); - args.push(Expr::Symbol(name.clone())); - params.push(Expr::Symbol(name)); - } + // env.bind_builtin("append", |env, expr| { + // let mut list = vec![]; + // for e in expr { + // let e = env.eval(e.clone()); + // if let Expr::List(l) = e { + // list.extend(l); + // } else { + // return Expr::error(format!("Invalid append {e}")); + // } + // } + // Expr::List(list) + // }); - let body = method.apply(&args); + // env.bind_builtin("eval", |env, expr| { + // let e = env.eval(expr[0].clone()); + // env.eval(e) + // }); - let f = Expr::Function(None, params, Box::new(body)); - f + // env.bind_builtin("exit", |env, expr| match env.eval(expr[0].clone()) { + // Expr::Int(i) => std::process::exit(i as i32), + // Expr::String(s) => { + // eprintln!("{s}"); + // std::process::exit(1); + // } + // e => { + // eprintln!("{e}"); + // std::process::exit(1); + // } + // }); - // let method = get(env, vec![obj.clone(), method_name]); - // // Apply the method with the object as the first argument, and the rest of the arguments. - // let mut args = vec![obj]; - // args.extend(expr[2..].iter().cloned()); - } - env.bind_builtin("@", method); - fn set_attr(env: &mut Env, expr: Vec) -> Expr { - let a = env.eval(expr[0].clone()); - let b = env.eval(expr[1].clone()); - let c = env.eval(expr[2].clone()); - - match (a, b) { - (Expr::String(mut a), Expr::Int(b)) => { - if b == a.len() as i64 { - a.push_str(&c.to_string()); - } else { - a = a - .chars() - .enumerate() - .map(|(i, c)| if i == b as usize { c } else { '\0' }) - .collect(); - } - Expr::String(a) - } - (Expr::List(mut a), Expr::Int(b)) => { - if b as usize >= a.len() { - a.resize(b as usize + 1, Expr::None); - } - a[b as usize] = c; - Expr::List(a) - } - (Expr::Map(mut a), b) => { - a.insert(b, c); - Expr::Map(a) - } - (Expr::Tree(mut a), b) => { - a.insert(b, c); - Expr::Tree(a) - } - (a, b) => return Expr::error(format!("Invalid expr set {} {} {}", a, b, c)), - } - } - env.bind_builtin("set", set_attr); + // env.bind_builtin("quote", |_env, expr| expr[0].clone()); - env.bind_builtin("zip", |env, expr| { - let a = env.eval(expr[0].clone()); - let b = env.eval(expr[1].clone()); + // env.bind_builtin("or", |env, expr| { + // for e in expr { + // let e = env.eval(e.clone()); + // if e == Expr::Bool(true) { + // return Expr::Bool(true); + // } + // } + // Expr::Bool(false) + // }); - match (a, b) { - (Expr::List(a), Expr::List(b)) => { - let mut list = vec![]; - for (a, b) in a.into_iter().zip(b.into_iter()) { - list.push(Expr::List(vec![a, b])); - } - Expr::List(list) - } - (a, b) => return Expr::error(format!("Invalid expr zip {} {}", a, b)), - } - }); - - // Convert a list of pairs into a map. - env.bind_builtin("to-map", |env, expr| { - let a = env.eval(expr[0].clone()); - match a { - Expr::List(a) => { - let mut map = std::collections::HashMap::new(); - for e in a { - if let Expr::List(e) = e { - if e.len() == 2 { - map.insert(e[0].clone(), e[1].clone()); - } else { - return Expr::error(format!("Invalid pair {}", Expr::from(e))); - } - } else { - return Expr::error(format!("Invalid pair {}", Expr::from(e))); - } - } - Expr::Map(map) - } - Expr::Map(a) => return Expr::Map(a), - Expr::Tree(a) => return Expr::Map(a.into_iter().collect()), - a => return Expr::error(format!("Invalid expr to-map {}", Expr::from(a))), - } - }); - - // Convert a list of pairs into a tree. - env.bind_builtin("to-tree", |env, expr| { - let a = env.eval(expr[0].clone()); - match a { - Expr::List(a) => { - let mut tree = std::collections::BTreeMap::new(); - for e in a { - if let Expr::List(e) = e { - if e.len() == 2 { - tree.insert(e[0].clone(), e[1].clone()); - } else { - return Expr::error(format!("Invalid pair {}", Expr::from(e))); - } - } else { - return Expr::error(format!("Invalid pair {}", Expr::from(e))); - } - } - Expr::Tree(tree) - } - Expr::Map(a) => return Expr::Tree(a.into_iter().collect()), - Expr::Tree(a) => return Expr::Tree(a), - a => return Expr::error(format!("Invalid expr to-tree {}", Expr::from(a))), - } - }); - - env.bind_builtin("to-list", |env, expr| { - let a = env.eval(expr[0].clone()); - match a { - Expr::Map(a) => { - let mut list = vec![]; - for (k, v) in a { - list.push(Expr::List(vec![k, v])); - } - Expr::List(list) - } - Expr::Tree(a) => { - let mut list = vec![]; - for (k, v) in a { - list.push(Expr::List(vec![k, v])); - } - Expr::List(list) - } - Expr::List(a) => return Expr::List(a), - a => return Expr::error(format!("Invalid expr to-list {}", a)), - } - }); - - env.bind_builtin("map", |env, expr| { - let f = env.eval(expr[0].clone()); - let a = env.eval(expr[1].clone()); - match a { - Expr::List(a) => { - let mut list = vec![]; - for e in a { - list.push(env.eval(Expr::List(vec![f.clone(), e]))); - } - Expr::List(list) - } - Expr::Map(a) => { - let mut map = std::collections::HashMap::new(); - for (k, v) in a { - // map.insert(k.clone(), env.eval(Expr::List(vec![f.clone(), k, v]))); - let pair = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); - if let Expr::List(pair) = pair { - map.insert(pair[0].clone(), pair[1].clone()); - } else { - return Expr::error(format!("Invalid pair {}", pair)); - } - } - Expr::Map(map) - } - Expr::Tree(a) => { - let mut tree = std::collections::BTreeMap::new(); - for (k, v) in a { - // tree.insert(k.clone(), env.eval(Expr::List(vec![f.clone(), k, v]))); - let pair = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); - if let Expr::List(pair) = pair { - tree.insert(pair[0].clone(), pair[1].clone()); - } else { - return Expr::error(format!("Invalid pair {}", pair)); - } - } - Expr::Tree(tree) - } - a => return Expr::error(format!("Invalid expr map {}", a)), - } - }); - - env.bind_builtin("filter", |env, expr| { - let f = env.eval(expr[0].clone()); - let a = env.eval(expr[1].clone()); - - match a { - Expr::List(a) => { - let mut list = vec![]; - for e in a { - if env.eval(Expr::List(vec![f.clone(), e.clone()])) == Expr::Bool(true) { - list.push(e); - } - } - Expr::List(list) - } - Expr::Map(a) => { - let mut map = std::collections::HashMap::new(); - for (k, v) in a { - let x = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); - if x == Expr::Bool(true) { - map.insert(k, v); - } - } - Expr::Map(map) - } - Expr::Tree(a) => { - let mut tree = std::collections::BTreeMap::new(); - for (k, v) in a { - let x = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); - if x == Expr::Bool(true) { - tree.insert(k, v); - } - } - Expr::Tree(tree) - } - a => return Expr::error(format!("Invalid expr filter {}", a)), - } - }); - - env.bind_builtin("reduce", |env, expr| { - let f = env.eval(expr[0].clone()); - let a = env.eval(expr[1].clone()); - let b = env.eval(expr[2].clone()); - - match a { - Expr::List(a) => { - let mut acc = b; - for e in a { - acc = env.eval(Expr::List(vec![f.clone(), acc, e])); - } - acc - } - Expr::Map(a) => { - let mut acc = b; - for (k, v) in a { - acc = env.eval(Expr::List(vec![f.clone(), acc, k, v])); - } - acc - } - Expr::Tree(a) => { - let mut acc = b; - for (k, v) in a { - acc = env.eval(Expr::List(vec![f.clone(), acc, k, v])); - } - acc - } - a => return Expr::error(format!("Invalid expr reduce {}", a)), - } - }); - - env.bind_builtin("range", |env, expr| { - let a = env.eval(expr[0].clone()); - let b = env.eval(expr[1].clone()); - // Check if there is a step - let c = if expr.len() == 3 { - env.eval(expr[2].clone()) - } else { - Expr::Int(1) - }; - - let (a, b) = match (a, b) { - (Expr::Int(a), Expr::Int(b)) => (a, b), - (Expr::Float(a), Expr::Float(b)) => (a as i64, b as i64), - (Expr::Int(a), Expr::Float(b)) => (a, b as i64), - (Expr::Float(a), Expr::Int(b)) => (a as i64, b), - (a, b) => return Expr::error(format!("Invalid expr range {} {}", a, b)), - }; - - let c = match c { - Expr::Int(c) => c, - Expr::Float(c) => c as i64, - c => return Expr::error(format!("Invalid expr range {}", c)), - }; - - let mut list = vec![]; - let mut i = a; - while i <= b { - list.push(Expr::Int(i)); - i += c; - } - Expr::List(list) - }); - - env.bind_builtin("rev", |env, expr| { - let a = env.eval(expr[0].clone()); - match a { - Expr::List(mut a) => { - a.reverse(); - Expr::List(a) - } - a => return Expr::error(format!("Invalid expr rev {}", a)), - } - }); - - env.bind_builtin("rand", |env, expr| { - use rand::Rng; - let low = env.eval(expr[0].clone()); - let high = env.eval(expr[1].clone()); - match (low, high) { - (Expr::Int(low), Expr::Int(high)) => { - let mut rng = rand::thread_rng(); - Expr::Int(rng.gen_range(low..=high)) - } - (Expr::Float(low), Expr::Float(high)) => { - let mut rng = rand::thread_rng(); - Expr::Float(rng.gen_range(low..=high)) - } - (Expr::Int(low), Expr::Float(high)) => { - let mut rng = rand::thread_rng(); - Expr::Float(rng.gen_range(low as f64..=high)) - } - (Expr::Float(low), Expr::Int(high)) => { - let mut rng = rand::thread_rng(); - Expr::Float(rng.gen_range(low..=high as f64)) - } - (a, b) => return Expr::error(format!("Invalid expr rand {} {}", a, b)), - } - }); - - env.bind_builtin("read", |env, expr| { - // Read a file - let path = env.eval(expr[0].clone()); - - match path { - Expr::String(path) => { - let path = std::path::Path::new(&path); - let file = std::fs::File::open(path).unwrap(); - let reader = std::io::BufReader::new(file); - let mut code = String::new(); - for line in reader.lines() { - code.push_str(&line.unwrap()); - code.push_str("\n"); - } - Expr::String(code) - } - a => return Expr::error(format!("Invalid expr read {}", a)), - } - }); + // env.bind_builtin("and", |env, expr| { + // for e in expr { + // let e = env.eval(e.clone()); + // if e == Expr::Bool(false) { + // return Expr::Bool(false); + // } + // } + // Expr::Bool(true) + // }); + + // env.bind_builtin("not", |env, expr| { + // let e = env.eval(expr[0].clone()); + // match e { + // Expr::Bool(b) => Expr::Bool(!b), + // e => return Expr::error(format!("Invalid not {e}")), + // } + // }); - env.bind_builtin("write", |env, expr| { - // Write a file - use std::io::Write; - let path = env.eval(expr[0].clone()); + // env.bind_builtin("len", |env, expr| { + // let e = env.eval(expr[0].clone()); + // match e { + // Expr::String(s) => Expr::Int(s.len() as i64), + // Expr::List(l) => Expr::Int(l.len() as i64), + // Expr::Map(m) => Expr::Int(m.len() as i64), + // Expr::Tree(t) => Expr::Int(t.len() as i64), + // e => Expr::error(format!("Invalid len {e}")), + // } + // }); - let content = env.eval(expr[1].clone()); + // env.bind_builtin("let", |env, expr| { + // let mut new_env = env.clone(); + // let bindings = expr[0].clone(); + // let body = expr[1].clone(); + // match bindings { + // Expr::List(bindings) => { + // for binding in bindings { + // if let Expr::List(binding) = binding { + // let name = binding[0].clone(); + // let value = env.eval(binding[1].clone()); + // new_env.bind(name, value); + // } else { + // return Expr::error(format!("Invalid binding {binding}")); + // } + // } + // } + // Expr::Map(bindings) => { + // for (name, value) in bindings { + // new_env.bind(name, env.eval(value)); + // } + // } + // Expr::Tree(bindings) => { + // for (name, value) in bindings { + // new_env.bind(name, env.eval(value)); + // } + // } + // bindings => return Expr::error(format!("Invalid bindings {bindings}")), + // } + // new_env.eval(body) + // }); - match (path, content) { - (Expr::String(path), Expr::String(content)) => { - let path = std::path::Path::new(&path); - let mut file = std::fs::File::create(path).unwrap(); - file.write_all(content.as_bytes()).unwrap(); - Expr::None - } - (a, b) => return Expr::error(format!("Invalid expr write {} {}", a, b)), - } - }); - - env.bind_builtin("shell", |env, expr| { - // Run a shell command - let cmd = env.eval(expr[0].clone()); - - match cmd { - Expr::String(cmd) => { - let output = std::process::Command::new("sh") - .arg("-c") - .arg(&cmd) - .output() - .expect("failed to execute process"); - let stdout = String::from_utf8(output.stdout).unwrap(); - let stderr = String::from_utf8(output.stderr).unwrap(); - Expr::List(vec![Expr::String(stdout), Expr::String(stderr)]) - } - a => return Expr::error(format!("Invalid expr shell {}", a)), - } - }); + // env.bind_builtin("get", get); + // env.alias("get", "."); + + // fn method(env: &mut Env, expr: Vec) -> Expr { + // // Apply a method with a self parameter + // let obj = env.eval(expr[0].clone()); + // let method_name = expr[1].clone(); + + // // Get the param count + // let method = get(env, vec![obj.clone(), method_name.clone()]); + // let param_count = match &method { + // Expr::Object(obj) => { + // if let Expr::Function(_, params, _) = &*obj.read().unwrap() { + // params.len() + // } else { + // return Expr::error(format!("Invalid method {method}")); + // } + // } + // Expr::Function(_, params, _) => params.len(), + // _ => return Expr::error(format!("Invalid method {method}")), + // }; + // // Return a function that takes the rest of the arguments, + // // and applies the method with the object as the first argument. + // let env = env.clone(); + + // let mut args = vec![obj.clone()]; + // let mut params = vec![]; + // for i in 1..param_count { + // let name = Symbol::from(format!("arg{}", i)); + // args.push(Expr::Symbol(name.clone())); + // params.push(Expr::Symbol(name)); + // } + + // let body = method.apply(&args); + + // let f = Expr::Function(None, params, Box::new(body)); + // f + + // // let method = get(env, vec![obj.clone(), method_name]); + // // // Apply the method with the object as the first argument, and the rest of the arguments. + // // let mut args = vec![obj]; + // // args.extend(expr[2..].iter().cloned()); + // } + // env.bind_builtin("@", method); + + // env.bind_builtin("zip", |env, expr| { + // let a = env.eval(expr[0].clone()); + // let b = env.eval(expr[1].clone()); + + // match (a, b) { + // (Expr::List(a), Expr::List(b)) => { + // let mut list = vec![]; + // for (a, b) in a.into_iter().zip(b.into_iter()) { + // list.push(Expr::List(vec![a, b])); + // } + // Expr::List(list) + // } + // (a, b) => return Expr::error(format!("Invalid expr zip {} {}", a, b)), + // } + // }); + + // // Convert a list of pairs into a map. + // env.bind_builtin("to-map", |env, expr| { + // let a = env.eval(expr[0].clone()); + // match a { + // Expr::List(a) => { + // let mut map = std::collections::HashMap::new(); + // for e in a { + // if let Expr::List(e) = e { + // if e.len() == 2 { + // map.insert(e[0].clone(), e[1].clone()); + // } else { + // return Expr::error(format!("Invalid pair {}", Expr::from(e))); + // } + // } else { + // return Expr::error(format!("Invalid pair {}", Expr::from(e))); + // } + // } + // Expr::Map(map) + // } + // Expr::Map(a) => return Expr::Map(a), + // Expr::Tree(a) => return Expr::Map(a.into_iter().collect()), + // a => return Expr::error(format!("Invalid expr to-map {}", Expr::from(a))), + // } + // }); + + // // Convert a list of pairs into a tree. + // env.bind_builtin("to-tree", |env, expr| { + // let a = env.eval(expr[0].clone()); + // match a { + // Expr::List(a) => { + // let mut tree = std::collections::BTreeMap::new(); + // for e in a { + // if let Expr::List(e) = e { + // if e.len() == 2 { + // tree.insert(e[0].clone(), e[1].clone()); + // } else { + // return Expr::error(format!("Invalid pair {}", Expr::from(e))); + // } + // } else { + // return Expr::error(format!("Invalid pair {}", Expr::from(e))); + // } + // } + // Expr::Tree(tree) + // } + // Expr::Map(a) => return Expr::Tree(a.into_iter().collect()), + // Expr::Tree(a) => return Expr::Tree(a), + // a => return Expr::error(format!("Invalid expr to-tree {}", Expr::from(a))), + // } + // }); + + // env.bind_builtin("to-list", |env, expr| { + // let a = env.eval(expr[0].clone()); + // match a { + // Expr::Map(a) => { + // let mut list = vec![]; + // for (k, v) in a { + // list.push(Expr::List(vec![k, v])); + // } + // Expr::List(list) + // } + // Expr::Tree(a) => { + // let mut list = vec![]; + // for (k, v) in a { + // list.push(Expr::List(vec![k, v])); + // } + // Expr::List(list) + // } + // Expr::List(a) => return Expr::List(a), + // a => return Expr::error(format!("Invalid expr to-list {}", a)), + // } + // }); + + // env.bind_builtin("map", |env, expr| { + // let f = env.eval(expr[0].clone()); + // let a = env.eval(expr[1].clone()); + // match a { + // Expr::List(a) => { + // let mut list = vec![]; + // for e in a { + // list.push(env.eval(Expr::List(vec![f.clone(), e]))); + // } + // Expr::List(list) + // } + // Expr::Map(a) => { + // let mut map = std::collections::HashMap::new(); + // for (k, v) in a { + // // map.insert(k.clone(), env.eval(Expr::List(vec![f.clone(), k, v]))); + // let pair = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + // if let Expr::List(pair) = pair { + // map.insert(pair[0].clone(), pair[1].clone()); + // } else { + // return Expr::error(format!("Invalid pair {}", pair)); + // } + // } + // Expr::Map(map) + // } + // Expr::Tree(a) => { + // let mut tree = std::collections::BTreeMap::new(); + // for (k, v) in a { + // // tree.insert(k.clone(), env.eval(Expr::List(vec![f.clone(), k, v]))); + // let pair = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + // if let Expr::List(pair) = pair { + // tree.insert(pair[0].clone(), pair[1].clone()); + // } else { + // return Expr::error(format!("Invalid pair {}", pair)); + // } + // } + // Expr::Tree(tree) + // } + // a => return Expr::error(format!("Invalid expr map {}", a)), + // } + // }); + + // env.bind_builtin("filter", |env, expr| { + // let f = env.eval(expr[0].clone()); + // let a = env.eval(expr[1].clone()); + + // match a { + // Expr::List(a) => { + // let mut list = vec![]; + // for e in a { + // if env.eval(Expr::List(vec![f.clone(), e.clone()])) == Expr::Bool(true) { + // list.push(e); + // } + // } + // Expr::List(list) + // } + // Expr::Map(a) => { + // let mut map = std::collections::HashMap::new(); + // for (k, v) in a { + // let x = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + // if x == Expr::Bool(true) { + // map.insert(k, v); + // } + // } + // Expr::Map(map) + // } + // Expr::Tree(a) => { + // let mut tree = std::collections::BTreeMap::new(); + // for (k, v) in a { + // let x = env.eval(Expr::List(vec![f.clone(), k.quote(), v.quote()])); + // if x == Expr::Bool(true) { + // tree.insert(k, v); + // } + // } + // Expr::Tree(tree) + // } + // a => return Expr::error(format!("Invalid expr filter {}", a)), + // } + // }); + + // env.bind_builtin("reduce", |env, expr| { + // let f = env.eval(expr[0].clone()); + // let a = env.eval(expr[1].clone()); + // let b = env.eval(expr[2].clone()); + + // match a { + // Expr::List(a) => { + // let mut acc = b; + // for e in a { + // acc = env.eval(Expr::List(vec![f.clone(), acc, e])); + // } + // acc + // } + // Expr::Map(a) => { + // let mut acc = b; + // for (k, v) in a { + // acc = env.eval(Expr::List(vec![f.clone(), acc, k, v])); + // } + // acc + // } + // Expr::Tree(a) => { + // let mut acc = b; + // for (k, v) in a { + // acc = env.eval(Expr::List(vec![f.clone(), acc, k, v])); + // } + // acc + // } + // a => return Expr::error(format!("Invalid expr reduce {}", a)), + // } + // }); + + // env.bind_builtin("range", |env, expr| { + // let a = env.eval(expr[0].clone()); + // let b = env.eval(expr[1].clone()); + // // Check if there is a step + // let c = if expr.len() == 3 { + // env.eval(expr[2].clone()) + // } else { + // Expr::Int(1) + // }; + + // let (a, b) = match (a, b) { + // (Expr::Int(a), Expr::Int(b)) => (a, b), + // (Expr::Float(a), Expr::Float(b)) => (a as i64, b as i64), + // (Expr::Int(a), Expr::Float(b)) => (a, b as i64), + // (Expr::Float(a), Expr::Int(b)) => (a as i64, b), + // (a, b) => return Expr::error(format!("Invalid expr range {} {}", a, b)), + // }; + + // let c = match c { + // Expr::Int(c) => c, + // Expr::Float(c) => c as i64, + // c => return Expr::error(format!("Invalid expr range {}", c)), + // }; + + // let mut list = vec![]; + // let mut i = a; + // while i <= b { + // list.push(Expr::Int(i)); + // i += c; + // } + // Expr::List(list) + // }); + + // env.bind_builtin("rev", |env, expr| { + // let a = env.eval(expr[0].clone()); + // match a { + // Expr::List(mut a) => { + // a.reverse(); + // Expr::List(a) + // } + // a => return Expr::error(format!("Invalid expr rev {}", a)), + // } + // }); + + // env.bind_builtin("rand", |env, expr| { + // use rand::Rng; + // let low = env.eval(expr[0].clone()); + // let high = env.eval(expr[1].clone()); + // match (low, high) { + // (Expr::Int(low), Expr::Int(high)) => { + // let mut rng = rand::thread_rng(); + // Expr::Int(rng.gen_range(low..=high)) + // } + // (Expr::Float(low), Expr::Float(high)) => { + // let mut rng = rand::thread_rng(); + // Expr::Float(rng.gen_range(low..=high)) + // } + // (Expr::Int(low), Expr::Float(high)) => { + // let mut rng = rand::thread_rng(); + // Expr::Float(rng.gen_range(low as f64..=high)) + // } + // (Expr::Float(low), Expr::Int(high)) => { + // let mut rng = rand::thread_rng(); + // Expr::Float(rng.gen_range(low..=high as f64)) + // } + // (a, b) => return Expr::error(format!("Invalid expr rand {} {}", a, b)), + // } + // }); + + // env.bind_builtin("read", |env, expr| { + // // Read a file + // let path = env.eval(expr[0].clone()); + + // match path { + // Expr::String(path) => { + // let path = std::path::Path::new(&path); + // let file = std::fs::File::open(path).unwrap(); + // let reader = std::io::BufReader::new(file); + // let mut code = String::new(); + // for line in reader.lines() { + // code.push_str(&line.unwrap()); + // code.push_str("\n"); + // } + // Expr::String(code) + // } + // a => return Expr::error(format!("Invalid expr read {}", a)), + // } + // }); + + // env.bind_builtin("write", |env, expr| { + // // Write a file + // use std::io::Write; + // let path = env.eval(expr[0].clone()); + + // let content = env.eval(expr[1].clone()); + + // match (path, content) { + // (Expr::String(path), Expr::String(content)) => { + // let path = std::path::Path::new(&path); + // let mut file = std::fs::File::create(path).unwrap(); + // file.write_all(content.as_bytes()).unwrap(); + // Expr::None + // } + // (a, b) => return Expr::error(format!("Invalid expr write {} {}", a, b)), + // } + // }); + + // env.bind_builtin("shell", |env, expr| { + // // Run a shell command + // let cmd = env.eval(expr[0].clone()); + + // match cmd { + // Expr::String(cmd) => { + // let output = std::process::Command::new("sh") + // .arg("-c") + // .arg(&cmd) + // .output() + // .expect("failed to execute process"); + // let stdout = String::from_utf8(output.stdout).unwrap(); + // let stderr = String::from_utf8(output.stderr).unwrap(); + // Expr::List(vec![Expr::String(stdout), Expr::String(stderr)]) + // } + // a => return Expr::error(format!("Invalid expr shell {}", a)), + // } + // }); env } From 197fe85120355043adcc23c02ec77942b766c1fd Mon Sep 17 00:00:00 2001 From: Adam McDaniel Date: Tue, 19 Nov 2024 22:56:10 -0500 Subject: [PATCH 3/3] Added to examples and stripped unused value from sys.args --- examples/code/args.lisp | 3 +++ src/extensions/system.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 examples/code/args.lisp diff --git a/examples/code/args.lisp b/examples/code/args.lisp new file mode 100644 index 0000000..d5df237 --- /dev/null +++ b/examples/code/args.lisp @@ -0,0 +1,3 @@ +{ + (io.println (fmt.show (sys.args))) +} \ No newline at end of file diff --git a/src/extensions/system.rs b/src/extensions/system.rs index 876fcaf..3605e28 100644 --- a/src/extensions/system.rs +++ b/src/extensions/system.rs @@ -57,7 +57,7 @@ fn get_system_env(_env: &mut Env, _args: Vec) -> Expr { fn get_system_args(_env: &mut Env, _args: Vec) -> Expr { // Get the arguments supplied to the program - let args: Vec = std::env::args().map(Expr::String).collect(); + let args: Vec = std::env::args().skip(1).map(Expr::String).collect(); return Expr::List(args); }