Skip to content

Commit

Permalink
Merge pull request #13 from cratelyn/lints
Browse files Browse the repository at this point in the history
πŸ’ final pass, address lints and polish formatting
  • Loading branch information
cratelyn authored Nov 23, 2023
2 parents 8c53847 + cb4a07a commit 214ec8d
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 48 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@ voice feels like, and why people do it.

#### 🐣 j

**πŸ” main loop**

above we looked at the Incunabulum's main event loop. here is j's equivalent entrypoint:

```rust
mod p;use{p::*,j::*,std::io::Write};
fn main()->R<()>{let mut st=ST::default(); // define symbol table
let prompt =| |{print!(" ");std::io::stdout().flush()?;ok!()}; // (callback) print whitespace
let read =|_ |{let mut l=S::new();stdin().read_line(&mut l)?;Ok(l)}; // (callback) read input
let mut eval=|s:S|{eval(&s,&mut st)}; // (callback) read and evaluate once
let print =|a:A|{println!("{a}")}; // (callback) print array
loop{prompt().and_then(read).and_then(&mut eval)?.map(print);}; /* !!! main event loop !!! */ }
```

**πŸƒ verbs**

the core of the four dyadic verbs `[`, `]`, `+`, and `*` is shown below. the definitions of the `A`
array type and the `D` dyadic verb enum are included for reference.

