int_seq/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
extern crate proc_macro;
mod affine;
mod oeis;
mod parser;
mod sequence;
use parser::parse_int_seq;
use proc_macro::TokenStream;
/// Run doctests from the README.md file
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
struct ReadmeDoctests;
/// Given a sequence of integers that includes an range ellipsis, we deduce the
/// integers that are omitted using various heuristics. This macro will produce
/// an array of integers at compile time. We do not support lazy iterators yet
/// since some integer sequences do not have a simple closed form formula.
///
/// We currently use two heuristics for inferring integer sequences:
/// - Affine sequences are of the form `a*i + b`
/// - OEIS sequences are in the [On-Line Encyclopedia of Integer Sequences (OEIS)](https://oeis.org/) database. We use `reqwest` to perform HTTP requests within a procedural macro to query the database. By doing this at compile time, we can avoid runtime overheads
///
/// This is inspired by [Raku lang's sequence operator (`...`)](https://doc.perl6.org/language/operators#infix_...).
///
/// Example:
/// ```rust
/// use int_seq::int_seq;
///
/// // fibonacci sequence
/// assert_eq!(int_seq!(0, 1, 1, 2, 3, 5, 8, 13..100), [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]);
/// ```
#[proc_macro]
pub fn int_seq(token_stream: TokenStream) -> TokenStream {
let (seq, end) = parse_int_seq(&token_stream).unwrap_or_else(|| {
panic!(
"could not parse token stream. token stream: {:x?}",
token_stream
)
});
let inferred_seq = sequence::infer_sequence(&seq).expect("could not infer sequence");
let generated_seq = inferred_seq.generate(&seq, end);
let comma_seperated_ints = generated_seq
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(",");
format!("[{}]", comma_seperated_ints).parse().unwrap()
}