807 lines
21 KiB
Rust
807 lines
21 KiB
Rust
use core::fmt;
|
|
use std::{
|
|
collections::{HashMap, HashSet},
|
|
env,
|
|
io::{self, Read},
|
|
iter,
|
|
sync::Mutex,
|
|
};
|
|
|
|
use intaglio::{Symbol, SymbolTable};
|
|
use itertools::Itertools;
|
|
use once_cell::sync::Lazy;
|
|
use rand::{distributions::Alphanumeric, Rng};
|
|
|
|
static TABLE: Lazy<Mutex<SymbolTable>> = Lazy::new(|| Mutex::new(SymbolTable::new()));
|
|
static FORALL: Lazy<Symbol> = Lazy::new(|| TABLE.lock().unwrap().intern("forall").unwrap());
|
|
/// Note that it is actually the string "def". This is for the user's convenience.
|
|
static CONCRETE: Lazy<Symbol> = Lazy::new(|| TABLE.lock().unwrap().intern("def").unwrap());
|
|
|
|
const DEFAULT_COMPLEXITY_LIMIT: usize = 20_000;
|
|
const DEFAULT_STEP_LIMIT: usize = 2_000;
|
|
|
|
fn main() {
|
|
match do_it() {
|
|
Some(out) => println!("{out}"),
|
|
None => println!("error :("),
|
|
}
|
|
}
|
|
|
|
fn do_it() -> Option<String> {
|
|
let buf = if let Some(buf) = env::args().nth(1) {
|
|
buf
|
|
} else {
|
|
let mut buf = String::new();
|
|
io::stdin().read_to_string(&mut buf).ok()?;
|
|
buf
|
|
};
|
|
//eprintln!("{:#?}", lex(&buf));
|
|
Some(format!(
|
|
"{}",
|
|
run_program(&buf, DEFAULT_STEP_LIMIT, DEFAULT_COMPLEXITY_LIMIT)?
|
|
))
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
enum Sexp {
|
|
Atom(Symbol),
|
|
List(Vec<Sexp>),
|
|
}
|
|
|
|
impl Sexp {
|
|
fn nil() -> Self {
|
|
Self::List(Vec::new())
|
|
}
|
|
|
|
/// Returns `true` if the sexp is [`Atom`].
|
|
///
|
|
/// [`Atom`]: Sexp::Atom
|
|
#[must_use]
|
|
fn is_atom(&self) -> bool {
|
|
matches!(self, Self::Atom(..))
|
|
}
|
|
|
|
/// Returns `true` if the sexp is [`List`].
|
|
///
|
|
/// [`List`]: Sexp::List
|
|
#[must_use]
|
|
fn is_list(&self) -> bool {
|
|
matches!(self, Self::List(..))
|
|
}
|
|
|
|
#[must_use]
|
|
fn atom(&self) -> Option<&Symbol> {
|
|
match self {
|
|
Sexp::Atom(at) => Some(at),
|
|
Sexp::List(_) => None,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn list(&self) -> Option<&[Sexp]> {
|
|
match self {
|
|
Sexp::Atom(_) => None,
|
|
Sexp::List(xs) => Some(&xs),
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn unwrap_atom(&self) -> Symbol {
|
|
match self {
|
|
Sexp::Atom(sym) => *sym,
|
|
Sexp::List(_) => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/// Push a sexp to the end of a sexp. Does nothing if the sexp is an atom.
|
|
fn push(&mut self, item: Self) {
|
|
match self {
|
|
Sexp::Atom(_) => (),
|
|
Sexp::List(xs) => xs.push(item),
|
|
}
|
|
}
|
|
|
|
fn rw(&self, rule: &Rule) -> Sexp {
|
|
rw(rule, self)
|
|
}
|
|
|
|
#[must_use]
|
|
fn pertinent(&self) -> HashSet<Symbol> {
|
|
match self {
|
|
Sexp::Atom(sym) => {
|
|
let mut set = HashSet::with_capacity(1);
|
|
set.insert(*sym);
|
|
set
|
|
}
|
|
Sexp::List(xs) => xs
|
|
.iter()
|
|
.map(Sexp::pertinent)
|
|
.fold(HashSet::with_capacity(0), |a, b| {
|
|
a.union(&b).copied().collect::<HashSet<Symbol>>()
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn concretion_targets(&self) -> HashSet<&Sexp> {
|
|
match self {
|
|
at @ Sexp::Atom(_) => {
|
|
let mut out = HashSet::with_capacity(1);
|
|
out.insert(at);
|
|
out
|
|
}
|
|
Sexp::List(xs) => {
|
|
let mut out = HashSet::from_iter(xs);
|
|
out.insert(self);
|
|
out.extend(xs.iter().flat_map(Sexp::concretion_targets));
|
|
out
|
|
}
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn contains_sym(&self, sym: Symbol) -> bool {
|
|
match self {
|
|
Sexp::Atom(at) => *at == sym,
|
|
Sexp::List(xs) => xs.iter().any(|s| s.contains_sym(sym)),
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn complexity(&self) -> usize {
|
|
match self {
|
|
Sexp::Atom(_) => 1,
|
|
Sexp::List(xs) => 10 + xs.iter().map(Sexp::complexity).sum::<usize>(),
|
|
}
|
|
}
|
|
|
|
// #[must_use]
|
|
// fn apply_lets(&self) -> Sexp {
|
|
// match self {
|
|
// Sexp::Atom(_) => self.clone(),
|
|
// Sexp::List(xs) => match Let::from_sexp(self) {
|
|
// Some(let_) => let_.eval(),
|
|
// None => Sexp::List(xs.iter().map(Sexp::apply_lets).collect()),
|
|
// },
|
|
// }
|
|
// }
|
|
|
|
#[must_use]
|
|
fn apply_special_form<'src, F: SpecialForm<'src>>(&'src self) -> Sexp {
|
|
match self {
|
|
Sexp::Atom(_) => match F::from_sexp(self) {
|
|
Some(form) => form.eval(),
|
|
None => self.clone(),
|
|
},
|
|
Sexp::List(xs) => match F::from_sexp(self) {
|
|
Some(form) => form.eval(),
|
|
None => Sexp::List(xs.iter().map(Sexp::apply_special_form::<F>).collect()),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Sexp {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Sexp::Atom(id) => write!(f, "{}", TABLE.lock().unwrap().get(*id).unwrap()),
|
|
Sexp::List(xs) => write!(f, "({})", xs.iter().map(ToString::to_string).join(" ")),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
enum Token<'src> {
|
|
LParen,
|
|
RParen,
|
|
Atom(&'src str),
|
|
}
|
|
|
|
fn lex(src: &str) -> Vec<Token> {
|
|
// maybe a bad idea?
|
|
let mut out = Vec::with_capacity(src.len());
|
|
|
|
let mut atom = None;
|
|
let mut in_comment = false;
|
|
|
|
let indices: Vec<_> = src.char_indices().map(|(a, _)| a).collect();
|
|
let slice_src = |start: usize, end: usize| -> &str {
|
|
let end = indices.get(end + 1).unwrap_or(&src.len()) - 1;
|
|
&src[indices[start]..=end]
|
|
};
|
|
|
|
for (i, c) in src.chars().enumerate() {
|
|
match c {
|
|
';' | '`' => {
|
|
in_comment = true;
|
|
}
|
|
'\n' => {
|
|
in_comment = false;
|
|
}
|
|
_ => (),
|
|
}
|
|
if !in_comment {
|
|
match c {
|
|
'(' => {
|
|
if let Some((start, end)) = atom {
|
|
out.push(Token::Atom(slice_src(start, end)));
|
|
atom = None;
|
|
}
|
|
out.push(Token::LParen);
|
|
}
|
|
|
|
')' => {
|
|
if let Some((start, end)) = atom {
|
|
out.push(Token::Atom(slice_src(start, end)));
|
|
atom = None;
|
|
}
|
|
out.push(Token::RParen);
|
|
}
|
|
|
|
c if c.is_whitespace() => {
|
|
if let Some((start, end)) = atom {
|
|
out.push(Token::Atom(slice_src(start, end)));
|
|
atom = None;
|
|
}
|
|
}
|
|
|
|
_ => {
|
|
atom = match atom {
|
|
Some((start, end)) => Some((start, end + 1)),
|
|
None => Some((i, i)),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some((start, end)) = atom {
|
|
out.push(Token::Atom(slice_src(start, end)));
|
|
}
|
|
|
|
out
|
|
}
|
|
|
|
fn at_depth(depth: usize, sexp: &mut Sexp) -> &mut Sexp {
|
|
if depth == 0 {
|
|
sexp
|
|
} else {
|
|
match sexp {
|
|
at @ Sexp::Atom(_) => at,
|
|
Sexp::List(xs) => at_depth(depth - 1, xs.last_mut().unwrap()),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse<'src>(tokens: &'src [Token<'src>]) -> Option<Vec<Sexp>> {
|
|
let mut out = Sexp::nil();
|
|
let mut depth = 0;
|
|
|
|
for token in tokens {
|
|
match token {
|
|
Token::LParen => {
|
|
at_depth(depth, &mut out).push(Sexp::nil());
|
|
depth += 1;
|
|
}
|
|
Token::RParen => {
|
|
if depth == 0 {
|
|
return None;
|
|
}
|
|
depth -= 1;
|
|
}
|
|
Token::Atom(at) => {
|
|
let id = TABLE.lock().unwrap().intern((*at).to_string()).unwrap();
|
|
at_depth(depth, &mut out).push(Sexp::Atom(id));
|
|
}
|
|
}
|
|
}
|
|
|
|
match out {
|
|
Sexp::List(xs) => Some(xs),
|
|
Sexp::Atom(_) => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
enum Rule {
|
|
Forall {
|
|
vars: Vec<Symbol>,
|
|
lhs: Sexp,
|
|
rhs: Sexp,
|
|
},
|
|
Concrete {
|
|
lhs: Sexp,
|
|
rhs: Sexp,
|
|
},
|
|
}
|
|
|
|
impl Rule {
|
|
fn forall(vars: Vec<Symbol>, lhs: Sexp, rhs: Sexp) -> Rule {
|
|
Rule::Forall { vars, lhs, rhs }
|
|
}
|
|
|
|
fn concrete(lhs: Sexp, rhs: Sexp) -> Rule {
|
|
Rule::Concrete { lhs, rhs }
|
|
}
|
|
|
|
fn concrify(&self, sexp: &Sexp) -> Rule {
|
|
match self {
|
|
Rule::Forall { vars, lhs, rhs } => {
|
|
if vars.is_empty() {
|
|
Rule::Concrete {
|
|
lhs: lhs.clone(),
|
|
rhs: rhs.clone(),
|
|
}
|
|
} else {
|
|
let rule = Rule::var_replace(vars[0], sexp);
|
|
let lhs = rw(&rule, lhs);
|
|
let rhs = rw(&rule, rhs);
|
|
|
|
if vars.len() == 1 {
|
|
Rule::Concrete { lhs, rhs }
|
|
} else {
|
|
Rule::Forall {
|
|
vars: vars[1..].to_vec(),
|
|
lhs,
|
|
rhs,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Rule::Concrete { .. } => self.clone(),
|
|
}
|
|
}
|
|
|
|
fn concretions(&self, targets: &HashSet<&Sexp>) -> HashSet<Rule> {
|
|
if self.is_concrete() {
|
|
let mut out = HashSet::with_capacity(1);
|
|
out.insert(self.clone());
|
|
out
|
|
} else {
|
|
let mut out = HashSet::with_capacity(targets.len() * self.num_vars());
|
|
|
|
for target in targets {
|
|
let concred = self.concrify(target);
|
|
if concred.is_concrete() {
|
|
out.insert(concred);
|
|
break;
|
|
}
|
|
|
|
for concred in concred.concretions(targets) {
|
|
out.insert(concred);
|
|
}
|
|
}
|
|
|
|
out
|
|
}
|
|
}
|
|
|
|
// fn matches(&self, expr: &Sexp) -> bool {
|
|
// could_match(self.vars(), self.lhs(), expr)
|
|
// }
|
|
|
|
#[must_use]
|
|
fn lhs(&self) -> &Sexp {
|
|
match self {
|
|
Rule::Concrete { lhs, .. } | Rule::Forall { lhs, .. } => lhs,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn rhs(&self) -> &Sexp {
|
|
match self {
|
|
Rule::Forall { rhs, .. } | Rule::Concrete { rhs, .. } => rhs,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn vars(&self) -> Option<&Vec<Symbol>> {
|
|
match self {
|
|
Rule::Forall { vars, .. } => Some(vars),
|
|
Rule::Concrete { .. } => None,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn num_vars(&self) -> usize {
|
|
match self.vars() {
|
|
Some(xs) => xs.len(),
|
|
None => 0,
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn rw(&self, sexp: &Sexp) -> Sexp {
|
|
rw(self, sexp)
|
|
}
|
|
|
|
/// Returns `true` if the rule is [`Forall`].
|
|
///
|
|
/// [`Forall`]: Rule::Forall
|
|
#[must_use]
|
|
fn is_forall(&self) -> bool {
|
|
matches!(self, Self::Forall { .. })
|
|
}
|
|
|
|
/// Returns `true` if the rule is [`Concrete`].
|
|
///
|
|
/// [`Concrete`]: Rule::Concrete
|
|
#[must_use]
|
|
fn is_concrete(&self) -> bool {
|
|
matches!(self, Self::Concrete { .. })
|
|
}
|
|
|
|
fn var_replace(from: Symbol, to: &Sexp) -> Rule {
|
|
Rule::Concrete {
|
|
lhs: Sexp::Atom(from),
|
|
rhs: to.clone(),
|
|
}
|
|
}
|
|
|
|
#[must_use]
|
|
fn concrete_with_matches(&self, matches: &Matches<'_>) -> Rule {
|
|
match self {
|
|
Rule::Forall { vars, .. } => {
|
|
let mut out = self.clone();
|
|
|
|
for var in vars {
|
|
let expr = matches.get(var).copied().cloned().unwrap_or_else(Sexp::nil);
|
|
out = out.concrify(&expr);
|
|
}
|
|
|
|
out
|
|
}
|
|
Rule::Concrete { .. } => self.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Rule {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Rule::Forall { vars, lhs, rhs } => {
|
|
let vars = vars
|
|
.iter()
|
|
.map(|v| TABLE.lock().unwrap().get(*v).unwrap().to_string())
|
|
.join(" ");
|
|
let abs = format!("∀{vars},");
|
|
let lhs = format!("{abs:>15} {lhs}");
|
|
write!(f, "{lhs:<50} ==> {rhs}")
|
|
}
|
|
Rule::Concrete { lhs, rhs } => write!(f, "{:>50} ==> {rhs}", lhs.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn could_match(vars: Option<&[Symbol]>, lhs: &Sexp, expr: &Sexp) -> bool {
|
|
let is_var = |var: &Symbol| -> bool {
|
|
match vars {
|
|
Some(vars) => vars.contains(var),
|
|
None => false,
|
|
}
|
|
};
|
|
|
|
match (lhs, expr) {
|
|
(Sexp::Atom(a), Sexp::Atom(b)) => a == b || is_var(a),
|
|
(Sexp::Atom(a), Sexp::List(_)) => is_var(a),
|
|
(Sexp::List(a), Sexp::List(b)) => {
|
|
a.len() == b.len() && a.iter().zip(b).all(|(a, b)| could_match(vars, a, b))
|
|
}
|
|
(Sexp::List(_), Sexp::Atom(_)) => false,
|
|
}
|
|
}
|
|
|
|
type Matches<'src> = HashMap<Symbol, &'src Sexp>;
|
|
|
|
// DONE?: there. can. be. at most. one. match.
|
|
// i'm happy that this is faster, but it might be worth going back to the per-variable thing
|
|
fn matches<'src>(vars: &[Symbol], lhs: &Sexp, expr: &'src Sexp) -> Option<Matches<'src>> {
|
|
match (lhs, expr) {
|
|
(Sexp::Atom(a), Sexp::Atom(b)) => {
|
|
if a == b {
|
|
Some(HashMap::with_capacity(0))
|
|
} else if vars.contains(a) {
|
|
let mut out = HashMap::with_capacity(1);
|
|
out.insert(*a, expr);
|
|
Some(out)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
(Sexp::Atom(a), Sexp::List(_)) => {
|
|
if vars.contains(a) {
|
|
let mut out = HashMap::with_capacity(1);
|
|
out.insert(*a, expr);
|
|
Some(out)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
(Sexp::List(_), Sexp::Atom(_)) => None,
|
|
(Sexp::List(xs), Sexp::List(ys)) => {
|
|
if xs.len() == ys.len() {
|
|
xs.iter()
|
|
.zip(ys)
|
|
.map(|(lhs, expr)| matches(vars, lhs, expr))
|
|
.reduce(|a, b| match (a, b) {
|
|
(None, _) => None,
|
|
(_, None) => None,
|
|
(Some(a), Some(b)) => merge_matches(vars, a, b),
|
|
})
|
|
.unwrap_or(None)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn merge_matches<'src>(
|
|
vars: &[Symbol],
|
|
a: Matches<'src>,
|
|
b: Matches<'src>,
|
|
) -> Option<Matches<'src>> {
|
|
let mut out = HashMap::with_capacity(vars.len());
|
|
|
|
for var in vars {
|
|
match (a.get(var), b.get(var)) {
|
|
(None, None) => {}
|
|
(Some(m), None) | (None, Some(m)) => {
|
|
out.insert(*var, *m);
|
|
}
|
|
(Some(a), Some(b)) => {
|
|
if a != b {
|
|
return None;
|
|
}
|
|
out.insert(*var, *a);
|
|
}
|
|
}
|
|
}
|
|
|
|
Some(out)
|
|
}
|
|
|
|
// fn absorb_matches<'src>(from: Matches<'src>, into: &mut Matches<'src>) {
|
|
// for (var, matches) in from {
|
|
// let insert_into = into.entry(var).or_default();
|
|
// for matc in matches {
|
|
// insert_into.insert(matc);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
fn rw(rule: &Rule, sexp: &Sexp) -> Sexp {
|
|
if rule.lhs() == rule.rhs() {
|
|
return sexp.clone();
|
|
}
|
|
|
|
match rule {
|
|
Rule::Forall { vars, lhs, .. } => {
|
|
if let Some(matc) = matches(vars, lhs, sexp) {
|
|
rule.concrete_with_matches(&matc).rw(sexp)
|
|
} else {
|
|
match sexp {
|
|
Sexp::Atom(_) => sexp.clone(),
|
|
Sexp::List(xs) => Sexp::List(xs.iter().map(|s| s.rw(rule)).collect()),
|
|
}
|
|
}
|
|
}
|
|
Rule::Concrete { lhs, rhs } => {
|
|
if sexp == lhs {
|
|
rhs.clone()
|
|
} else {
|
|
match sexp {
|
|
Sexp::Atom(_) => sexp.clone(),
|
|
Sexp::List(xs) => Sexp::List(xs.iter().map(|s| s.rw(rule)).collect()),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn simp(
|
|
rules: &[Rule],
|
|
mut expr: Sexp,
|
|
step_limit: usize,
|
|
complexity_limit: usize,
|
|
) -> (Sexp, usize) {
|
|
let mut max = 0;
|
|
let mut grace = step_limit / 4;
|
|
for i in 0..step_limit {
|
|
expr = expr
|
|
.apply_special_form::<Genslop>()
|
|
.apply_special_form::<Log>()
|
|
.apply_special_form::<Let>()
|
|
.apply_special_form::<Eq>();
|
|
|
|
let complexity = expr.complexity();
|
|
// eprintln!("{}/{step_limit} {complexity}", i + 1);
|
|
if complexity > complexity_limit {
|
|
break;
|
|
}
|
|
|
|
let last = expr.clone();
|
|
|
|
for rule in rules {
|
|
expr = rw(rule, &expr);
|
|
}
|
|
|
|
if expr == last {
|
|
grace -= 1;
|
|
}
|
|
|
|
if grace == 0 {
|
|
break;
|
|
}
|
|
|
|
max = i;
|
|
}
|
|
|
|
(expr, max)
|
|
}
|
|
|
|
trait SpecialForm<'src>: Sized {
|
|
fn from_sexp(sexp: &'src Sexp) -> Option<Self>;
|
|
|
|
fn eval(self) -> Sexp;
|
|
}
|
|
|
|
static LET: Lazy<Symbol> = Lazy::new(|| TABLE.lock().unwrap().intern("let").unwrap());
|
|
#[derive(Debug, Clone)]
|
|
struct Let<'src> {
|
|
from: Symbol,
|
|
to: &'src Sexp,
|
|
body: &'src Sexp,
|
|
}
|
|
|
|
impl<'src> SpecialForm<'src> for Let<'src> {
|
|
fn from_sexp(sexp: &'src Sexp) -> Option<Self> {
|
|
match sexp {
|
|
Sexp::Atom(_) => None,
|
|
Sexp::List(xs) => match &xs[..] {
|
|
[Sexp::Atom(let_), Sexp::Atom(from), to, body] if *let_ == *LET => Some(Self {
|
|
from: *from,
|
|
to,
|
|
body,
|
|
}),
|
|
_ => None,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn eval(self) -> Sexp {
|
|
let Self { from, to, body } = self;
|
|
|
|
rw(&Rule::var_replace(from, to), body)
|
|
}
|
|
}
|
|
|
|
static EQ: Lazy<Symbol> = Lazy::new(|| TABLE.lock().unwrap().intern("eq").unwrap());
|
|
#[derive(Debug, Clone)]
|
|
struct Eq<'src> {
|
|
a: &'src Sexp,
|
|
b: &'src Sexp,
|
|
then: &'src Sexp,
|
|
els: &'src Sexp,
|
|
}
|
|
|
|
impl<'src> SpecialForm<'src> for Eq<'src> {
|
|
fn from_sexp(sexp: &'src Sexp) -> Option<Self> {
|
|
match sexp {
|
|
Sexp::Atom(_) => None,
|
|
Sexp::List(xs) => match &xs[..] {
|
|
[Sexp::Atom(eq), a, b, then, els] if *eq == *EQ => Some(Self { a, b, then, els }),
|
|
_ => None,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn eval(self) -> Sexp {
|
|
let Self { a, b, then, els } = self;
|
|
|
|
if a == b {
|
|
then.clone()
|
|
} else {
|
|
els.clone()
|
|
}
|
|
}
|
|
}
|
|
|
|
static GENSLOP: Lazy<Symbol> = Lazy::new(|| TABLE.lock().unwrap().intern("genslop").unwrap());
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Genslop;
|
|
|
|
impl Genslop {
|
|
fn gen() -> String {
|
|
format!(
|
|
"slop-{}",
|
|
rand::thread_rng()
|
|
.sample_iter(&Alphanumeric)
|
|
.take(8)
|
|
.map(char::from)
|
|
.collect::<String>()
|
|
)
|
|
}
|
|
}
|
|
|
|
impl SpecialForm<'_> for Genslop {
|
|
fn from_sexp(sexp: &Sexp) -> Option<Self> {
|
|
if let Some(genslop) = sexp.atom() {
|
|
if *genslop == *GENSLOP {
|
|
Some(Genslop)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn eval(self) -> Sexp {
|
|
let ident = Genslop::gen();
|
|
let symbol = TABLE.lock().unwrap().intern(ident).unwrap();
|
|
Sexp::Atom(symbol)
|
|
}
|
|
}
|
|
|
|
static LOG: Lazy<Symbol> = Lazy::new(|| TABLE.lock().unwrap().intern("log").unwrap());
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct Log<'src>(&'src Sexp);
|
|
|
|
impl<'src> SpecialForm<'src> for Log<'src> {
|
|
fn from_sexp(sexp: &'src Sexp) -> Option<Self> {
|
|
match sexp {
|
|
Sexp::List(xs) if xs.len() == 2 && *xs[0].atom()? == *LOG => Some(Self(&xs[1])),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn eval(self) -> Sexp {
|
|
eprintln!("LOG: {}", self.0);
|
|
self.0.clone()
|
|
}
|
|
}
|
|
|
|
fn make_rule(sexp: &Sexp) -> Option<Rule> {
|
|
match sexp {
|
|
Sexp::List(xs) => match &xs[..] {
|
|
[Sexp::Atom(forall), Sexp::List(vars), lhs, rhs]
|
|
if *forall == FORALL.to_owned() && vars.iter().all(Sexp::is_atom) =>
|
|
{
|
|
Some(Rule::forall(
|
|
vars.iter().map(Sexp::unwrap_atom).collect(),
|
|
lhs.clone(),
|
|
rhs.clone(),
|
|
))
|
|
}
|
|
[Sexp::Atom(concrete), lhs, rhs] if *concrete == CONCRETE.to_owned() => {
|
|
Some(Rule::concrete(lhs.clone(), rhs.clone()))
|
|
}
|
|
_ => None,
|
|
},
|
|
Sexp::Atom(_) => None,
|
|
}
|
|
}
|
|
|
|
fn run_program(src: &str, step_limit: usize, complexity_limit: usize) -> Option<Sexp> {
|
|
let mut parsed = parse(&lex(src))?;
|
|
|
|
let expr = parsed.pop()?;
|
|
let mut rules = parsed.iter().filter_map(make_rule).collect_vec();
|
|
rules.sort_by_key(Rule::num_vars);
|
|
|
|
for (i, rule) in rules.iter().enumerate() {
|
|
eprintln!("{}{rule}", if i % 2 == 0 { "\x1b[0m" } else { "\x1b[1m" });
|
|
}
|
|
//eprintln!("{:<50}\n{:<50}\n{:^50}", "|", "▾", expr.to_string());
|
|
|
|
let (simped, steps) = simp(&rules, expr, step_limit, complexity_limit);
|
|
eprintln!("\x1b[0m");
|
|
eprintln!("Complexity: {}", simped.complexity());
|
|
eprintln!("Steps: {}", steps + 1);
|
|
Some(simped)
|
|
}
|
|
|
|
// (forall (a b) (+ a (succ b)) (succ (+ a b))) (forall (a) (+ a 0) a) (forall (a) (= a a) true) (= (+ (succ 0) (succ 0)) (succ (succ 0)))
|