Skip to content

Commit

Permalink
Merge pull request #207 from calcit-lang/flexible-tuple
Browse files Browse the repository at this point in the history
Flexible tuple
  • Loading branch information
NoEgAm committed May 23, 2023
2 parents fb8088f + 77a1ecb commit a24dfcb
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 51 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "calcit"
version = "0.6.27"
version = "0.6.28"
authors = ["jiyinyiyong <[email protected]>"]
edition = "2021"
license = "MIT"
Expand All @@ -20,7 +20,7 @@ exclude = [
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cirru_edn = "0.2.21"
cirru_edn = "0.3.0"
# cirru_edn = { path = "/Users/chenyong/repo/cirru/edn.rs" }
cirru_parser = "0.1.24"
clap = "3.2.25"
Expand All @@ -37,7 +37,7 @@ colored = "2"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
libloading = "0.8.0"
ctrlc = "3.2.5"
ctrlc = "3.2.0"

[lib]
name = "calcit"
Expand Down
10 changes: 10 additions & 0 deletions calcit/test-cond.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@
match-ab (' :c 1 2)
[] "|no match"

assert=
match-ab (:: :a 1 2)
[] "|pattern a:" 1
assert=
match-ab (:: :b 1 2)
[] "|pattern b:" 1 2
assert=
match-ab (:: :c 1 2)
[] "|no match"

|test-tag-match $ quote
fn ()
log-title "|Testing tag-match"
Expand Down
7 changes: 7 additions & 0 deletions calcit/test.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,20 @@
assert= :tuple (type-of (:: :a :b))
assert= :a (nth (:: :a :b) 0)
assert= :b (nth (:: :a :b) 1)
assert= :c (nth (:: :a :b :c) 2)

assert= 2 (count (:: :a :b))
assert= 3 (count (:: :a :b :c))
assert= 4 (count (:: :a :b :c :d))

assert= :a (get (:: :a :b) 0)
assert= :b (get (:: :a :b) 1)

assert= (:: 1 0) $ update (:: 0 0) 0 inc
assert= (:: 0 1) $ update (:: 0 0) 1 inc
assert= (:: 1 0 0) $ update (:: 0 0 0) 0 inc
assert= (:: 0 1 0) $ update (:: 0 0 0) 1 inc
assert= (:: 0 0 1) $ update (:: 0 0 0) 2 inc

|test-effect $ quote
fn ()
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "@calcit/procs",
"version": "0.6.27",
"version": "0.6.28",
"main": "./lib/calcit.procs.mjs",
"devDependencies": {
"@types/node": "^18.16.3",
"@types/node": "^20.2.3",
"typescript": "^5.0.4"
},
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ fn handle_proc_internal(name: CalcitProc, args: &CalcitItems, call_stack: &CallS
CalcitProc::NativeTuple => meta::new_tuple(args), // unstable solution for the name
CalcitProc::NativeTupleNth => meta::tuple_nth(args),
CalcitProc::NativeTupleAssoc => meta::assoc(args),
CalcitProc::NativeTupleCount => meta::tuple_count(args),
// effects
CalcitProc::NativeDisplayStack => meta::display_stack(args, call_stack),
CalcitProc::Raise => effects::raise(args),
Expand Down
8 changes: 4 additions & 4 deletions src/builtins/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ pub fn foldl_shortcut(xs: &CalcitItems, call_stack: &CallStackList) -> Result<Ca
let values = TernaryTreeList::from(&[state, x.to_owned()]);
let pair = runner::run_fn(&values, scope, args, body, def_ns.to_owned(), call_stack)?;
match pair {
Calcit::Tuple(x0, x1) => match &*x0 {
Calcit::Tuple(x0, x1, _extra) => match &*x0 {
Calcit::Bool(b) => {
if *b {
return Ok((*x1).to_owned());
Expand Down Expand Up @@ -319,7 +319,7 @@ pub fn foldl_shortcut(xs: &CalcitItems, call_stack: &CallStackList) -> Result<Ca
let values = TernaryTreeList::from(&[state, x.to_owned()]);
let pair = runner::run_fn(&values, scope, args, body, def_ns.to_owned(), call_stack)?;
match pair {
Calcit::Tuple(x0, x1) => match &*x0 {
Calcit::Tuple(x0, x1, _extra) => match &*x0 {
Calcit::Bool(b) => {
if *b {
return Ok((*x1).to_owned());
Expand Down Expand Up @@ -357,7 +357,7 @@ pub fn foldl_shortcut(xs: &CalcitItems, call_stack: &CallStackList) -> Result<Ca
let values = TernaryTreeList::from(&[state, Calcit::List(TernaryTreeList::from(&[k.to_owned(), x.to_owned()]))]);
let pair = runner::run_fn(&values, scope, args, body, def_ns.to_owned(), call_stack)?;
match pair {
Calcit::Tuple(x0, x1) => match &*x0 {
Calcit::Tuple(x0, x1, _extra) => match &*x0 {
Calcit::Bool(b) => {
if *b {
return Ok((*x1).to_owned());
Expand Down Expand Up @@ -419,7 +419,7 @@ pub fn foldr_shortcut(xs: &CalcitItems, call_stack: &CallStackList) -> Result<Ca
let values = TernaryTreeList::from(&[state, x]);
let pair = runner::run_fn(&values, scope, args, body, def_ns.to_owned(), call_stack)?;
match pair {
Calcit::Tuple(x0, x1) => match &*x0 {
Calcit::Tuple(x0, x1, _extra) => match &*x0 {
Calcit::Bool(b) => {
if *b {
return Ok((*x1).to_owned());
Expand Down
48 changes: 39 additions & 9 deletions src/builtins/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,19 @@ pub fn turn_keyword(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
}

pub fn new_tuple(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
if xs.len() != 2 {
CalcitErr::err_str(format!("tuple expected 2 arguments, got {}", CrListWrap(xs.to_owned())))
if xs.len() < 2 {
CalcitErr::err_str(format!("tuple expected at least 2 arguments, got {}", CrListWrap(xs.to_owned())))
} else {
Ok(Calcit::Tuple(Arc::new(xs[0].to_owned()), Arc::new(xs[1].to_owned())))
let extra: Vec<Calcit> = if xs.len() == 2 {
vec![]
} else {
let mut ys: Vec<Calcit> = Vec::with_capacity(xs.len() - 2);
for i in 2..xs.len() {
ys.push(xs[i].to_owned());
}
ys
};
Ok(Calcit::Tuple(Arc::new(xs[0].to_owned()), Arc::new(xs[1].to_owned()), extra))
}
}

Expand All @@ -277,7 +286,7 @@ pub fn invoke_method(name: &str, invoke_args: &CalcitItems, call_stack: &CallSta
let value = invoke_args[0].to_owned();
let s0 = CalcitScope::default();
let class = match &invoke_args[0] {
Calcit::Tuple(a, _b) => (**a).to_owned(),
Calcit::Tuple(a, _b, _extra) => (**a).to_owned(),
// classed should already be preprocessed
Calcit::List(..) => runner::evaluate_symbol("&core-list-class", &s0, primes::CORE_NS, None, call_stack)?,
Calcit::Map(..) => runner::evaluate_symbol("&core-map-class", &s0, primes::CORE_NS, None, call_stack)?,
Expand Down Expand Up @@ -351,10 +360,17 @@ pub fn tuple_nth(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
return CalcitErr::err_str(format!("&tuple:nth expected 2 argument, got: {}", CrListWrap(xs.to_owned())));
}
match (&xs[0], &xs[1]) {
(Calcit::Tuple(a, b), Calcit::Number(n)) => match f64_to_usize(*n) {
(Calcit::Tuple(a, b, extra), Calcit::Number(n)) => match f64_to_usize(*n) {
Ok(0) => Ok((**a).to_owned()),
Ok(1) => Ok((**b).to_owned()),
Ok(m) => CalcitErr::err_str(format!("Tuple only got 2 elements, trying to index with {m}")),
Ok(m) => {
if m - 2 < extra.len() {
Ok(extra[m - 2].to_owned())
} else {
let size = extra.len() + 2;
CalcitErr::err_str(format!("Tuple only got {size} elements, trying to index with {m}"))
}
}
Err(e) => CalcitErr::err_str(format!("&tuple:nth expect usize, {e}")),
},
(a, b) => CalcitErr::err_str(format!("&tuple:nth expected a tuple and an index, got: {a} {b}")),
Expand All @@ -366,12 +382,16 @@ pub fn assoc(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
return CalcitErr::err_str(format!("tuple:assoc expected 3 arguments, got: {xs:?}"));
}
match (&xs[0], &xs[1]) {
(Calcit::Tuple(a0, a1), Calcit::Number(n)) => match f64_to_usize(*n) {
(Calcit::Tuple(a0, a1, extra), Calcit::Number(n)) => match f64_to_usize(*n) {
Ok(idx) => {
if idx == 0 {
Ok(Calcit::Tuple(Arc::new(xs[2].to_owned()), a1.to_owned()))
Ok(Calcit::Tuple(Arc::new(xs[2].to_owned()), a1.to_owned(), extra.to_owned()))
} else if idx == 1 {
Ok(Calcit::Tuple(a0.to_owned(), Arc::new(xs[2].to_owned())))
Ok(Calcit::Tuple(a0.to_owned(), Arc::new(xs[2].to_owned()), extra.to_owned()))
} else if idx - 2 < extra.len() {
let mut new_extra = extra.to_owned();
new_extra[idx - 2] = xs[2].to_owned();
Ok(Calcit::Tuple(a0.to_owned(), a1.to_owned(), new_extra))
} else {
CalcitErr::err_str(format!("Tuple only has fields of 0,1 , unknown index: {idx}"))
}
Expand All @@ -382,6 +402,16 @@ pub fn assoc(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
}
}

pub fn tuple_count(xs: &CalcitItems) -> Result<Calcit, CalcitErr> {
if xs.len() != 1 {
return CalcitErr::err_str(format!("tuple:count expected 1 argument, got: {xs:?}"));
}
match &xs[0] {
Calcit::Tuple(_, _, extra) => Ok(Calcit::Number((extra.len() + 2) as f64)),
x => CalcitErr::err_str(format!("&tuple:count expected a tuple, got: {x}")),
}
}

pub fn no_op() -> Result<Calcit, CalcitErr> {
Ok(Calcit::Nil)
}
Expand Down
11 changes: 5 additions & 6 deletions src/cirru/calcit-core.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,11 @@
&let
k (&list:first pattern)
quasiquote
if (&= (&list:first ~value) ~k)
if (&= (nth ~value 0) ~k)
let
~ $ map-indexed (&list:rest pattern) $ defn %key-match (idx x)
[] x $ quasiquote
&list:nth ~value (~ (inc idx))
nth ~value (~ (inc idx))
, ~branch
&key-match-internal ~value $ ~@ (&list:rest body)
if (&= pattern '_) branch
Expand Down Expand Up @@ -741,9 +741,7 @@
assoc x k $ f (&list:nth x k)
, x
if (tuple? x)
if (or (&= k 0) (&= k 1))
assoc x k $ f (&tuple:nth x k)
raise $ &str:concat "|tuple only has 0,1 fields, unknown field: " k
assoc x k $ f (&tuple:nth x k)
if (record? x)
if (contains? x k)
assoc x k $ f (&record:get x k)
Expand Down Expand Up @@ -1673,7 +1671,8 @@
|count $ quote
defn count (x)
if (nil? x) 0
if (tuple? x) 2
if (tuple? x)
&tuple:count x
if (list? x)
&list:count x
.count x
Expand Down
16 changes: 13 additions & 3 deletions src/data/edn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn calcit_to_edn(x: &Calcit) -> Result<Edn, String> {
}
Calcit::Proc(name) => Ok(Edn::Symbol(name.to_string().into())),
Calcit::Syntax(name, _ns) => Ok(Edn::sym(name.to_string())),
Calcit::Tuple(tag, data) => {
Calcit::Tuple(tag, data, extra) => {
match &**tag {
Calcit::Symbol { sym, .. } => {
if &**sym == "quote" {
Expand All @@ -65,7 +65,13 @@ pub fn calcit_to_edn(x: &Calcit) -> Result<Edn, String> {
Err(format!("unknown tag for EDN: {sym}")) // TODO more types to handle
}
}
Calcit::Record(name, _, _) => Ok(Edn::tuple(Edn::Keyword(name.to_owned()), calcit_to_edn(data)?)),
Calcit::Record(name, _, _) => {
let mut extra_values = vec![];
for item in extra {
extra_values.push(calcit_to_edn(item)?);
}
Ok(Edn::tuple(Edn::Keyword(name.to_owned()), calcit_to_edn(data)?, extra_values))
}
v => {
Err(format!("EDN tuple expected 'quote or record, unknown tag: {v}"))
// TODO more types to handle
Expand Down Expand Up @@ -98,7 +104,11 @@ pub fn edn_to_calcit(x: &Edn) -> Calcit {
Edn::Keyword(s) => Calcit::Keyword(s.to_owned()),
Edn::Str(s) => Calcit::Str((**s).into()),
Edn::Quote(nodes) => Calcit::CirruQuote(nodes.to_owned()),
Edn::Tuple(pair) => Calcit::Tuple(Arc::new(edn_to_calcit(&pair.0)), Arc::new(edn_to_calcit(&pair.1))),
Edn::Tuple(pair, extra) => Calcit::Tuple(
Arc::new(edn_to_calcit(&pair.0)),
Arc::new(edn_to_calcit(&pair.1)),
extra.into_iter().map(edn_to_calcit).collect(),

Check warning on line 110 in src/data/edn.rs

View workflow job for this annotation

GitHub Actions / clippy

this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`

warning: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> src/data/edn.rs:110:13 | 110 | extra.into_iter().map(edn_to_calcit).collect(), | ^^^^^^^^^ help: call directly: `iter` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref = note: `#[warn(clippy::into_iter_on_ref)]` on by default
),
Edn::List(xs) => {
let mut ys: primes::CalcitItems = TernaryTreeList::Empty;
for x in xs {
Expand Down
29 changes: 21 additions & 8 deletions src/primes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub enum Calcit {
/// atom, holding a path to its state, data inside remains during hot code swapping
Ref(Arc<str>, Arc<Mutex<ValueAndListeners>>),
/// more tagged union type, more like an internal structure
Tuple(Arc<Calcit>, Arc<Calcit>),
Tuple(Arc<Calcit>, Arc<Calcit>, Vec<Calcit>),
/// binary data, to be used by FFIs
Buffer(Vec<u8>),
/// cirru quoted data, for faster meta programming
Expand Down Expand Up @@ -184,7 +184,16 @@ impl fmt::Display for Calcit {
},
Calcit::CirruQuote(code) => f.write_str(&format!("(&cirru-quote {code})")),
Calcit::Ref(name, _locked_pair) => f.write_str(&format!("(&ref {name} ...)")),
Calcit::Tuple(a, b) => f.write_str(&format!("(:: {a} {b})")),
Calcit::Tuple(a, b, extra) => {
let mut extra_str = String::from("");
for item in extra {
if !extra_str.is_empty() {
extra_str.push(' ');
}
extra_str.push_str(&item.to_string())
}
f.write_str(&format!("(:: {a} {b} {extra_str})"))
}
Calcit::Buffer(buf) => {
f.write_str("(&buffer")?;
if buf.len() > 8 {
Expand Down Expand Up @@ -374,10 +383,11 @@ impl Hash for Calcit {
"ref:".hash(_state);
name.hash(_state);
}
Calcit::Tuple(a, b) => {
Calcit::Tuple(a, b, extra) => {
"tuple:".hash(_state);
a.hash(_state);
b.hash(_state);
extra.hash(_state);
}
Calcit::Buffer(buf) => {
"buffer:".hash(_state);
Expand Down Expand Up @@ -498,12 +508,15 @@ impl Ord for Calcit {
(Calcit::Ref(_, _), _) => Less,
(_, Calcit::Ref(_, _)) => Greater,

(Calcit::Tuple(a0, b0), Calcit::Tuple(a1, b1)) => match a0.cmp(a1) {
Equal => b0.cmp(b1),
(Calcit::Tuple(a0, b0, extra0), Calcit::Tuple(a1, b1, extra1)) => match a0.cmp(a1) {
Equal => match b0.cmp(b1) {
Equal => extra0.cmp(extra1),
v => v,
},
v => v,
},
(Calcit::Tuple(_, _), _) => Less,
(_, Calcit::Tuple(_, _)) => Greater,
(Calcit::Tuple(_, _, _), _) => Less,
(_, Calcit::Tuple(_, _, _)) => Greater,

(Calcit::Buffer(buf1), Calcit::Buffer(buf2)) => buf1.cmp(buf2),
(Calcit::Buffer(..), _) => Less,
Expand Down Expand Up @@ -590,7 +603,7 @@ impl PartialEq for Calcit {
(Calcit::Str(a), Calcit::Str(b)) => a == b,
(Calcit::Thunk(a, _), Calcit::Thunk(b, _)) => a == b,
(Calcit::Ref(a, _), Calcit::Ref(b, _)) => a == b,
(Calcit::Tuple(a, b), Calcit::Tuple(c, d)) => a == c && b == d,
(Calcit::Tuple(a, b, extra_a), Calcit::Tuple(c, d, extra_c)) => a == c && b == d && extra_a == extra_c,
(Calcit::Buffer(b), Calcit::Buffer(d)) => b == d,
(Calcit::CirruQuote(b), Calcit::CirruQuote(d)) => b == d,
(Calcit::List(a), Calcit::List(b)) => a == b,
Expand Down
3 changes: 3 additions & 0 deletions src/primes/proc_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum CalcitProc {
NativeTuple,
NativeTupleNth,
NativeTupleAssoc,
NativeTupleCount,
NativeDisplayStack,
Raise,
Quit,
Expand Down Expand Up @@ -200,6 +201,7 @@ impl FromStr for CalcitProc {
"::" => Ok(Self::NativeTuple),
"&tuple:nth" => Ok(Self::NativeTupleNth),
"&tuple:assoc" => Ok(Self::NativeTupleAssoc),
"&tuple:count" => Ok(Self::NativeTupleCount),
// effects
"&display-stack" => Ok(Self::NativeDisplayStack),
"raise" => Ok(Self::Raise),
Expand Down Expand Up @@ -378,6 +380,7 @@ impl Display for CalcitProc {
Self::NativeTuple => write!(f, "::"),
Self::NativeTupleNth => write!(f, "&tuple:nth"),
Self::NativeTupleAssoc => write!(f, "&tuple:assoc"),
Self::NativeTupleCount => write!(f, "&tuple:count"),
Self::NativeDisplayStack => write!(f, "&display-stack"),
Self::Raise => write!(f, "raise"),
Self::Quit => write!(f, "quit!"),
Expand Down
Loading

0 comments on commit a24dfcb

Please sign in to comment.