use cyclotron::bruteforce;
use cyclotron::lazy_set::LazySet as _;
use std::collections::BTreeSet;
use std::rc::Rc;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
enum Token {
Op(char),
Lit(i32),
}
impl Token {
fn op(self) -> Option<char> {
match self {
Token::Op(op) => Some(op),
_ => None,
}
}
fn lit(self) -> Option<i32> {
match self {
Token::Lit(x) => Some(x),
_ => None,
}
}
}
fn eat<T>(tokens: &[Token], f: impl FnOnce(Token) -> Option<T>) -> Option<(T, &[Token])> {
tokens
.split_first()
.and_then(|(&token, tokens)| f(token).map(|x| (x, tokens)))
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
enum Expr {
Const(i32),
Bin(Rc<Expr>, char, Rc<Expr>),
}
impl Expr {
fn eval(&self) -> i32 {
match self {
Expr::Const(x) => *x,
Expr::Bin(a, '+', b) => a.eval() + b.eval(),
Expr::Bin(a, '-', b) => a.eval() - b.eval(),
Expr::Bin(a, '*', b) => a.eval() * b.eval(),
Expr::Bin(a, '/', b) => a.eval() / b.eval(),
Expr::Bin(..) => unimplemented!(),
}
}
}
fn test<'a>(
name: &str,
mut parse_expr: impl FnMut(&'a [Token]) -> BTreeSet<(Rc<Expr>, &'a [Token])>,
) {
eprintln!("Testing {}:", name);
let mut parse_and_eval = |tokens| {
let parses = parse_expr(tokens);
eprintln!(" parse_expr({:?}) =", tokens);
for (e, after) in &parses {
eprintln!(" | {:?} // remaining: {:?}", e, after);
}
for (e, after) in parses {
if after.is_empty() {
eprintln!(" eval({:?}) = {}", e, e.eval())
}
}
};
parse_and_eval(&[Token::Lit(123)]);
eprintln!();
parse_and_eval(&[Token::Lit(1), Token::Op('+'), Token::Lit(2)]);
eprintln!();
parse_and_eval(&[
Token::Lit(1),
Token::Op('+'),
Token::Lit(2),
Token::Op('*'),
Token::Lit(3),
]);
}
fn test_bruteforce<'a>() {
test(
"bruteforce",
bruteforce::memoize(
|parse_expr, tokens: &'a [Token]| -> BTreeSet<(Rc<Expr>, &'a [Token])> {
let mut parses = BTreeSet::new();
for (lhs, tokens) in parse_expr(tokens) {
if let Some((op, tokens)) = eat(tokens, Token::op) {
for (rhs, tokens) in parse_expr(tokens) {
parses.insert((Rc::new(Expr::Bin(lhs.clone(), op, rhs)), tokens));
}
}
}
if let Some((x, tokens)) = eat(tokens, Token::lit) {
parses.insert((Rc::new(Expr::Const(x)), tokens));
}
parses
},
),
);
}
fn test_lazy_set<'a>() {
use cyclotron::lazy_set::{self, call};
let parse_expr = call;
let parse_expr = |tokens: &'a [Token]| {
parse_expr(tokens)
.flat_map(|(lhs, tokens)| {
eat(tokens, Token::op).flat_map(|(op, tokens)| {
parse_expr(tokens).map(move |(rhs, tokens)| (Expr::Bin(lhs, op, rhs), tokens))
})
})
.union(
eat(tokens, Token::lit).map(|(x, tokens)| (Expr::Const(x), tokens)),
)
.map(|(e, tokens)| (Rc::new(e), tokens))
};
test(
"lazy_set/bruteforce",
bruteforce::memoize(lazy_set::to_eager(parse_expr)),
);
test(
"lazy_set/depth_first",
lazy_set::depth_first::memoize(parse_expr),
);
}
fn main() {
test_bruteforce();
test_lazy_set();
}