diff --git a/README.md b/README.md
index e417e1b..f24efcb 100644
--- a/README.md
+++ b/README.md
@@ -17,8 +17,7 @@ the jsoftware wiki includes the following story concerning the original Incunabu
accordingly, j does not intend to be a fully-fledged implementation of an array language.
j values may be integers, arrays, or 2-dimensional matrices; higher-rank matrices are not
-implemented. J's adverbs are not implemented. a small number of monadic and dyadic verbs are
-provided. variables may be defined.
+implemented. a small number of verbs and adverbs are provided. variables may be defined.
**monadic verbs**
* `i.` "idot" generates a value
@@ -34,6 +33,14 @@ provided. variables may be defined.
* `[` "left" returns the left value
* `]` "right" returns the right value
+**monadic adverbs**
+* `/` "insert" places a dyadic verb between items of its argument
+* `\` "prefix" returns successive prefixes of its argument
+
+**dyadic adverbs**
+* `/` "table" returns a table of entries using a dyadic verb and two arguments
+* `\` "infix" applies a verb to successive parts of its right-hand argument
+
variables are assigned using `=:`. variable names may only contain lowercase ASCII `a-z`
characters, numeric `0-9` characters, and `_` underscores. variable names must begin with a
lowercase ASCII `a-z` character.
@@ -332,12 +339,17 @@ taken together, the expression `1+i.4` adds `1` to each element of the array `0
`1 2 3 4`.
`/` is a kind of "adverb." in traditional human languages, an adverb is a part of speech used to
-describe how a verb is performed. this same concept holds roughly true for J and K's adverbs.
+apply an adjective to a verb. or in other words, it describes how a verb is/was performed. this
+same concept holds roughly true for J and K's adverbs.
-we won't comprehensively explain adverbs here. in short, `+ /` applies the dyadic `+` operator
-"over" its argument(s).
+adverbs and [gerunds][j-gerunds] are very similar to higher-order functions. a higher-order
+function is a function that either accepts as an argument, or returns, another function. these
+constructs provide a way for programmers to abbreviate or abstract over common control flow
+patterns. J refers to the `/` adverb in this statement as "insert". this operator places the
+dyadic `+` operator between the elements of its argument. thus, `+ / 1 2 3` is equivalent to
+`1+2+3`.
-so, the expression above has the following structure, expressed as an ascii diagram.
+so, the expression above has the following structure:
```
+ / 1 + ! 100
@@ -350,7 +362,7 @@ so, the expression above has the following structure, expressed as an ascii diag
┝┳┥
┗━━━━━━━━━━━━━━━ find the sum of the given argument
┝━━━┳━━━━━━━┥
- ┗━━━━━━━━━━━━ sum "over" the sequence of numbers from 1 through 100
+ ┗━━━━━━━━━━━━ add each of the numbers from 1 through 100
```
these programs share the same structure, save that `i.` is the verb for generating sequences,
@@ -693,4 +705,4 @@ todo...
[incunabulum]: https://code.jsoftware.com/wiki/Essays/Incunabulum
[why-k]: https://xpqz.github.io/kbook/Introduction.html#why-k
[api-guidelines]: https://rust-lang.github.io/api-guidelines/future-proofing.html
-
+[j-gerunds]: https://code.jsoftware.com/wiki/Vocabulary/GerundsAndAtomicRepresentation
diff --git a/src/a.rs b/src/a.rs
index a9aa2b9..14b3029 100644
--- a/src/a.rs
+++ b/src/a.rs
@@ -140,11 +140,16 @@ use super::*; use std::marker::PhantomData as PD;
let(f)=|i_o,j_o|{i.get(j_o,i_o)};A::new(m_o,n_o)?.init_with(f)}
}
-/**dyadic verbs*/impl A{
- pub fn d_left (self,r:A)->R{Ok(self)}
- pub fn d_right(self,r:A)->R{Ok(r) }
- pub fn d_plus(self,r:A)->R{A::d_do(self,r,|x,y|x+y)}
- pub fn d_mul (self,r:A)->R{A::d_do(self,r,|x,y|x*y)}
+/**dyadic verbs*/impl D{
+ /*return dyad function**/ pub fn f(&self)->fn(I,I)->I{use D::*;
+ match(self){Plus=>D::add, Mul=>D::mul, Left=>D::left, Right=>D::right} }
+ /*add two numbers*/fn add (x:I,y:I)->I{x+y} /*multiply two numbers*/fn mul (x:I,y:I)->I{x*y}
+ /*left */fn left(x:I,y:I)->I{x } /*right */fn right(x:I,y:I)->I{ y}
+} impl A{
+ pub fn d_left (self,r:A)->R{Ok(self) }
+ pub fn d_right(self,r:A)->R{Ok(r) }
+ pub fn d_plus(self,r:A) ->R{A::d_do(self,r,D::add)}
+ pub fn d_mul (self,r:A) ->R{A::d_do(self,r,D::mul)}
pub fn d_do(l@A{m:ml,n:nl,..}:A,r@A{m:mr,n:nr,..}:A,f:impl Fn(I,I)->I)->R>{
let(li,ri)=(l.as_i().ok(),r.as_i().ok());let(ls,rs)=(l.as_slice().ok(),r.as_slice().ok());
if let(Some(li),Some(ri))=(li,ri){r!(A::from_i(f(li,ri)))} // two scalars
@@ -160,6 +165,47 @@ use super::*; use std::marker::PhantomData as PD;
}
}
+/**monadic adverbs*/mod adverbs_m{use super::*;
+ impl Ym{
+ /// using this adverb, apply the given dyadic verb to the provided operand.
+ pub fn apply(&self,d:D,a:A)->R{use Ym::*;match(self){Insert=>Ym::insert(d,a),Prefix=>Ym::prefix(d,a)}}
+ fn insert(d:D,a:A)->R{let mut v=a.vals();let(i)=v.next().ok_or(err!("empty"))?;v.fold(i,d.f()).try_into()}
+ fn prefix(d:D,a:A)->R{
+ let d_=|i|{use{D::*};match(d){Mul=>1,_=>i}};
+ if let Ok(i)=a.as_i() {A::from_i(d_(i))}
+ else if let Ok(s)=a.as_slice(){let (m,n)=(s.len(),s.len());
+ let p=|i,j|if(j>i){Ok(0)}else{a.get(1,j).map(d_)};
+ A::new(m,n)?.init_with(p)}
+ else { bail!("monadic `\\` is not implemented for matrices") }}
+ }
+ // === monadic `/`, `Ym::Insert` tests
+ macro_rules! test_insert{($f:ident,$d:expr,$a:expr,$o:expr)=>
+ {#[test]fn $f()->R<()>{let(a):R={$a};let(d):D={$d}; // typecheck macro arguments.
+ let i:I=a.and_then(|a:A|Ym::insert($d,a)).and_then(|a|a.as_i())?;
+ eq!(i,$o);ok!()}}}
+ test_insert!(add_a_scalar, D::Plus, A::from_i(42), 42 );
+ test_insert!(add_a_sequence, D::Plus, >::try_from(&[1,2,3,4,5]), (1+2+3+4+5) );
+ test_insert!(mul_a_sequence, D::Mul , >::try_from(&[1,2,3,4,5]), (1*2*3*4*5) );
+}
+
+/**dyadic adverbs*/mod adverbs_d{use super::*;
+ impl Yd{
+ /// using this adverb, apply the given dyadic verb to the provided operand.
+ pub fn apply(&self,d:D,l:A,r:A)->R{use Yd::*;match(self){Table=>Yd::table(d,l,r),Infix=>Yd::infix(d,l,r)}}
+ fn table(d:D,l:A,r:A)->R{let (l_i,r_i)=(l.as_i() ,r.as_i());
+ let (l_s,r_s)=(l.as_slice(),r.as_slice());
+ if let (Ok(l),Ok(r))=(l_i,r_i){let(i)=d.f()(l,r);A::from_i(i)}
+ else if let (Ok(l),Ok(r))=(l_s,r_s){let(m,n)=(l.len(),r.len());let(d)=d.f();
+ let f=|i,j|->R{let(x,y)=(l[i-1],r[j-1]);Ok(d(x,y))};
+ A::new(m,n)?.init_with(f)}
+ else {bail!("unexpected fallthrough in Yd::table")}}
+ fn infix(d:D,l:A,r:A)->R{let(s)=r.as_slice().map_err(|_|err!("infix rhs must be a slice"))?;
+ let(il)=l.as_i() .map_err(|_|err!("infix lhs must be a scalar"))?.try_into()?;
+ let(ic)=(s.len()-il)+1;
+ A::new(ic,il)?.init_with(|i,j|Ok(s[(i-1)+(j-1)]))}
+ }
+}
+
/**deep-copy*/impl A{
pub fn deep_copy(&self)->R{let A{m,n,l:li,d:di,i:_}=*self;A::new(m,n)?.init_with(|i,j|{self.get(i,j)})}
}
diff --git a/src/j.rs b/src/j.rs
index 60ecbe9..4d09ead 100644
--- a/src/j.rs
+++ b/src/j.rs
@@ -13,6 +13,8 @@ fn eval_(ast:B,st:&mut ST)->R>{use{M::*,D::*};
Tally=>a.m_tally(),Transpose=>a.m_trans()}}
N::D{d,l,r}=>{let(l,r)=(rec(l)?,rec(r)?);match d{Plus=>l.d_plus(r), Mul=>l.d_mul(r),
Left=>l.d_left(r), Right=>l.d_right(r)}}
+ N::Ym{ym,d,o}=>{rec(o).and_then(|a|ym.apply(d,a))}
+ N::Yd{yd,d,l,r}=>{let(l,r)=(rec(l)?,rec(r)?);yd.apply(d,l,r)}
N::S{sy} =>{st.get(&sy).ok_or(err!("undefined symbol: {sy:?}"))?;todo!("symbol value clone")}
- N::V{sy,e} =>{let(a)=rec(e)?;st.insert(sy,a);r!(Ok(None))}
+ N::E{sy,e} =>{let(a)=rec(e)?;st.insert(sy,a);r!(Ok(None))}
}.map(O::Some)}
diff --git a/src/r.rs b/src/r.rs
index 35ae338..875338b 100644
--- a/src/r.rs
+++ b/src/r.rs
@@ -1,12 +1,15 @@
-/**input lexing*/pub(crate) use lex::lex;mod lex{use crate::*;
+/**input lexing*/ pub(crate) use self::{lex::lex,parse::{D,M,N,Yd,Ym,parse}};
+mod lex{use crate::*;
/**syntax token*/ #[derive(CL,DBG,PE)] pub(crate) enum T {/*array literal*/A(V),
+ /*assignment*/ E ,
/* NB: this does not identify whether possible verbs */ /*(ad)verb*/ V(S) ,
/* are monadic or dyadic. that is done during parsing.*/ /*symbol*/ SY(SY) }
impl T{pub(super) fn is_noun(&self)->bool{use T::*;matches!(self,A(_)|SY(_))}}
pub(crate) fn lex(input:&str)->R>{use std::ops::Deref;
let(mut ts)=input.split_whitespace().peekable(); let(mut o)=V::with_capacity(ts.size_hint().0);
while let Some(t) =ts.next(){
- if let Some(sy) =t.parse().ok().map(T::SY){o.push(sy);} // symbol
+ if t == "=:" {o.push(T::E)} // assignment
+ else if let Some(sy) =t.parse().ok().map(T::SY){o.push(sy);} // symbol
else if let Some(mut v)=t.parse().ok().map(|i|vec![i]){ // array literal
macro_rules! peek{()=>{ts.peek().and_then(|t|t.parse().ok())}} // ..is the next token a number?
macro_rules! put{($i:ident)=>{ts.next().map(drop);v.push($i);}} // ..append to our array literal
@@ -16,6 +19,7 @@
#[cfg(test)]mod t{use super::{*,T::A as TA,T::V as TV,T::SY as TSY};
/// test helper: lex an expression and check the output
macro_rules! t{($f:ident,$i:literal,$o:expr)=>{#[test]fn $f()->R<()>{eq!(lex($i)?,$o);ok!()}}}
+ macro_rules! sy{($i:literal)=>{$i.parse().map(T::SY).unwrap()}}
// === lexing unit tests ===
t!(lex_1, "1", v![TA(v![1])] );
t!(lex_9, "9", v![TA(v![9])] );
@@ -23,16 +27,24 @@
t!(lex_monad, "# 1 2 3", v![TV(S::from("#")), TA(v![1,2,3])] );
t!(lex_dyad, "1 + 2", v![TA(v![1]), TV(S::from("+")), TA(v![2])] );
t!(lex_two_verbs, "1 + # 1 2 3", v![TA(v![1]), TV(S::from("+")), TV(S::from("#")), TA(v![1,2,3])] );
- t!(lex_symbol, "abc", v![TSY("abc".parse().unwrap())] );
+ t!(sum_over, "+ / 1 2", v![TV(S::from("+")), TV(S::from("/")), TA(v![1,2])] );
+ t!(lex_symbol, "abc", v![sy!("abc")] );
+ t!(lex_assign, "a =: 1", v![sy!("a"), T::E, TA(v![1])] );
}
-}/**input parsing*/pub(crate) use parse::{D,M,N,parse};mod parse{use {crate::*,super::lex::{T,lex}};
+}/**input parsing*/mod parse{use {crate::*,super::lex::{T,lex}};
/**dyadic verb */ #[derive(DBG,PE,PO)] pub enum D {Plus,Mul, Left, Right }
/**monadic verb */ #[derive(DBG,PE,PO)] pub enum M {Idot,Shape,Tally,Transpose,Same}
+ /**dyadic adverb */ #[derive(DBG )] pub enum Yd{/**dyadic `/` */ Table ,
+ /**dyadic `\` */ Infix }
+ /**monadic adverb */ #[derive(DBG )] pub enum Ym{/**monadic `/`*/ Insert,
+ /**monadic `\`*/ Prefix}
/**ast node */ #[derive(DBG, )] pub enum N {/**array literal*/ A{a:A},
/**dyadic verb*/ D{d:D,l:B,r:B},
/**monadic verb*/ M{m:M,o:B},
+ /**dyadic adverb*/ Yd{yd:Yd,d:D,l:B,r:B},
+ /**monadic adverb*/ Ym{ym:Ym,d:D,o:B},
/**symbol*/ S{sy:SY},
- /**symbol assignment*/V{sy:SY,e:B}}
+ /**symbol assignment*/E{sy:SY,e:B}}
impl From for N{fn from(sy:SY)->N{N::S{sy}}}
impl TF> for N{type Error=E; fn try_from(a:Vec)->R{a.try_into().map(|a|N::A{a})}}
/**parse a sequence of tokens into an abstract syntax tree.*/
@@ -41,32 +53,84 @@
/*debug*/debug_assert!(ts.is_empty());debug_assert!(ctx.len() <= 1,"AST needs a root node: {ctx:?}");/*debug*/
Ok(ctx.pop())}
fn parse_(ts:&mut V,ctx:&mut V>)->R<()>{
+ // push a new AST node onto the `ctx` stack and return, indicating a successful parsing "step."
macro_rules! step{($n:expr)=>{ctx.push(b!($n));r!(ok!());}}
+
let(v):S=match ts.pop(){
Some(T::V(v)) =>v, /*take the next verb, or return if done*/ None=>r!(ok!()),
Some(T::A(v)) =>{let(n)=v.try_into()?;step!(n);} // array literal
- Some(T::SY(sy))=>{let(n)=sy.into(); step!(n);}}; // symbol name
- let(rhs)=ctx.pop().ok_or(err!("no right-hand operand for `{v:?}`"))?;
- if ts.last().map(T::is_noun).unwrap_or(false){ // dyadic verbs
- let(l)=match(ts.pop()).unwrap(/*we just peeked this*/){T::V(v)=>ur!(),/*..and checked it isn't a verb*/
- T::A(a)=> {a.try_into().map(|a|b!(a))?}
- T::SY(sy)=>if(v=="=:"){step!(N::V{sy,e:rhs});}else{b!(sy.into())} }; // handle variable assignment
- let(d)=D::new(&v).ok_or(err!("invalid dyadic verb {v:?}"))?; step!(N::D{d,l,r:rhs});
- }else{let(m)=M::new(&v).ok_or(err!("invalid monadic verb {v:?}"))?; step!(N::M{m,o:rhs}); }}
- impl M{fn new(s:&str)->O{use M::*;Some(match s{"i."=>Idot,"$"=>Shape,"#"=>Tally,"|:"=>Transpose,"["|"]"=>Same,_=>r!(None)})}}
- impl D{fn new(s:&str)->O{use D::*;Some(match s{"+"=>Plus,"*"=>Mul,"["=>Left,"]"=>Right,_=>r!(None)})}}
+ Some(T::SY(sy))=>{let(n)=sy.into(); step!(n);} // symbol name
+ Some(T::E) =>{let Some(T::SY(sy))=ts.pop()else{bail!("assignment must apply to a symbol")};
+ /*assignment*/ let(e)=ctx.pop().ok_or(err!("assignment missing right-hand side"))?;
+ step!(N::E{sy,e});}};
+ let(rhs)=ctx.pop().ok_or(err!("no right-hand operand for `{v:?}`"))?; /*right-hand operand*/
+ let(lhs):O>=match ts.pop(){ /*take the left-hand operand, if it exists. */
+ None =>{ None} Some(T::A(v)) =>Some(b!(v.try_into()?)),
+ Some(t@T::V(_)|t@T::E)=>{ts.push(t);None} Some(T::SY(sy)) =>Some(b!(sy.into())),
+ };
+ /*first, process monadic and dyadic verbs*/
+ if let Some(l)=lhs{let(d)=D::new(&v).ok_or(err!("invalid dyad {v:?}"))?;step!(N::D{l,r:rhs,d});}
+ else if let Some(m)=M::new(&v){step!(N::M{m,o:rhs});}
+
+ /*otherwise, we should treat this as an adverb*/
+ let(y)=v;let(d)=ts.pop().ok_or(err!("adverbs need a verb to apply"))?;
+ macro_rules! ym {()=>{
+ let(ym)=Ym::new(&y).ok_or(err!("invalid monadic adverb {y:?}"))?;
+ let(d)=match(d){T::V(ref d)=>D::new(d),_=>None}.ok_or(err!("invalid dyadic verb {d:?} for adverb {y:?}"))?;
+ step!(N::Ym{ym,d,o:rhs});
+ }}
+ macro_rules! yd {($l:ident)=>{
+ let(yd)=Yd::new(&y).ok_or(err!("invalid dyadic adverb {y:?}"))?;
+ let(d)=match(d){T::V(ref d)=>D::new(d),_=>None}.ok_or(err!("invalid dyadic verb {d:?} for adverb {y:?}"))?;
+ step!(N::Yd{yd,d,l:$l,r:rhs});
+ }}
+ match(ts.pop()){ /*confirm the arity by examining the left-hand operand (NB: put it back if you don't need it!)*/
+ /*monadic adverb*/ /*dyadic adverb */
+ None =>{ ym!();} Some(T::A(v)) =>{let(l)=b!(v.try_into()?);yd!(l);}
+ Some(t@T::E|t@T::V(_))=>{ts.push(t);ym!();} Some(T::SY(sy))=>{let(l)=b!(sy.into()); yd!(l);}
+ }
+ bail!("fallthrough: unexpected parsing error");
+ }
+ /**get a tuple, representing a window peeking on the next two elements in this token stream.*/
+ fn lhs_window(ts:&[T])->(O<&T>,O<&T>){match ts{
+ []=>(None,None), [a]=>(None,Some(&a)), [a,b]=>(Some(&a),Some(&b)), [..,a,b]=>(Some(&a),Some(&b)) } }
+ #[cfg(test)]mod lhs_t{use super::*;
+ #[test] fn lhs_window_works_on_empty(){is!(matches!(lhs_window(&[]), (None, None))) }
+ #[test] fn lhs_window_works_on_one (){is!(matches!(lhs_window(&[T::E]), (None, Some(_)))) }
+ #[test] fn lhs_window_works_on_two (){is!(matches!(lhs_window(&[T::E,T::A(v![])]),(Some(T::E),Some(T::A(_)))))}
+ #[test] fn lhs_window_works_on_three(){
+ is!(matches!(lhs_window(&[T::E,T::A(v![]),T::V(S::from("+"))]),(Some(T::A(_)),Some(_))))}
+ }
+
+ impl M {fn new(s:&str)->O {use M::*; Some(match s{"i."=>Idot ,"$" =>Shape ,"|:"=>Transpose ,
+ "#" =>Tally ,"[" =>Same ,"]" =>Same ,
+ _=>r!(None)})}}
+ impl D {fn new(s:&str)->O {use D::*; Some(match s{"+" =>Plus ,"*" =>Mul ,"[" =>Left ,
+ "]" =>Right , _=>r!(None)})}}
+ impl Ym{fn new(s:&str)->O{use Ym::*;Some(match s{"/" =>Insert,"\\"=>Prefix, _=>r!(None)})}}
+ impl Yd{fn new(s:&str)->O{use Yd::*;Some(match s{"/" =>Table ,"\\"=>Infix , _=>r!(None)})}}
#[cfg(test)]mod t{use super::*;
macro_rules! t{($f:ident,$i:literal)=>{#[test]fn $f()->R<()>{let(mut ts)=lex($i)?;let ast=parse(&mut ts)?;ok!()}}}
macro_rules! tf{($f:ident,$i:literal)=>{#[test] #[should_panic]fn $f(){let(mut ts)=lex($i).unwrap();let ast=parse(&mut ts).unwrap();}}}
- t!(parse_1x1,"1"); t!(parse_1x3,"1 2 3"); t!(parse_tally_1,"# 1"); t!(parse_tally_1x3,"# 1 2 3");
- tf!(parse_tally_as_dyad_fails, "1 # 2"); tf!(parse_tally_with_no_operand, "#");
- tf!(parse_idot_as_dyad_fails, "1 # 2"); tf!(parse_idot_with_no_operand, "i.");
- t!(parse_idot_1,"i. 1"); t!(parse_idot_1x2,"i. 4 3"); t!(parse_1plus2,"1 + 2");
- t!(parse_1x3_times_1x3,"1 2 3 * 4 5 6"); t!(parse_tally_tally_1x3,"# # 1 2 3");
- t!(parse_symbol,"a"); t!(parse_symbol_plus_symbol,"a + b"); t!(parse_tally_symbol,"# a");
- t!(parse_symbol_times_symbol,"a * b"); t!(parse_tally_tally_symbol,"# # a");
- tf!(parse_symbol_times_symbol_numbers,"a * b 1"); tf!(parse_tally_tally_symbol_symbol,"# # a b");
- t!(assign_symbol_scalar,"a =: 1"); t!(assign_symbol_slice,"a =: 1 2 3"); t!(assign_symbol_idot,"a =: i. 2 3");
+ /*parsing unit tests; t!(..) asserts a success, while tf asserts a failure.*/
+ t!(parse_1x1,"1"); t!(parse_1x3,"1 2 3");
+ t!(parse_tally_1,"# 1"); t!(parse_tally_1x3,"# 1 2 3");
+ tf!(parse_tally_as_dyad_fails,"1 # 2"); tf!(parse_tally_with_no_operand, "#");
+ tf!(parse_idot_as_dyad_fails,"1 # 2"); tf!(parse_idot_with_no_operand, "i.");
+ t!(parse_idot_1,"i. 1"); t!(parse_idot_1x2,"i. 4 3");
+ t!(parse_1plus2,"1 + 2"); t!(parse_1x3_times_1x3,"1 2 3 * 4 5 6");
+ t!(parse_tally_tally_1x3,"# # 1 2 3"); t!(parse_symbol,"a");
+ t!(parse_symbol_plus_symbol,"a + b"); t!(parse_tally_symbol,"# a");
+ t!(parse_symbol_times_symbol,"a * b"); t!(parse_tally_tally_symbol,"# # a");
+ tf!(parse_bad_symbol_literal,"a * b 1"); tf!(parse_tally_tally_symbol_symbol,"# # a b");
+ t!(assign_symbol_scalar,"a =: 1"); t!(assign_symbol_slice,"a =: 1 2 3");
+ t!(assign_symbol_idot,"a =: i. 2 3"); t!(parse_monad_then_dyad,"1 + # 1 2 3");
t!(assign_symbol_slice_plus_slice,"a =: 1 2 3 + 1 2 3"); t!(parse_empty,"");
+ t!(parse_insert_add_to_matrix,"+ / i. 3 3"); t!(parse_prefix_of_sequence, "] \\ i. 3");
+ t!(parse_multiplication_table,"1 2 3 * / 1 2 3"); t!(parse_infixes_of_sequence,"4 ] \\ i. 10");
+ tf!(parse_no_verb_over_sequence_fails,"/ i. 3 3"); tf!(parse_no_verb_prefix_sequence_fails,"/ i. 3 3");
+ // NOTE: J will allow this, but first-class functions are not implemented here.
+ tf!(parse_add_over_no_sequence_fails,"+ /"); tf!(parse_add_prefix_no_sequence_fails,"+ \\");
+ /* TODO: running sums should be supported */ // t!(parse_a_running_sum, "+ / \\ 1 2 3 4 5");
}
}
diff --git a/tests/t.rs b/tests/t.rs
index 94c1204..505bfbe 100644
--- a/tests/t.rs
+++ b/tests/t.rs
@@ -53,6 +53,9 @@
#[test]fn right_dyad() ->R<()>{let(a)=eval_s("1 ] 2")?; eq!(a.as_i()?,2); ok!()}
#[test]fn left_dyad_other() ->R<()>{let(a)=eval_s("1 [ 2 3 4")?; eq!(a.as_i()?,1); ok!()}
#[test]fn right_dyad_other() ->R<()>{let(a)=eval_s("1 ] 2 3 4")?; eq!(a.as_slice()?,&[2,3,4]);ok!()}
+ #[test]fn left_dyad_does_not_rotate_slice()->R<()>{
+ /*NB: other operators like + or * may rotate the left-hand argument to fit. [ does not. */
+ let(a)=eval_s("1 2 3 4 [ i. 4 1")?;eq!(a.as_slice()?,&[1,2,3,4]);ok!()}
} #[cfg(test)]mod symbol_assignment{use super::*;
#[test]fn assign_and_get_i()->R<()>{let(mut st)=ST::default();let(a)=eval("a =: 3",&mut st)?;
assert_eq!(st.get_s("a").unwrap().as_i().unwrap(),3);ok!()}
@@ -69,4 +72,50 @@
#[test]fn display_scalar()->R<()>{let(a)=A::from_i(666)?;eq!(a.to_string(),"666\n");ok!()}
#[test]fn display_slice()->R<()>{let a:&[I]=&[7,8,9];let a=A::try_from(a)?;eq!(a.to_string(),"7 8 9\n");ok!()}
#[test]fn display_matrix()->R<()>{let(a)=eval_s("i. 3 3")?;eq!(a.to_string(),"0 1 2\n3 4 5\n6 7 8\n");ok!()}
+} #[cfg(test)]mod adverb{use super::*;
+ // === monadic / "insert" adverb
+ #[test]fn insert_sum_one_number()->R<()>{let(a)=eval_s("+ / 1")?;let(i)=a.as_i()?;eq!(i,1);ok!()}
+ #[test]fn insert_sum_two_numbers()->R<()>{let(a)=eval_s("+ / 1 8")?;let(i)=a.as_i()?;eq!(i,9);ok!()}
+ #[test]fn insert_sum_a_sequence()->R<()>{let(a)=eval_s("+ / i. 4")?;let(i)=a.as_i()?;eq!(i,6);ok!()}
+ #[test]fn insert_sum_a_shifted_sequence()->R<()>{let(a)=eval_s("+ / 1 + i. 4")?;let(i)=a.as_i()?;eq!(i,10);ok!()}
+ #[test]fn insert_product_of_a_sequence()->R<()>{let(a)=eval_s("* / i. 3")?;let(i)=a.as_i()?;eq!(i,0);ok!()}
+ #[test]fn insert_product_of_a_shifted_sequence()->R<()>{let(a)=eval_s("* / 2 + i. 3")?;let(i)=a.as_i()?;eq!(i,24);ok!()}
+ // === monadic \ "prefix" adverb
+ #[test]fn prefix_of_scalar()->R<()>{let(a)=eval_s("] \\ 1")?;let(i)=a.as_i()?;eq!(i,1);ok!()}
+ #[test]fn prefix_of_slice() ->R<()>{let(a)=eval_s("] \\ 1 2 3")?;eq!(a.into_matrix()?,&[&[1,0,0],
+ &[1,2,0],
+ &[1,2,3],]);ok!()}
+ #[test]fn prefix_of_slice_2()->R<()>{let(a)=eval_s("+ \\ 1 2 3")?;eq!(a.into_matrix()?,&[&[1,0,0],
+ &[1,2,0],
+ &[1,2,3],]);ok!()}
+ #[test]fn prefix_of_slice_3()->R<()>{let(a)=eval_s("* \\ 1 2 3")?;eq!(a.into_matrix()?,&[&[1,0,0],
+ &[1,1,0],
+ &[1,1,1],]);ok!()}
+ // === dyadic / "table" adverb
+ #[test]fn table_of_scalars_plus()->R<()>{let(a)=eval_s("1 + / 1")?;eq!(a.as_i()?,2);ok!()}
+ #[test]fn table_of_scalars_mult()->R<()>{let(a)=eval_s("1 * / 1")?;eq!(a.as_i()?,1);ok!()}
+ #[test]fn table_of_scalar_plus_slice()->R<()>{let(a)=eval_s("1 + / 1 2 3")?;eq!(a.as_slice()?,&[2,3,4]);ok!()}
+ #[test]fn table_of_two_slices_mult()->R<()>{let(a)=eval_s("1 2 3 * / 1 2 3")?;eq!(a.into_matrix()?,&[&[1,2,3],
+ &[2,4,6],
+ &[3,6,9]]);
+ ok!()}
+ #[test]fn table_of_two_diff_slices_mult()->R<()>{let(a)=eval_s("2 4 * / 1 2 3")?;eq!(a.into_matrix()?,&[&[2,4,6],
+ &[4,8,12]]);
+ ok!()}
+ // === dyadic \ "infix" adverb
+ #[test]fn infix_to_reshape_1()->R<()>{let(a)=eval_s("1 ] \\ 1 2 3")?;eq!(a.into_matrix()?,&[&[1],
+ &[2],
+ &[3]]);
+ ok!()}
+ #[test]fn infix_to_reshape_2()->R<()>{let(a)=eval_s("2 ] \\ 1 2 3")?;eq!(a.into_matrix()?,&[&[1,2],
+ &[2,3]]);
+ ok!()}
+ #[test]fn infix_to_reshape_3()->R<()>{let(a)=eval_s("3 ] \\ 1 2 3 4")?;eq!(a.into_matrix()?,&[&[1,2,3],
+ &[2,3,4]]);
+ ok!()}
+} #[cfg(test)]mod adverb_fancy{use super::*; /*XXX: these are left unsolved for now*/
+ #[ignore] #[test]fn running_sum_of_a_sequence()->R<()>{let(a)=eval_s("+ / \\ 1 2 3 4 5")?;
+ let(i)=a.as_slice()?;eq!(i,&[1,3,6,10,15]);ok!()}
+ #[ignore] #[test]fn running_product_of_a_sequence()->R<()>{let(a)=eval_s("* / \\ 1 2 3 4 5")?;
+ let(i)=a.as_slice()?;eq!(i,&[1,2,6,24,120]);ok!()}
}