From fc245348b4856c0ee39a8cd5ba1631e3e9e21b9b Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Tue, 3 Jun 2025 18:54:33 -0400 Subject: [PATCH] start pulling base fns into bytecode interpreter --- src/base.rs | 436 ++++++++++++++++++++++++------------------------ src/compiler.rs | 18 +- src/main.rs | 8 +- src/value.rs | 30 +++- src/vm.rs | 47 ++++-- 5 files changed, 283 insertions(+), 256 deletions(-) diff --git a/src/base.rs b/src/base.rs index 1a40e5c..08fdf1f 100644 --- a/src/base.rs +++ b/src/base.rs @@ -4,40 +4,44 @@ use ran::ran_f64; use std::rc::Rc; #[derive(Clone, Debug)] -pub enum BaseFn<'src> { - Nullary(fn() -> Value<'src>), - Unary(fn(&Value<'src>) -> Value<'src>), - Binary(fn(&Value<'src>, &Value<'src>) -> Value<'src>), - Ternary(fn(&Value<'src>, &Value<'src>, &Value<'src>) -> Value<'src>), +pub enum BaseFn { + Nullary(fn() -> Value), + Unary(fn(&Value) -> Value), + Binary(fn(&Value, &Value) -> Value), + Ternary(fn(&Value, &Value, &Value) -> Value), } -pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { - Value::Boolean(x == y) +pub fn eq(x: &Value, y: &Value) -> Value { + if x == y { + Value::True + } else { + Value::False + } } -pub fn add<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { +pub fn add(x: &Value, y: &Value) -> Value { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x + y), _ => unreachable!("internal Ludus error"), } } -pub fn sub<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { +pub fn sub(x: &Value, y: &Value) -> Value { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x - y), _ => unreachable!("internal Ludus error"), } } -pub fn unbox<'src>(x: &Value<'src>) -> Value<'src> { +pub fn unbox(x: &Value) -> Value { match x { - Value::Box(_, cell) => cell.borrow().clone(), + Value::Box(cell) => cell.as_ref().borrow().clone(), _ => unreachable!("internal Ludus error"), } } -pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> Value<'src> { - if let Value::Box(_, cell) = b { +pub fn store(b: &Value, val: &Value) -> Value { + if let Value::Box(cell) = b { cell.replace(val.clone()); val.clone() } else { @@ -47,103 +51,97 @@ pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> Value<'src> { // TODO: do better than returning just the docstr // name, patterns, AND docstring -pub fn doc<'src>(f: &Value<'src>) -> Value<'src> { +pub fn doc(f: &Value) -> Value { match f { Value::Fn(f) => { - let name = &f.borrow().name; - let doc = &f.borrow().doc; + let lfn = f.as_ref().get().unwrap(); + let name = lfn.name; + let doc = lfn.doc; if let Some(docstr) = doc { - Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))) + Value::String(Rc::new(format!("{name}: {docstr}"))) } else { - Value::AllocatedString(Rc::new(format!("{name}: no documentation found"))) + Value::String(Rc::new(format!("{name}: no documentation found"))) } } - _ => Value::InternedString("no documentation found"), + _ => Value::Interned("no documentation found"), } } -pub fn and<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { - Value::Boolean(x.bool() && y.bool()) -} - -pub fn assoc<'src>(dict: &Value<'src>, key: &Value<'src>, value: &Value<'src>) -> Value<'src> { +pub fn assoc(dict: &Value, key: &Value, value: &Value) -> Value { match (dict, key) { - (Value::Dict(d), Value::Keyword(k)) => Value::Dict(d.update(k, value.clone())), + (Value::Dict(d), Value::Keyword(k)) => Value::Dict(Box::new(d.update(k, value.clone()))), _ => unreachable!("internal Ludus error"), } } -pub fn r#bool<'src>(x: &Value<'src>) -> Value<'src> { - Value::Boolean(x.bool()) +pub fn r#bool(x: &Value) -> Value { + match x { + Value::Nil | Value::False => Value::False, + _ => Value::True, + } } -pub fn chars<'src>(x: &Value<'src>) -> Value<'src> { +pub fn chars(x: &Value) -> Value { match x { - Value::InternedString(s) => { + Value::Interned(s) => { let chars = s.chars(); let mut charlist = vector![]; for char in chars { if char.is_ascii() { - charlist.push_back(Value::AllocatedString(Rc::new(char.to_string()))) + charlist.push_back(Value::String(Rc::new(char.to_string()))) } else { return Value::Tuple(Rc::new(vec![ Value::Keyword("err"), - Value::AllocatedString(Rc::new(format!( - "{char} is not an ascii character" - ))), + Value::String(Rc::new(format!("{char} is not an ascii character"))), ])); } } - Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::List(charlist)])) + Value::Tuple(Rc::new(vec![ + Value::Keyword("ok"), + Value::List(Box::new(charlist)), + ])) } - Value::AllocatedString(s) => { + Value::String(s) => { let chars = s.chars(); let mut charlist = vector![]; for char in chars { if char.is_ascii() { - charlist.push_back(Value::AllocatedString(Rc::new(char.to_string()))) + charlist.push_back(Value::String(Rc::new(char.to_string()))) } else { return Value::Tuple(Rc::new(vec![ Value::Keyword("err"), - Value::AllocatedString(Rc::new(format!( - "{char} is not an ascii character" - ))), + Value::String(Rc::new(format!("{char} is not an ascii character"))), ])); } } - Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::List(charlist)])) + Value::Tuple(Rc::new(vec![ + Value::Keyword("ok"), + Value::List(Box::new(charlist)), + ])) } _ => unreachable!("internal Ludus error"), } } // TODO: figure out how to get to opportunistic mutation here -pub fn concat<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { +pub fn concat(x: &Value, y: &Value) -> Value { match (x, y) { - (Value::InternedString(x), Value::InternedString(y)) => { - Value::AllocatedString(Rc::new(format!("{x}{y}"))) - } - (Value::AllocatedString(x), Value::AllocatedString(y)) => { - Value::AllocatedString(Rc::new(format!("{x}{y}"))) - } - (Value::AllocatedString(x), Value::InternedString(y)) => { - Value::AllocatedString(Rc::new(format!("{x}{y}"))) - } - (Value::InternedString(x), Value::AllocatedString(y)) => { - Value::AllocatedString(Rc::new(format!("{x}{y}"))) - } + (Value::Interned(x), Value::Interned(y)) => Value::String(Rc::new(format!("{x}{y}"))), + (Value::String(x), Value::String(y)) => Value::String(Rc::new(format!("{x}{y}"))), + (Value::String(x), Value::Interned(y)) => Value::String(Rc::new(format!("{x}{y}"))), + (Value::Interned(x), Value::String(y)) => Value::String(Rc::new(format!("{x}{y}"))), (Value::List(x), Value::List(y)) => { - let mut newlist = x.clone(); - newlist.append(y.clone()); - Value::List(newlist) + let mut newlist = *x.clone(); + newlist.append(*y.clone()); + Value::List(Box::new(newlist)) } _ => unreachable!("internal Ludus error"), } } -pub fn append<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { +pub fn append(x: &Value, y: &Value) -> Value { match x { Value::List(list) => { let mut newlist = list.clone(); @@ -154,35 +152,35 @@ pub fn append<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { } } -pub fn dec<'src>(x: &Value<'src>) -> Value<'src> { +pub fn dec(x: &Value) -> Value { match x { Value::Number(n) => Value::Number(n - 1.0), _ => unreachable!("internal Ludus error"), } } -pub fn inc<'src>(x: &Value<'src>) -> Value<'src> { +pub fn inc(x: &Value) -> Value { match x { Value::Number(n) => Value::Number(n + 1.0), _ => unreachable!("internal Ludus error"), } } -pub fn div<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { +pub fn div(x: &Value, y: &Value) -> Value { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x / y), _ => unreachable!("internal Ludus error"), } } -pub fn mult<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { +pub fn mult(x: &Value, y: &Value) -> Value { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x * y), _ => unreachable!("internal Ludus error"), } } -pub fn dissoc<'src>(dict: &Value<'src>, key: &Value<'src>) -> Value<'src> { +pub fn dissoc(dict: &Value, key: &Value) -> Value { match (dict, key) { (Value::Dict(dict), Value::Keyword(key)) => { let mut new = dict.clone(); @@ -193,7 +191,7 @@ pub fn dissoc<'src>(dict: &Value<'src>, key: &Value<'src>) -> Value<'src> { } } -pub fn first<'src>(ordered: &Value<'src>) -> Value<'src> { +pub fn first(ordered: &Value) -> Value { match ordered { Value::List(list) => match list.front() { Some(n) => n.clone(), @@ -209,7 +207,7 @@ pub fn first<'src>(ordered: &Value<'src>) -> Value<'src> { // TODO: figure out how to handle negative numbers // the cast from f64 to usize discards sign info -pub fn at<'src>(ordered: &Value<'src>, i: &Value<'src>) -> Value<'src> { +pub fn at(ordered: &Value, i: &Value) -> Value { match (ordered, i) { (Value::List(list), Value::Number(n)) => { let i = *n as usize; @@ -229,7 +227,7 @@ pub fn at<'src>(ordered: &Value<'src>, i: &Value<'src>) -> Value<'src> { } } -pub fn get<'src>(dict: &Value<'src>, key: &Value<'src>) -> Value<'src> { +pub fn get(dict: &Value, key: &Value) -> Value { match (dict, key) { (Value::Dict(dict), Value::Keyword(key)) => match dict.get(key) { Some(x) => x.clone(), @@ -239,7 +237,7 @@ pub fn get<'src>(dict: &Value<'src>, key: &Value<'src>) -> Value<'src> { } } -pub fn last<'src>(ordered: &Value<'src>) -> Value<'src> { +pub fn last(ordered: &Value) -> Value { match ordered { Value::List(list) => match list.last() { Some(x) => x.clone(), @@ -253,12 +251,8 @@ pub fn last<'src>(ordered: &Value<'src>) -> Value<'src> { } } -pub fn or<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { - Value::Boolean(x.bool() || y.bool()) -} - // TODO: fix this: x is a list of all the args passed to Ludus's print! -pub fn print<'src>(x: &Value<'src>) -> Value<'src> { +pub fn print(x: &Value) -> Value { let Value::List(args) = x else { unreachable!("internal Ludus error") }; @@ -271,30 +265,32 @@ pub fn print<'src>(x: &Value<'src>) -> Value<'src> { Value::Keyword("ok") } -pub fn show<'src>(x: &Value<'src>) -> Value<'src> { - Value::AllocatedString(Rc::new(format!("{x}"))) +pub fn show(x: &Value) -> Value { + Value::String(Rc::new(format!("{x}"))) } -pub fn rest<'src>(ordered: &Value<'src>) -> Value<'src> { +pub fn rest(ordered: &Value) -> Value { match ordered { - Value::List(list) => Value::List(list.clone().split_at(1).1), - Value::Tuple(tuple) => Value::List(Vector::from_iter(tuple.iter().next().cloned())), + Value::List(list) => Value::List(Box::new(list.clone().split_at(1).1)), + Value::Tuple(tuple) => { + Value::List(Box::new(Vector::from_iter(tuple.iter().next().cloned()))) + } _ => unreachable!("internal Ludus error"), } } -pub fn count<'src>(coll: &Value<'src>) -> Value<'src> { +pub fn count(coll: &Value) -> Value { match coll { Value::Dict(d) => Value::Number(d.len() as f64), Value::List(l) => Value::Number(l.len() as f64), Value::Tuple(t) => Value::Number(t.len() as f64), - Value::AllocatedString(s) => Value::Number(s.len() as f64), - Value::InternedString(s) => Value::Number(s.len() as f64), + Value::String(s) => Value::Number(s.len() as f64), + Value::Interned(s) => Value::Number(s.len() as f64), _ => unreachable!("internal Ludus error"), } } -pub fn range<'src>(start: &Value<'src>, end: &Value<'src>) -> Value<'src> { +pub fn range(start: &Value, end: &Value) -> Value { match (start, end) { (Value::Number(start), Value::Number(end)) => { let start = *start as isize; @@ -303,25 +299,25 @@ pub fn range<'src>(start: &Value<'src>, end: &Value<'src>) -> Value<'src> { for n in start..end { range.push_back(Value::Number(n as f64)) } - Value::List(range) + Value::List(Box::new(range)) } _ => unreachable!("internal Ludus error"), } } -pub fn slice<'src>(ordered: &Value<'src>, start: &Value<'src>, end: &Value<'src>) -> Value<'src> { +pub fn slice(ordered: &Value, start: &Value, end: &Value) -> Value { match (ordered, start, end) { (Value::List(list), Value::Number(start), Value::Number(end)) => { let mut newlist = list.clone(); let start = std::cmp::max(*start as usize, 0); let end = std::cmp::min(*end as usize, list.len()); - Value::List(newlist.slice(start..end)) + Value::List(Box::new(newlist.slice(start..end))) } // TODO: figure out something better to do than return an empty string on a bad slice - (Value::AllocatedString(string), Value::Number(start), Value::Number(end)) => { + (Value::String(string), Value::Number(start), Value::Number(end)) => { let start = std::cmp::max(*start as usize, 0); let end = std::cmp::min(*end as usize, string.len()); - Value::AllocatedString(Rc::new( + Value::String(Rc::new( string .clone() .as_str() @@ -330,19 +326,19 @@ pub fn slice<'src>(ordered: &Value<'src>, start: &Value<'src>, end: &Value<'src> .to_string(), )) } - (Value::InternedString(string), Value::Number(start), Value::Number(end)) => { + (Value::Interned(string), Value::Number(start), Value::Number(end)) => { let start = std::cmp::max(*start as usize, 0); let end = std::cmp::min(*end as usize, string.len()); - Value::AllocatedString(Rc::new(string.get(start..end).unwrap_or("").to_string())) + Value::String(Rc::new(string.get(start..end).unwrap_or("").to_string())) } _ => unreachable!("internal Ludus error"), } } -pub fn list<'src>(x: &Value<'src>) -> Value<'src> { +pub fn list(x: &Value) -> Value { match x { Value::List(_) => x.clone(), - Value::Tuple(t) => Value::List(Vector::from_iter(t.iter().cloned())), + Value::Tuple(t) => Value::List(Box::new(Vector::from_iter(t.iter().cloned()))), Value::Dict(d) => { let kvs = d.iter(); let mut list = vector![]; @@ -350,292 +346,300 @@ pub fn list<'src>(x: &Value<'src>) -> Value<'src> { let kv = Value::Tuple(Rc::new(vec![Value::Keyword(key), value.clone()])); list.push_back(kv); } - Value::List(list) + Value::List(Box::new(list)) } - _ => Value::List(vector![x.clone()]), + _ => Value::List(Box::new(vector![x.clone()])), } } -pub fn number<'src>(x: &Value<'src>) -> Value<'src> { +pub fn number(x: &Value) -> Value { match x { - Value::InternedString(string) => match string.parse::() { + Value::Interned(string) => match string.parse::() { Ok(n) => Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::Number(n)])), Err(_) => Value::Tuple(Rc::new(vec![ Value::Keyword("err"), - Value::AllocatedString(Rc::new(format!("could not parse `{string}` as a number"))), + Value::String(Rc::new(format!("could not parse `{string}` as a number"))), ])), }, - Value::AllocatedString(string) => match string.parse::() { + Value::String(string) => match string.parse::() { Ok(n) => Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::Number(n)])), Err(_) => Value::Tuple(Rc::new(vec![ Value::Keyword("err"), - Value::AllocatedString(Rc::new(format!("could not parse `{string}` as a number"))), + Value::String(Rc::new(format!("could not parse `{string}` as a number"))), ])), }, _ => unreachable!("internal Ludus error"), } } -pub fn r#type<'src>(x: &Value<'src>) -> Value<'src> { +pub fn r#type(x: &Value) -> Value { match x { Value::Nil => Value::Keyword("nil"), Value::Number(_) => Value::Keyword("number"), - Value::Boolean(_) => Value::Keyword("boolean"), + Value::True | Value::False => Value::Keyword("bool"), Value::Keyword(_) => Value::Keyword("keyword"), Value::Tuple(_) => Value::Keyword("tuple"), - Value::InternedString(_) => Value::Keyword("string"), - Value::AllocatedString(_) => Value::Keyword("string"), + Value::Interned(_) => Value::Keyword("string"), + Value::String(_) => Value::Keyword("string"), Value::List(_) => Value::Keyword("list"), Value::Dict(_) => Value::Keyword("dict"), Value::Fn(_) => Value::Keyword("fn"), - Value::Box(_, _) => Value::Keyword("box"), - Value::Placeholder => unreachable!("internal Ludus error"), - Value::Args(_) => unreachable!("internal Ludus error"), - Value::Base(_) => Value::Keyword("fn"), - Value::Recur(..) => unreachable!("internal Ludus error"), - Value::FnDecl(_) => Value::Keyword("fn"), + Value::Box(_) => Value::Keyword("box"), + Value::BaseFn(_) => Value::Keyword("fn"), + Value::Nothing => unreachable!(), } } -pub fn split<'src>(source: &Value<'src>, splitter: &Value) -> Value<'src> { +pub fn split(source: &Value, splitter: &Value) -> Value { match (source, splitter) { - (Value::AllocatedString(source), Value::AllocatedString(splitter)) => { + (Value::String(source), Value::String(splitter)) => { let parts = source.split_terminator(splitter.as_str()); let mut list = vector![]; for part in parts { - list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + list.push_back(Value::String(Rc::new(part.to_string()))); } - Value::List(list) + Value::List(Box::new(list)) } - (Value::AllocatedString(source), Value::InternedString(splitter)) => { + (Value::String(source), Value::Interned(splitter)) => { let parts = source.split_terminator(splitter); let mut list = vector![]; for part in parts { - list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + list.push_back(Value::String(Rc::new(part.to_string()))); } - Value::List(list) + Value::List(Box::new(list)) } - (Value::InternedString(source), Value::AllocatedString(splitter)) => { + (Value::Interned(source), Value::String(splitter)) => { let parts = source.split_terminator(splitter.as_str()); let mut list = vector![]; for part in parts { - list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + list.push_back(Value::String(Rc::new(part.to_string()))); } - Value::List(list) + Value::List(Box::new(list)) } - (Value::InternedString(source), Value::InternedString(splitter)) => { + (Value::Interned(source), Value::Interned(splitter)) => { let parts = source.split_terminator(splitter); let mut list = vector![]; for part in parts { - list.push_back(Value::AllocatedString(Rc::new(part.to_string()))); + list.push_back(Value::String(Rc::new(part.to_string()))); } - Value::List(list) + Value::List(Box::new(list)) } _ => unreachable!("internal Ludus error"), } } -pub fn upcase<'src>(string: &Value<'src>) -> Value<'src> { +pub fn upcase(string: &Value) -> Value { match string { - Value::AllocatedString(string) => Value::AllocatedString(Rc::new(string.to_uppercase())), - Value::InternedString(string) => Value::AllocatedString(Rc::new(string.to_uppercase())), + Value::String(string) => Value::String(Rc::new(string.to_uppercase())), + Value::Interned(string) => Value::String(Rc::new(string.to_uppercase())), _ => unreachable!("internal Ludus error"), } } -pub fn downcase<'src>(string: &Value<'src>) -> Value<'src> { +pub fn downcase(string: &Value) -> Value { match string { - Value::AllocatedString(string) => Value::AllocatedString(Rc::new(string.to_lowercase())), - Value::InternedString(string) => Value::AllocatedString(Rc::new(string.to_lowercase())), + Value::String(string) => Value::String(Rc::new(string.to_lowercase())), + Value::Interned(string) => Value::String(Rc::new(string.to_lowercase())), _ => unreachable!("internal Ludus error"), } } -pub fn trim<'src>(string: &Value<'src>) -> Value<'src> { +pub fn trim(string: &Value) -> Value { match string { - Value::AllocatedString(string) => { - Value::AllocatedString(Rc::new(string.trim().to_string())) - } - Value::InternedString(string) => Value::AllocatedString(Rc::new(string.trim().to_string())), + Value::String(string) => Value::String(Rc::new(string.trim().to_string())), + Value::Interned(string) => Value::String(Rc::new(string.trim().to_string())), _ => unreachable!("internal Ludus error"), } } -pub fn triml<'src>(string: &Value<'src>) -> Value<'src> { +pub fn triml(string: &Value) -> Value { match string { - Value::AllocatedString(string) => { - Value::AllocatedString(Rc::new(string.trim_start().to_string())) - } - Value::InternedString(string) => { - Value::AllocatedString(Rc::new(string.trim_start().to_string())) - } + Value::String(string) => Value::String(Rc::new(string.trim_start().to_string())), + Value::Interned(string) => Value::String(Rc::new(string.trim_start().to_string())), _ => unreachable!("internal Ludus error"), } } -pub fn trimr<'src>(string: &Value<'src>) -> Value<'src> { +pub fn trimr(string: &Value) -> Value { match string { - Value::AllocatedString(string) => { - Value::AllocatedString(Rc::new(string.trim_end().to_string())) - } - Value::InternedString(string) => { - Value::AllocatedString(Rc::new(string.trim_end().to_string())) - } + Value::String(string) => Value::String(Rc::new(string.trim_end().to_string())), + Value::Interned(string) => Value::String(Rc::new(string.trim_end().to_string())), _ => unreachable!("internal Ludus error"), } } -pub fn atan_2<'src>(x: &Value, y: &Value) -> Value<'src> { +pub fn atan_2(x: &Value, y: &Value) -> Value { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x.atan2(*y)), _ => unreachable!("internal Ludus error"), } } -pub fn ceil<'src>(x: &Value) -> Value<'src> { +pub fn ceil(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.ceil()), _ => unreachable!("internal Ludus error"), } } -pub fn cos<'src>(x: &Value) -> Value<'src> { +pub fn cos(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.cos()), _ => unreachable!("internal Ludus error"), } } -pub fn floor<'src>(x: &Value) -> Value<'src> { +pub fn floor(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.floor()), _ => unreachable!("internal Ludus error"), } } -pub fn random<'src>() -> Value<'src> { +pub fn random() -> Value { Value::Number(ran_f64()) } -pub fn round<'src>(x: &Value) -> Value<'src> { +pub fn round(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.round()), _ => unreachable!("internal Ludus error"), } } -pub fn sin<'src>(x: &Value) -> Value<'src> { +pub fn sin(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.sin()), _ => unreachable!("internal Ludus error"), } } -pub fn sqrt<'src>(x: &Value) -> Value<'src> { +pub fn sqrt(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.sqrt()), _ => unreachable!("internal Ludus error"), } } -pub fn tan<'src>(x: &Value) -> Value<'src> { +pub fn tan(x: &Value) -> Value { match x { Value::Number(x) => Value::Number(x.tan()), _ => unreachable!("internal Ludus error"), } } -pub fn gt<'src>(x: &Value, y: &Value) -> Value<'src> { +pub fn gt(x: &Value, y: &Value) -> Value { match (x, y) { - (Value::Number(x), Value::Number(y)) => Value::Boolean(x > y), + (Value::Number(x), Value::Number(y)) => { + if x > y { + Value::True + } else { + Value::False + } + } _ => unreachable!("internal Ludus error"), } } -pub fn gte<'src>(x: &Value, y: &Value) -> Value<'src> { +pub fn gte(x: &Value, y: &Value) -> Value { match (x, y) { - (Value::Number(x), Value::Number(y)) => Value::Boolean(x >= y), + (Value::Number(x), Value::Number(y)) => { + if x >= y { + Value::True + } else { + Value::False + } + } _ => unreachable!("internal Ludus error"), } } -pub fn lt<'src>(x: &Value, y: &Value) -> Value<'src> { +pub fn lt(x: &Value, y: &Value) -> Value { match (x, y) { - (Value::Number(x), Value::Number(y)) => Value::Boolean(x < y), + (Value::Number(x), Value::Number(y)) => { + if x < y { + Value::True + } else { + Value::False + } + } _ => unreachable!("internal Ludus error"), } } -pub fn lte<'src>(x: &Value, y: &Value) -> Value<'src> { +pub fn lte(x: &Value, y: &Value) -> Value { match (x, y) { - (Value::Number(x), Value::Number(y)) => Value::Boolean(x <= y), + (Value::Number(x), Value::Number(y)) => { + if x <= y { + Value::True + } else { + Value::False + } + } _ => unreachable!("internal Ludus error"), } } -pub fn r#mod<'src>(x: &Value, y: &Value) -> Value<'src> { +pub fn r#mod(x: &Value, y: &Value) -> Value { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x % y), _ => unreachable!("internal Ludus error"), } } -pub fn base<'src>() -> Vec<(String, Value<'src>)> { +pub fn base() -> Value { let members = vec![ - ("add", Value::Base(BaseFn::Binary(add))), - ("and", Value::Base(BaseFn::Binary(and))), - ("append", Value::Base(BaseFn::Binary(append))), - ("assoc", Value::Base(BaseFn::Ternary(assoc))), - ("at", Value::Base(BaseFn::Binary(at))), - ("atan_2", Value::Base(BaseFn::Binary(atan_2))), - ("bool", Value::Base(BaseFn::Unary(r#bool))), - ("ceil", Value::Base(BaseFn::Unary(ceil))), - ("chars", Value::Base(BaseFn::Unary(chars))), - ("concat", Value::Base(BaseFn::Binary(concat))), - ("cos", Value::Base(BaseFn::Unary(cos))), - ("count", Value::Base(BaseFn::Unary(count))), - ("dec", Value::Base(BaseFn::Unary(dec))), - ("dissoc", Value::Base(BaseFn::Binary(dissoc))), - ("div", Value::Base(BaseFn::Binary(div))), - ("doc!", Value::Base(BaseFn::Unary(doc))), - ("downcase", Value::Base(BaseFn::Unary(downcase))), - ("eq?", Value::Base(BaseFn::Binary(eq))), - ("first", Value::Base(BaseFn::Unary(first))), - ("floor", Value::Base(BaseFn::Unary(floor))), - ("get", Value::Base(BaseFn::Binary(get))), - ("gt?", Value::Base(BaseFn::Binary(gt))), - ("gte?", Value::Base(BaseFn::Binary(gte))), - ("inc", Value::Base(BaseFn::Unary(inc))), - ("last", Value::Base(BaseFn::Unary(last))), - ("list", Value::Base(BaseFn::Unary(list))), - ("lt?", Value::Base(BaseFn::Binary(lt))), - ("lte?", Value::Base(BaseFn::Binary(lte))), - ("mod", Value::Base(BaseFn::Binary(r#mod))), - ("mult", Value::Base(BaseFn::Binary(mult))), - ("number", Value::Base(BaseFn::Unary(number))), - ("or", Value::Base(BaseFn::Binary(or))), + ("add", Value::BaseFn(BaseFn::Binary(add))), + ("append", Value::BaseFn(BaseFn::Binary(append))), + ("assoc", Value::BaseFn(BaseFn::Ternary(assoc))), + ("at", Value::BaseFn(BaseFn::Binary(at))), + ("atan_2", Value::BaseFn(BaseFn::Binary(atan_2))), + ("bool", Value::BaseFn(BaseFn::Unary(r#bool))), + ("ceil", Value::BaseFn(BaseFn::Unary(ceil))), + ("chars", Value::BaseFn(BaseFn::Unary(chars))), + ("concat", Value::BaseFn(BaseFn::Binary(concat))), + ("cos", Value::BaseFn(BaseFn::Unary(cos))), + ("count", Value::BaseFn(BaseFn::Unary(count))), + ("dec", Value::BaseFn(BaseFn::Unary(dec))), + ("dissoc", Value::BaseFn(BaseFn::Binary(dissoc))), + ("div", Value::BaseFn(BaseFn::Binary(div))), + ("doc!", Value::BaseFn(BaseFn::Unary(doc))), + ("downcase", Value::BaseFn(BaseFn::Unary(downcase))), + ("eq?", Value::BaseFn(BaseFn::Binary(eq))), + ("first", Value::BaseFn(BaseFn::Unary(first))), + ("floor", Value::BaseFn(BaseFn::Unary(floor))), + ("get", Value::BaseFn(BaseFn::Binary(get))), + ("gt?", Value::BaseFn(BaseFn::Binary(gt))), + ("gte?", Value::BaseFn(BaseFn::Binary(gte))), + ("inc", Value::BaseFn(BaseFn::Unary(inc))), + ("last", Value::BaseFn(BaseFn::Unary(last))), + ("list", Value::BaseFn(BaseFn::Unary(list))), + ("lt?", Value::BaseFn(BaseFn::Binary(lt))), + ("lte?", Value::BaseFn(BaseFn::Binary(lte))), + ("mod", Value::BaseFn(BaseFn::Binary(r#mod))), + ("mult", Value::BaseFn(BaseFn::Binary(mult))), + ("number", Value::BaseFn(BaseFn::Unary(number))), ("pi", Value::Number(std::f64::consts::PI)), - ("print!", Value::Base(BaseFn::Unary(print))), - ("random", Value::Base(BaseFn::Nullary(random))), - ("range", Value::Base(BaseFn::Binary(range))), - ("rest", Value::Base(BaseFn::Unary(rest))), - ("round", Value::Base(BaseFn::Unary(round))), - ("show", Value::Base(BaseFn::Unary(show))), - ("sin", Value::Base(BaseFn::Unary(sin))), - ("slice", Value::Base(BaseFn::Ternary(slice))), - ("split", Value::Base(BaseFn::Binary(split))), - ("sqrt", Value::Base(BaseFn::Unary(sqrt))), + ("print!", Value::BaseFn(BaseFn::Unary(print))), + ("random", Value::BaseFn(BaseFn::Nullary(random))), + ("range", Value::BaseFn(BaseFn::Binary(range))), + ("rest", Value::BaseFn(BaseFn::Unary(rest))), + ("round", Value::BaseFn(BaseFn::Unary(round))), + ("show", Value::BaseFn(BaseFn::Unary(show))), + ("sin", Value::BaseFn(BaseFn::Unary(sin))), + ("slice", Value::BaseFn(BaseFn::Ternary(slice))), + ("split", Value::BaseFn(BaseFn::Binary(split))), + ("sqrt", Value::BaseFn(BaseFn::Unary(sqrt))), ("sqrt_2", Value::Number(std::f64::consts::SQRT_2)), - ("store!", Value::Base(BaseFn::Binary(store))), - ("sub", Value::Base(BaseFn::Binary(sub))), - ("tan", Value::Base(BaseFn::Unary(tan))), - ("trim", Value::Base(BaseFn::Unary(trim))), - ("triml", Value::Base(BaseFn::Unary(triml))), - ("trimr", Value::Base(BaseFn::Unary(trimr))), - ("type", Value::Base(BaseFn::Unary(r#type))), - ("unbox", Value::Base(BaseFn::Unary(unbox))), - ("upcase", Value::Base(BaseFn::Unary(upcase))), + ("store!", Value::BaseFn(BaseFn::Binary(store))), + ("sub", Value::BaseFn(BaseFn::Binary(sub))), + ("tan", Value::BaseFn(BaseFn::Unary(tan))), + ("trim", Value::BaseFn(BaseFn::Unary(trim))), + ("triml", Value::BaseFn(BaseFn::Unary(triml))), + ("trimr", Value::BaseFn(BaseFn::Unary(trimr))), + ("type", Value::BaseFn(BaseFn::Unary(r#type))), + ("unbox", Value::BaseFn(BaseFn::Unary(unbox))), + ("upcase", Value::BaseFn(BaseFn::Unary(upcase))), ]; - let pkg = Value::Dict(HashMap::from(members)); - vec![("base".to_string(), pkg)] + Value::Dict(Box::new(HashMap::from(members))) } diff --git a/src/compiler.rs b/src/compiler.rs index 0c83131..7a31948 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1039,23 +1039,6 @@ impl Compiler { compiler.scope_depth -= 1; } - // let mut the_chunks = vec![]; - - // for (arity, chunks) in chunks.iter() { - // let mut jump_len: u8 = 0; - // for chunk in chunks.iter().rev() { - // let chunk_len = chunk.bytecode.len(); - // chunk.bytecode[chunk_len - 1] = jump_len; - // jump_len += chunk_len as u8; - // } - // let the_chunk = chunks.into_iter().fold(vec![], |chunks, chunk| { - // chunks.append(); - // chunks - // }); - // the_chunks.push((arity, the_chunk)) - // } - // - // let mut the_chunks = vec![]; for (arity, mut compiler) in compilers.into_iter() { @@ -1080,6 +1063,7 @@ impl Compiler { let init_val = Value::Fn(Rc::new(cell)); self.emit_constant(init_val); self.bind(name); + // TODO: close over everything accessed in the function // TODO: pull the function off the stack, and set the OnceCell. diff --git a/src/main.rs b/src/main.rs index 59e3c08..7e9bde6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::env; const DEBUG_COMPILE: bool = true; const DEBUG_RUN: bool = true; -// mod base; +mod base; mod spans; use crate::spans::Spanned; @@ -82,10 +82,10 @@ fn foo () -> { (x, y) } -let x = foo () -let y = foo () +let a = foo () +let b = foo () -(x, y) +(a, b) "; run(src); } diff --git a/src/value.rs b/src/value.rs index 0a0258a..10bcdb2 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,3 +1,4 @@ +use crate::base::BaseFn; use crate::compiler::Chunk; use crate::parser::Ast; use crate::spans::Spanned; @@ -23,7 +24,7 @@ impl LFn { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum Value { Nothing, Nil, @@ -38,6 +39,28 @@ pub enum Value { Dict(Box>), Box(Rc>), Fn(Rc>), + BaseFn(BaseFn), +} + +impl PartialEq for Value { + fn eq(&self, other: &Value) -> bool { + use Value::*; + match (self, other) { + (Nothing, Nothing) | (Nil, Nil) | (True, True) | (False, False) => true, + (Keyword(str1), Keyword(str2)) | (Interned(str1), Interned(str2)) => str1 == str2, + (String(x), String(y)) => x == y, + (String(x), Interned(y)) => x.as_ref() == y, + (Interned(x), String(y)) => x == y.as_ref(), + (Number(x), Number(y)) => x == y, + (Tuple(x), Tuple(y)) => x == y, + (List(x), List(y)) => x == y, + (Dict(x), Dict(y)) => x == y, + (Box(x), Box(y)) => std::ptr::eq(x.as_ref().as_ptr(), y.as_ref().as_ptr()), + (Fn(x), Fn(y)) => x == y, + (BaseFn(x), BaseFn(y)) => std::ptr::eq(x, y), + _ => false, + } + } } impl std::fmt::Display for Value { @@ -81,6 +104,7 @@ impl std::fmt::Display for Value { ), Box(value) => write!(f, "box {}", value.as_ref().borrow()), Fn(lfn) => write!(f, "fn {}", lfn.get().unwrap().name), + BaseFn(_) => write!(f, "base fn"), } } } @@ -121,7 +145,8 @@ impl Value { } Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()), Fn(lfn) => format!("fn {}", lfn.get().unwrap().name), - _ => todo!(), + BaseFn(_) => "base fn".to_string(), + Nothing => unreachable!(), } } @@ -185,6 +210,7 @@ impl Value { Dict(..) => "dict", Box(..) => "box", Fn(..) => "fn", + BaseFn(..) => "fn", } } } diff --git a/src/vm.rs b/src/vm.rs index 688c61d..8eccd98 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,3 +1,4 @@ +use crate::base::BaseFn; use crate::compiler::{Chunk, Op}; use crate::parser::Ast; use crate::spans::Spanned; @@ -83,7 +84,7 @@ pub struct Vm { pub call_stack: Vec, pub frame: CallFrame, pub ip: usize, - pub return_register: [Value; 7], + pub return_register: [Value; 8], pub matches: bool, pub match_depth: u8, pub result: Option>, @@ -111,7 +112,7 @@ impl Vm { call_stack: Vec::with_capacity(64), frame: base_frame, ip: 0, - return_register: [const { Value::Nothing }; 7], + return_register: [const { Value::Nothing }; 8], matches: false, match_depth: 0, result: None, @@ -662,25 +663,37 @@ impl Vm { self.ip += 2; let val = self.pop(); - let Value::Fn(_) = val else { - return self.panic_with(format!("{} is not a function", val.show())); - }; - let mut frame = CallFrame { - function: val, - arity, - stack_base: self.stack.len() - arity as usize, - ip: 0, - }; + match val { + Value::Fn(_) => { + let mut frame = CallFrame { + function: val, + arity, + stack_base: self.stack.len() - arity as usize, + ip: 0, + }; - swap(&mut self.frame, &mut frame); - frame.ip = self.ip; + swap(&mut self.frame, &mut frame); + frame.ip = self.ip; - self.call_stack.push(frame); - self.ip = 0; + self.call_stack.push(frame); + self.ip = 0; - if crate::DEBUG_RUN { - println!("== calling into {} ==", self.frame.function.show()); + if crate::DEBUG_RUN { + println!("== calling into {} ==", self.frame.function.show()); + } + } + Value::BaseFn(base_fn) => { + let value = match (arity, base_fn) { + (0, BaseFn::Nullary(f)) => f(), + (1, BaseFn::Unary(f)) => f(&self.pop()), + (2, BaseFn::Binary(f)) => f(&self.pop(), &self.pop()), + (3, BaseFn::Ternary(f)) => f(&self.pop(), &self.pop(), &self.pop()), + _ => return self.panic("internal ludus error"), + }; + self.push(value); + } + _ => return self.panic_with(format!("{} is not a function", val.show())), } } Return => {