Expand Down
28 changes: 13 additions & 15 deletions src/a.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use super::*; use std::marker::PhantomData as PD;
fn oob(&self,i:U,j:U)->R<()>{let A{m,n,..}=*self;
if(i==0||j==0||i>m||j>n){bail!("({i},{j}) is out-of-bounds of ({m},{n})")}ok!()}
/// returns the scalar `A_ij` within this array. returns an error if position is out-of-bounds.
pub fn index(&self,i:U,j:U)->R<U> {self.oob(i,j)?;let A{m,n,..}=*self;let(i,j)=(i-1,j-1);Ok((i*n)+j)}
pub fn index(&self,i:U,j:U)->R<U> {self.oob(i,j)?;let(i,j)=(i-1,j-1);Ok((i*self.n)+j)}
/// returns the scalar `A_ij` within this array. does not check if the position is in bounds.
pub fn index_uc(&self,i:U,j:U)->R<U>{ let A{m,n,..}=*self;let(i,j)=(i-1,j-1);Ok((i*n)+j)}
pub fn index_uc(&self,i:U,j:U)->R<U>{ let(i,j)=(i-1,j-1);Ok((i*self.n)+j)}
}
// === test helpers ===
/// `A::index` test case generator. check that indexing at a position returns the expected value.
Expand All @@ -27,17 +27,17 @@ use super::*; use std::marker::PhantomData as PD;
#[macro_export] macro_rules! toob{($f:ident,$a:ident,$i:literal,$j:literal)=>
{#[test] fn $f()->R<()>{is!($a()?.index($i,$j).is_err());ok!()}}}
// === 1x1 array indexing ===
fn sca()->R<A>{let Ok(a@A{m:1,n:1,..})=A::from_i(42)else{bail!("bad dims")};Ok(a)}
#[cfg(test)] fn sca()->R<A>{let Ok(a@A{m:1,n:1,..})=A::from_i(42)else{bail!("bad dims")};Ok(a)}
toob!(i00_for_scalar,sca,0,0);toob!(i10_for_scalar,sca,1,0);toob!(i01_for_scalar,sca,0,1);
toob!(i21_for_scalar,sca,2,1);toob!(i12_for_scalar,sca,1,2);toob!(i22_for_scalar,sca,2,2);
ti!(i11_for_scalar,sca,1,1,0);
// === 2x3 array indexing ===
fn arr2x3()->R<A>{let Ok(a@A{m:2,n:3,..})=A::zeroed(2,3)else{bail!("bad dims")};Ok(a)}
#[cfg(test)] fn arr2x3()->R<A>{let Ok(a@A{m:2,n:3,..})=A::zeroed(2,3)else{bail!("bad dims")};Ok(a)}
toob!(i00_for_2x3,arr2x3,0,0);toob!(i01_for_2x3,arr2x3,0,1);toob!(i10_for_2x3,arr2x3,1,0);
ti!(i11_for_2x3,arr2x3,1,1,0);ti!(i12_for_2x3,arr2x3,1,2,1);ti!(i13_for_2x3,arr2x3,1,3,2);
ti!(i21_for_2x3,arr2x3,2,1,3);ti!(i22_for_2x3,arr2x3,2,2,4);ti!(i23_for_2x3,arr2x3,2,3,5);
// === 3x3 array indexing ===
fn arr3x3()->R<A>{let Ok(a@A{m:3,n:3,..})=A::zeroed(3,3)else{bail!("bad dims")};Ok(a)}
#[cfg(test)] fn arr3x3()->R<A>{let Ok(a@A{m:3,n:3,..})=A::zeroed(3,3)else{bail!("bad dims")};Ok(a)}
toob!(i00_for_3x3,arr3x3,0,0);toob!(i01_for_3x3,arr3x3,0,1);toob!(i14_for_3x3,arr3x3,1,4);
toob!(i41_for_3x3,arr3x3,4,1);toob!(i44_for_3x3,arr3x3,4,4);
ti!(i11_for_3x3,arr3x3,1,1,0);ti!(i21_for_3x3,arr3x3,2,1,3);ti!(i22_for_3x3,arr3x3,2,2,4);
Expand Down Expand Up @@ -111,8 +111,7 @@ use super::*; use std::marker::PhantomData as PD;
impl A<MI>{ // only allow matrix access for initialized arrays
pub fn into_matrix(&self)->R<Vec<&[I]>>{let(A{m,n,..})=*self;
(0..m).map(|m|{self.ptr_at(m+1,1)}).map(|r|r.map(|d|unsafe{from_raw_parts(d as *mut I, n as U)})).collect()}}
impl TF<&[&[I]]> for A{type Error=E;fn try_from(s:&[&[I]])->R<A>{todo!("TF<&[I]> for A")}}
impl PE<&[&[I]]> for A{fn eq(&self,r:&&[&[I]])->bool{let(A{m,n,..})=self;
impl PE<&[&[I]]> for A{fn eq(&self,r:&&[&[I]])->bool{
if(r.len()!=self.m){r!(false)}for(i,r_i)in(r.into_iter().enumerate()){
if(r_i.len()!=self.n){r!(false)}for(j,r_ij)in(r_i.into_iter().enumerate()){
let(i,j)=(i+1,j+1);let(a_ij)=match(self.get(i,j)){Ok(v)=>v,Err(_)=>r!(false)};
Expand All @@ -121,7 +120,7 @@ use super::*; use std::marker::PhantomData as PD;
/**monadic verbs*/impl A{
pub fn m_same(self)->R<A>{Ok(self)}
pub fn m_idot(self)->R<A>{let(a@A{m,n,..})=self;let gi=|i,j|a.get(i,j)?.try_into().map_err(E::from);
if let(1,1)=(m,n){let(m,n)=(1,gi(1,1)?);let(mut o)=A::new(1,n)?;
if let(1,1)=(m,n){let(n)=(gi(1,1)?);let(mut o)=A::new(1,n)?;
for(j)in(1..=n){o.set(1,j,(j-1).try_into()?)?;}Ok(unsafe{o.finish()})}
else if let(1,2)=(m,n){let(m,n)=(gi(1,1)?,gi(1,2)?);
let(mut v)=0_u32;let(f)=move |_,_|{let(v_o)=v;v+=1;Ok(v_o)};A::new(m,n)?.init_with(f)}
Expand All @@ -136,9 +135,9 @@ use super::*; use std::marker::PhantomData as PD;
/*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}
/*left */fn left(x:I,_:I)->I{x } /*right */fn right(_:I,y:I)->I{ y}
} impl A{
pub fn d_left (self,r:A)->R<A>{Ok(self) }
pub fn d_left (self,_:A)->R<A>{Ok(self) }
pub fn d_right(self,r:A)->R<A>{Ok(r) }
pub fn d_plus(self,r:A) ->R<A>{A::d_do(self,r,D::add)}
pub fn d_mul (self,r:A) ->R<A>{A::d_do(self,r,D::mul)}
Expand Down Expand Up @@ -171,7 +170,7 @@ use super::*; use std::marker::PhantomData as PD;
// === monadic `/`, `Ym::Insert` tests
macro_rules! test_insert{($f:ident,$d:expr,$a:expr,$o:expr)=>
{#[test]fn $f()->R<()>{let(a):R<A>={$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())?;
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, <A as TF<&[I]>>::try_from(&[1,2,3,4,5]), (1+2+3+4+5) );
Expand All @@ -188,15 +187,14 @@ use super::*; use std::marker::PhantomData as PD;
let f=|i,j|->R<I>{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<A>{let(s)=r.as_slice().map_err(|_|err!("infix rhs must be a slice"))?;
fn infix(_:D,l:A,r:A)->R<A>{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<MI>{
pub fn deep_copy(&self)->R<A>{let A{m,n,l:li,d:di,i:_}=*self;A::new(m,n)?.init_with(|i,j|{self.get(i,j)})}}
/**deep-copy*/impl A<MI>{pub fn deep_copy(&self)->R<A>{A::new(self.m,self.n)?.init_with(|i,j|{self.get(i,j)})}}

/**display*/mod fmt{use super::*;
impl DS for A<MI>{
fn fmt(&self,fmt:&mut FMT)->FR{let A{m,n,..}=*self;for(i,j)in(self.iter())
fn fmt(&self,fmt:&mut FMT)->FR{let A{n,..}=*self;for(i,j)in(self.iter())
{let(x)=self.get_uc(i,j).map_err(|_|std::fmt::Error)?;write!(fmt,"{x}{}",if(j==n){'\n'}else{' '})?;}ok!()}}}
7 changes: 3 additions & 4 deletions src/j.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![allow(dead_code,unused_variables,unreachable_code,unused_imports,unused_parens)]
/**prelude*/mod p;use p::*;#[cfg(test)]use p::tp::*;
/**array*/mod a; /**read input*/mod r; /**symbol table*/mod s;
pub use self::{a::*,r::*,s::*};
#![allow(unused_parens)] /**prelude*/mod p;use p::*; /*test prelude*/#[cfg(test)]use p::tp::*;
/**symbol table*/mod s; /**array*/ mod a; /**lex/parse input*/mod r;use r::*;
pub use self::{a::*,s::*};
pub fn eval(input:&str,st:&mut ST)->R<O<A>>{
let(mut ts)=lex(input)?;let(ast)=match(parse(&mut ts)?){Some(x)=>x,None=>rrn!()};eval_(ast,st)}
fn eval_(ast:B<N>,st:&mut ST)->R<O<A>>{use{M::*,D::*};
Expand Down
2 changes: 1 addition & 1 deletion src/p.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! prelude; shorthand aliases for common types and traits, macros for common patterns.
#![allow(unused_imports)] //! prelude; shorthand aliases for common types and traits, macros for common patterns.
pub(crate)use{Box as B,char as C,u32 as I,usize as U,Option as O,String as S,TryFrom as TF,TryInto as TI,Vec as V};
pub(crate)use{std::{alloc::Layout as L,clone::Clone as CL,cmp::{PartialEq as PE,PartialOrd as PO},
collections::{BTreeMap as BM,VecDeque as VD},fmt::{Debug as DBG,Display as DS,Formatter as FMT,Result as FR},
Expand Down
30 changes: 8 additions & 22 deletions src/r.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ mod lex{use crate::*;
/*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<V<T>>{use std::ops::Deref;
pub(crate) fn lex(input:&str)->R<V<T>>{
let(mut ts)=input.split_whitespace().peekable(); let(mut o)=V::with_capacity(ts.size_hint().0);
while let Some(t) =ts.next(){
if t == "=:" {o.push(T::E)} // assignment
Expand All @@ -16,7 +15,7 @@ mod lex{use crate::*;
while let Some(i)=peek!(){put!(i);} o.push(T::A(v));}
else {o.push(T::V(S::from(t)))} // otherwise, a verb or adverb
} r!(Ok(o)) }
#[cfg(test)]mod t{use super::{*,T::A as TA,T::V as TV,T::SY as TSY};
#[cfg(test)]mod t{use super::{*,T::A as TA,T::V as TV};
/// 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()}}
Expand All @@ -31,7 +30,7 @@ mod lex{use crate::*;
t!(lex_symbol, "abc", v![sy!("abc")] );
t!(lex_assign, "a =: 1", v![sy!("a"), T::E, TA(v![1])] );
}
}/**input parsing*/mod parse{use {crate::*,super::lex::{T,lex}};
}/**input parsing*/mod parse{use {crate::*,super::lex::T};
/**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,Inc}
/**dyadic adverb */ #[derive(DBG )] pub enum Yd{/**dyadic `/` */ Table ,
Expand All @@ -55,7 +54,6 @@ mod lex{use crate::*;
fn parse_(ts:&mut V<T>,ctx:&mut V<B<N>>)->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
Expand All @@ -71,7 +69,6 @@ mod lex{use crate::*;
/*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 {()=>{
Expand All @@ -88,20 +85,7 @@ mod lex{use crate::*;
/*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<M> {use M::*; Some(match s{"i."=>Idot ,"$" =>Shape ,"|:"=>Transpose ,
"#" =>Tally ,"[" =>Same ,"]" =>Same ,
">:"=>Inc, _=>r!(None)})}}
Expand All @@ -110,8 +94,10 @@ mod lex{use crate::*;
impl Ym{fn new(s:&str)->O<Ym>{use Ym::*;Some(match s{"/" =>Insert,"\\"=>Prefix, _=>r!(None)})}}
impl Yd{fn new(s:&str)->O<Yd>{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();}}}
macro_rules! t {($f:ident,$i:literal)=>{
#[test] fn $f()->R<()>{let(mut ts)=lex($i)?; let _=parse(&mut ts)?;ok!()}}}
macro_rules! tf{($f:ident,$i:literal)=>{
#[test] #[should_panic]fn $f() {let(mut ts)=lex($i).unwrap();let _=parse(&mut ts).unwrap();}}}
/*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");
Expand Down
15 changes: 9 additions & 6 deletions src/x.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! a J interpreter fragment, implemented in Rust.
#![allow(dead_code,unused_variables,unreachable_code,unused_imports,unused_parens)]
mod p; use{p::*,j::*}; fn main()->R<()>{use std::io::Write;let(mut st)=ST::default();
let(pr)=||{print!(" ");std::io::stdout().flush()};
let(rl)=||{let(mut l)=S::new();stdin().read_line(&mut l)?;Ok::<_,E>(l)};let(mut ev)=|l:S|eval(&l,&mut st);
loop{pr()?;let(o)=rl().and_then(|l|ev(l))?;if let Some(o)=o{println!("{}",o)}}ur!();}
/**a J interpreter fragment, implemented in Rust.*/
mod p;use{p::*,j::*,std::io::Write};
/// main interpreter entrypoint and event-loop.
fn main()->R<()>{let mut st=ST::default(); // define symbol table
let prompt =| |{print!(" ");std::io::stdout().flush()?;ok!()}; // (callback) print whitespace
let read =|_ |{let mut l=S::new();stdin().read_line(&mut l)?;Ok(l)}; // (callback) read input
let mut eval=|s:S|{eval(&s,&mut st)}; // (callback) read and evaluate once
let print =|a:A|{println!("{a}")}; // (callback) print array
loop{prompt().and_then(read).and_then(&mut eval)?.map(print);}; /* !!! main event loop !!! */ }

0 comments on commit 214ec8d

Please sign in to comment.