1201 lines
64 KiB
Rust
1201 lines
64 KiB
Rust
use std::fmt;
|
|
use std::rc::Rc;
|
|
use std::convert::From;
|
|
use std::collections::{BTreeSet,BTreeMap,hash_map::DefaultHasher};
|
|
use std::result::Result;
|
|
use std::iter;
|
|
use std::hash::{Hash,Hasher};
|
|
|
|
|
|
// TODO:
|
|
// -extend vau & env logic and SuspendedPair PE with sequence_params & wrap_level
|
|
// -add veval and vif & -1 wrap_level handling to SuspendedPair
|
|
// -add current-hashes to if, DeriComb Calls, and DCtx-push/can_progress, and Hash to *everything*
|
|
// -expand combiner_Return_ok with (veval body {env}) and (func ...params) | func doesn't take de and func+params are return ok
|
|
// -add drop redundent veval
|
|
// -add compiler
|
|
|
|
impl From<i32> for Form { fn from(item: i32) -> Self { Form::Int(item) } }
|
|
impl From<bool> for Form { fn from(item: bool) -> Self { Form::Bool(item) } }
|
|
// todo, strings not symbols?
|
|
impl From<String> for Form { fn from(item: String) -> Self { Form::Symbol(item) } }
|
|
impl From<&str> for Form { fn from(item: &str) -> Self { Form::Symbol(item.to_owned()) } }
|
|
|
|
impl<A: Into<Form>, B: Into<Form>> From<(A, B)> for Form {
|
|
fn from(item: (A, B)) -> Self {
|
|
Form::Pair(Rc::new(item.0.into()), Rc::new(item.1.into()))
|
|
}
|
|
}
|
|
|
|
pub enum PossibleTailCall {
|
|
Result(Rc<Form>),
|
|
TailCall(Rc<Form>, Rc<Form>),
|
|
}
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
pub enum Form {
|
|
Nil,
|
|
Int(i32),
|
|
Bool(bool),
|
|
Symbol(String),
|
|
Pair(Rc<Form>,Rc<Form>),
|
|
PrimComb(String, fn(Rc<Form>, Rc<Form>) -> PossibleTailCall),
|
|
DeriComb { se: Rc<Form>, de: Option<String>, params: String, body: Rc<Form> },
|
|
}
|
|
impl Form {
|
|
pub fn truthy(&self) -> bool {
|
|
match self {
|
|
Form::Bool(b) => *b,
|
|
Form::Nil => false,
|
|
_ => true,
|
|
}
|
|
}
|
|
pub fn int(&self) -> Option<i32> {
|
|
match self {
|
|
Form::Int(i) => Some(*i),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn sym(&self) -> Option<&str> {
|
|
match self {
|
|
Form::Symbol(s) => Some(s),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn car(&self) -> Option<Rc<Form>> {
|
|
match self {
|
|
Form::Pair(car, _cdr) => Some(Rc::clone(car)),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn cdr(&self) -> Option<Rc<Form>> {
|
|
match self {
|
|
Form::Pair(_car, cdr) => Some(Rc::clone(cdr)),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn append(&self, x: Rc<Form>) -> Option<Form> {
|
|
match self {
|
|
Form::Pair(car, cdr) => cdr.append(x).map(|x| Form::Pair(Rc::clone(car), Rc::new(x))),
|
|
Form::Nil => Some(Form::Pair(x, Rc::new(Form::Nil))),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn marked(&self, bctx: BCtx) -> (BCtx, Rc<MarkedForm>) {
|
|
match &*self {
|
|
Form::Nil => (bctx, Rc::new(MarkedForm::Nil)),
|
|
Form::Int(i) => (bctx, Rc::new(MarkedForm::Int(*i))),
|
|
Form::Bool(b) => (bctx, Rc::new(MarkedForm::Bool(*b))),
|
|
Form::Symbol(s) => (bctx, Rc::new(MarkedForm::Symbol(s.clone()))),
|
|
Form::Pair(car, cdr) => {
|
|
let (bctx, car) = car.marked(bctx);
|
|
let (bctx, cdr) = cdr.marked(bctx);
|
|
(bctx, MarkedForm::new_pair(car, cdr))
|
|
},
|
|
Form::DeriComb { se, de, params, body } => {
|
|
// this is a bit sus, but we don't run into it as of yet
|
|
panic!();
|
|
//let (bctx, se) = se.marked(bctx);
|
|
//let (bctx, body) = body.marked(bctx);
|
|
//let (bctx, new_id) = bctx.new_id();
|
|
//(bctx, Rc::new(MarkedForm::DeriComb { ids: NeededIds::new_none(), se, de: de.clone(),
|
|
// id: new_id, wrap_level: 0, sequence_params: vec![],
|
|
// rest_params: Some(params.clone()), body }))
|
|
},
|
|
Form::PrimComb(name, _f) => {
|
|
(bctx, match &name[..] {
|
|
"eval" => Rc::new(MarkedForm::PrimComb { name: "eval".to_owned(), takes_de: true, wrap_level: 1, f: |bctx, dctx, p| {
|
|
// put in partial eval logic,
|
|
// and veval
|
|
let b = p.car()?.unval()?;
|
|
let e = p.cdr()?.car()?;
|
|
//println!("Doing Eval (via tail call) of {} in {}", b, e);
|
|
Ok((bctx, PossibleMarkedTailCall::TailCall(e, b)))
|
|
}}),
|
|
"vau" => Rc::new(MarkedForm::PrimComb { name: "vau".to_owned(), takes_de: true, wrap_level: 0, f: |bctx, dctx, p| {
|
|
let de = p.car()?.sym().map(|s| s.to_owned()).ok();
|
|
let params = p.cdr()?.car()?.sym()?.to_owned();
|
|
let body = p.cdr()?.cdr()?.car()?.unval()?;
|
|
let se = Rc::clone(&dctx.e);
|
|
let (bctx, id) = bctx.new_id();
|
|
// TODO: figure out wrap level, sequence params, etc
|
|
let wrap_level = 0;
|
|
let sequence_params = vec![];
|
|
let rest_params = Some(params);
|
|
//
|
|
let inner_dctx = dctx.copy_push_frame(id.clone(), &se, &de, None, &rest_params, None, &body).unwrap();
|
|
let (bctx, body) = partial_eval(bctx, inner_dctx, Rc::clone(&body))?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(MarkedForm::new_deri_comb( se, None, de, id, wrap_level, sequence_params, rest_params, body ))))
|
|
}}),
|
|
// TODO: handle vif, partial eval branches
|
|
"if" => Rc::new(MarkedForm::PrimComb { name: "if".to_owned(), takes_de: true, wrap_level: 0, f: |bctx, dctx, p| {
|
|
let (bctx, cond) = partial_eval(bctx, dctx.clone(), p.car()?.unval()?)?;
|
|
let e = Rc::clone(&dctx.e);
|
|
if cond.truthy()? {
|
|
Ok((bctx, PossibleMarkedTailCall::TailCall(e, p.cdr()?.car()?.unval()?)))
|
|
} else {
|
|
Ok((bctx, PossibleMarkedTailCall::TailCall(e, p.cdr()?.cdr()?.car()?.unval()?)))
|
|
}
|
|
}}),
|
|
// TODO: handle these in the context of paritals
|
|
"cons" => Rc::new(MarkedForm::PrimComb { name: "cons".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let h = p.car()?;
|
|
let t = p.cdr()?.car()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(MarkedForm::new_pair(h, t))))
|
|
}}),
|
|
"car" => Rc::new(MarkedForm::PrimComb { name: "car".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(p.car()?.car()?)))
|
|
}}),
|
|
"cdr" => Rc::new(MarkedForm::PrimComb { name: "cdr".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(p.car()?.cdr()?)))
|
|
}}),
|
|
"quote" => Rc::new(MarkedForm::PrimComb { name: "quote".to_owned(), takes_de: false, wrap_level: 0, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(p.car()?)))
|
|
}}),
|
|
// This one needs to control eval to print debug before continuint
|
|
// which is werid to PE
|
|
"debug" => Rc::new(MarkedForm::PrimComb { name: "debug".to_owned(), takes_de: true, wrap_level: 0, f: |bctx, dctx, p| {
|
|
panic!();
|
|
let e = Rc::clone(&dctx.e);
|
|
Ok((bctx, PossibleMarkedTailCall::TailCall(e, p.cdr()?.car()?)))
|
|
}}),
|
|
// ditto
|
|
"assert" => Rc::new(MarkedForm::PrimComb { name: "assert".to_owned(), takes_de: true, wrap_level: 0, f: |bctx, dctx, p| {
|
|
//panic!();
|
|
println!("Assert test {:?}", p.car());
|
|
let (bctx, cond) = partial_eval(bctx, dctx.clone(), p.car()?.unval()?)?;
|
|
println!("\tAssert result {}", cond);
|
|
if !cond.truthy()? {
|
|
println!("Assert failed: {:?}", cond);
|
|
}
|
|
assert!(cond.truthy()?);
|
|
let e = Rc::clone(&dctx.e);
|
|
Ok((bctx, PossibleMarkedTailCall::TailCall(e, p.cdr()?.car()?.unval()?)))
|
|
}}),
|
|
// (vau de params body), should be able to take in wrap_level != 1 and do stuff
|
|
"=" => Rc::new(MarkedForm::PrimComb { name: "=".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?;
|
|
let b = p.cdr()?.car()?;
|
|
//println!("DOing (= {} {}) = {}", a, b, a==b);
|
|
// TODO: double check that this ignores ids etc. It should, since
|
|
// wrap_level=1 should mean that everything's a value
|
|
// also, it should just check by hash then?
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(a == b)))))
|
|
}}),
|
|
"<" => Rc::new(MarkedForm::PrimComb { name: "<".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?;
|
|
let b = p.cdr()?.car()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(a.int()? < b.int()?)))))
|
|
}}),
|
|
">" => Rc::new(MarkedForm::PrimComb { name: ">".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?;
|
|
let b = p.cdr()?.car()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(a.int()? > b.int()?)))))
|
|
}}),
|
|
"<=" => Rc::new(MarkedForm::PrimComb { name: "<=".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?;
|
|
let b = p.cdr()?.car()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(a.int()? <= b.int()?)))))
|
|
}}),
|
|
">=" => Rc::new(MarkedForm::PrimComb { name: ">=".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?;
|
|
let b = p.cdr()?.car()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(a.int()? >= b.int()?)))))
|
|
}}),
|
|
"+" => Rc::new(MarkedForm::PrimComb { name: "+".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a + b)))))
|
|
}}),
|
|
"-" => Rc::new(MarkedForm::PrimComb { name: "-".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a - b)))))
|
|
}}),
|
|
"*" => Rc::new(MarkedForm::PrimComb { name: "*".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a * b)))))
|
|
}}),
|
|
"/" => Rc::new(MarkedForm::PrimComb { name: "/".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a / b)))))
|
|
}}),
|
|
"%" => Rc::new(MarkedForm::PrimComb { name: "%".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a % b)))))
|
|
}}),
|
|
"&" => Rc::new(MarkedForm::PrimComb { name: "&".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a & b)))))
|
|
}}),
|
|
"|" => Rc::new(MarkedForm::PrimComb { name: "|".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a | b)))))
|
|
}}),
|
|
"^" => Rc::new(MarkedForm::PrimComb { name: "^".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
let a = p.car()?.int()?;
|
|
let b = p.cdr()?.car()?.int()?;
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Int(a ^ b)))))
|
|
}}),
|
|
"comb?" => Rc::new(MarkedForm::PrimComb { name: "comb?".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(match &* p.car()? {
|
|
MarkedForm::PrimComb { .. } => true,
|
|
MarkedForm::DeriComb { .. } => true,
|
|
_ => false,
|
|
})))))
|
|
}}),
|
|
"pair?" => Rc::new(MarkedForm::PrimComb { name: "pair?".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(match &* p.car()? {
|
|
MarkedForm::Pair(_h, _i, _a,_b) => true,
|
|
_ => false,
|
|
})))))
|
|
}}),
|
|
"symbol?" => Rc::new(MarkedForm::PrimComb { name: "symbol?".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(match &* p.car()? {
|
|
MarkedForm::Symbol(_) => true,
|
|
_ => false,
|
|
})))))
|
|
}}),
|
|
"int?" => Rc::new(MarkedForm::PrimComb { name: "int?".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(match &* p.car()? {
|
|
MarkedForm::Int(_) => true,
|
|
_ => false,
|
|
})))))
|
|
}}),
|
|
// maybe bool? but also could be derived. Nil def
|
|
"bool?" => Rc::new(MarkedForm::PrimComb { name: "bool?".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(match &* p.car()? {
|
|
MarkedForm::Bool(_) => true,
|
|
_ => false,
|
|
})))))
|
|
}}),
|
|
"nil?" => Rc::new(MarkedForm::PrimComb { name: "nil?".to_owned(), takes_de: false, wrap_level: 1, f: |bctx, dctx, p| {
|
|
Ok((bctx, PossibleMarkedTailCall::Result(Rc::new(MarkedForm::Bool(match &* p.car()? {
|
|
MarkedForm::Nil => true,
|
|
_ => false,
|
|
})))))
|
|
}}),
|
|
_ => panic!("gah! don't have partial eval version of {}", name),
|
|
})
|
|
},
|
|
}
|
|
}
|
|
}
|
|
impl fmt::Display for Form {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Form::Nil => write!(f, "nil"),
|
|
Form::Int(i) => write!(f, "{}", i),
|
|
Form::Bool(b) => write!(f, "{}", b),
|
|
Form::Symbol(s) => write!(f, "{}", s),
|
|
Form::Pair(car, cdr) => {
|
|
write!(f, "({}", car)?;
|
|
let mut traverse: Rc<Form> = Rc::clone(cdr);
|
|
loop {
|
|
match &*traverse {
|
|
Form::Pair(ref carp, ref cdrp) => {
|
|
write!(f, " {}", carp)?;
|
|
traverse = Rc::clone(cdrp);
|
|
},
|
|
Form::Nil => {
|
|
write!(f, ")")?;
|
|
return Ok(());
|
|
},
|
|
x => {
|
|
write!(f, ". {})", x)?;
|
|
return Ok(());
|
|
},
|
|
}
|
|
}
|
|
},
|
|
Form::PrimComb(name, _f) => write!(f, "<{}>", name),
|
|
Form::DeriComb { se, de, params, body } => {
|
|
write!(f, "<{} {} {}>", de.as_ref().unwrap_or(&"".to_string()), params, body)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
|
pub struct EnvID(i32);
|
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
|
pub struct MFHash(u64);
|
|
impl MFHash {
|
|
pub fn combine(&self, other: &MFHash) -> Self {
|
|
let mut h = DefaultHasher::new();
|
|
"combine/".hash(&mut h); self.0.hash(&mut h); other.hash(&mut h);
|
|
MFHash(h.finish())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
|
pub enum NeededIds {
|
|
True(BTreeSet<MFHash>),
|
|
None(BTreeSet<MFHash>),
|
|
Some(BTreeSet<EnvID>,BTreeSet<MFHash>),
|
|
}
|
|
impl NeededIds {
|
|
fn new_true() -> Self { NeededIds::True( BTreeSet::new()) }
|
|
fn new_none() -> Self { NeededIds::None( BTreeSet::new()) }
|
|
fn new_single(i: EnvID) -> Self { NeededIds::Some(iter::once(i).collect(), BTreeSet::new()) }
|
|
fn needs_nothing(&self) -> bool {
|
|
match self {
|
|
NeededIds::True(hashes) => false,
|
|
NeededIds::None(hashes) => hashes.is_empty(),
|
|
NeededIds::Some(set,hashes) => false,
|
|
}
|
|
}
|
|
fn hashes(&self) -> &BTreeSet<MFHash> {
|
|
match self {
|
|
NeededIds::True(hashes) => hashes,
|
|
NeededIds::None(hashes) => hashes,
|
|
NeededIds::Some(set,hashes) => hashes,
|
|
}
|
|
}
|
|
fn union(&self, other: &NeededIds) -> Self {
|
|
match self {
|
|
NeededIds::True(hashes) => NeededIds::True(hashes.union(other.hashes()).cloned().collect()),
|
|
NeededIds::None(hashes) => other.union_hashes(hashes),
|
|
NeededIds::Some(set, hashes) => match other {
|
|
NeededIds::True(ohashes) => NeededIds::True(hashes.union(ohashes).cloned().collect()),
|
|
NeededIds::None(ohashes) => NeededIds::Some(set.clone(), hashes.union(ohashes).cloned().collect()),
|
|
NeededIds::Some(oset,ohashes) => NeededIds::Some(set.union(oset).cloned().collect(), hashes.union(ohashes).cloned().collect()),
|
|
},
|
|
}
|
|
}
|
|
fn union_without(&self, other: &NeededIds, without: EnvID) -> Self {
|
|
self.union(other).without(without)
|
|
}
|
|
fn without(self, without: EnvID) -> Self {
|
|
match self {
|
|
NeededIds::True(_) => self,
|
|
NeededIds::None(_) => self,
|
|
NeededIds::Some(set, hashes) => {
|
|
let new: BTreeSet<EnvID> = set.into_iter().filter(|x| *x != without).collect();
|
|
if new.is_empty() {
|
|
NeededIds::None(hashes)
|
|
} else {
|
|
NeededIds::Some(new, hashes)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
fn union_hashes(&self, other: &BTreeSet<MFHash>) -> Self {
|
|
match self {
|
|
NeededIds::True(hashes) => NeededIds::True( other.union(hashes).cloned().collect()),
|
|
NeededIds::None(hashes) => NeededIds::None( other.union(hashes).cloned().collect()),
|
|
NeededIds::Some(set, hashes) => NeededIds::Some(set.clone(), other.union(hashes).cloned().collect()),
|
|
}
|
|
}
|
|
fn add_hash(&self, h: MFHash) -> Self {
|
|
match self {
|
|
NeededIds::True(hashes) => NeededIds::True( hashes.iter().cloned().chain(iter::once(h)).collect()),
|
|
NeededIds::None(hashes) => NeededIds::None( hashes.iter().cloned().chain(iter::once(h)).collect()),
|
|
NeededIds::Some(set, hashes) => NeededIds::Some(set.clone(), hashes.iter().cloned().chain(iter::once(h)).collect()),
|
|
}
|
|
}
|
|
fn add_id(&self, i: EnvID) -> Self {
|
|
match self {
|
|
NeededIds::True(hashes) => NeededIds::True( hashes.clone()),
|
|
NeededIds::None(hashes) => NeededIds::Some(iter::once(i).collect(), hashes.clone()),
|
|
NeededIds::Some(set, hashes) => NeededIds::Some(set.iter().cloned().chain(iter::once(i)).collect(), hashes.clone()),
|
|
}
|
|
}
|
|
}
|
|
pub enum PossibleMarkedTailCall {
|
|
Result(Rc<MarkedForm>),
|
|
TailCall(Rc<MarkedForm>, Rc<MarkedForm>),
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct BCtx {
|
|
id_counter: i32
|
|
}
|
|
impl BCtx {
|
|
pub fn new_id(mut self) -> (Self, EnvID) {
|
|
let new_id = EnvID(self.id_counter);
|
|
self.id_counter += 1;
|
|
(self, new_id)
|
|
}
|
|
}
|
|
|
|
|
|
// force is for drop_redundent_eval
|
|
// memo is only for recording currently executing hashes (calls and if's, current for us)
|
|
// only_head is not currently used
|
|
//only_head env env_counter memo env_stack force
|
|
#[derive(Clone)]
|
|
pub struct DCtx {
|
|
e : Rc<MarkedForm>,
|
|
sus_env_stack: Rc<BTreeMap<EnvID, Rc<MarkedForm>>>,
|
|
sus_prm_stack: Rc<BTreeMap<EnvID, Rc<MarkedForm>>>,
|
|
real_set: Rc<BTreeSet<EnvID>>,
|
|
force: bool,
|
|
current: Rc<BTreeSet<MFHash>>,
|
|
ident: usize,
|
|
}
|
|
impl DCtx {
|
|
pub fn copy_set_env(&self, e: &Rc<MarkedForm>) -> Self {
|
|
DCtx { e: Rc::clone(e), sus_env_stack: Rc::clone(&self.sus_env_stack), sus_prm_stack: Rc::clone(&self.sus_prm_stack), real_set: Rc::clone(&self.real_set), force: self.force, current: Rc::clone(&self.current), ident: self.ident+1 }
|
|
}
|
|
pub fn copy_push_frame(&self, id: EnvID, se: &Rc<MarkedForm>, de: &Option<String>, e: Option<Rc<MarkedForm>>, rest_params: &Option<String>, prms: Option<Rc<MarkedForm>>, body: &Rc<MarkedForm>) -> Result<Self,MFHash> {
|
|
let mut sus_env_stack = Rc::clone(&self.sus_env_stack);
|
|
let mut sus_prm_stack = Rc::clone(&self.sus_prm_stack);
|
|
let mut real_set = Rc::clone(&self.real_set);
|
|
if (e.is_some() || prms.is_some()) {
|
|
Rc::make_mut(&mut real_set).insert(id.clone());
|
|
}
|
|
let inner_env = if let Some(de) = de {
|
|
let de_val = if let Some(e) = e {
|
|
Rc::make_mut(&mut sus_env_stack).insert(id.clone(), Rc::clone(&e));
|
|
e
|
|
} else {
|
|
Rc::new(MarkedForm::SuspendedEnvLookup { name: Some(de.clone()), id: id.clone() })
|
|
};
|
|
massoc(de, de_val, Rc::clone(se))
|
|
} else { Rc::clone(se) };
|
|
// not yet supporting sequence params
|
|
let inner_env = if let Some(p) = rest_params {
|
|
let p_val = if let Some(prms) = prms {
|
|
Rc::make_mut(&mut sus_prm_stack).insert(id.clone(), Rc::clone(&prms));
|
|
prms
|
|
} else {
|
|
Rc::new(MarkedForm::SuspendedParamLookup { name: Some(p.clone()), id: id.clone(), cdr_num: 0, car: false })
|
|
};
|
|
massoc(p, p_val, inner_env)
|
|
} else { inner_env };
|
|
// Push on current frame hash
|
|
let new_hash = inner_env.hash().combine(&body.hash());
|
|
if self.current.contains(&new_hash) {
|
|
println!("Hash Rec Stop!");
|
|
Err(new_hash)
|
|
} else {
|
|
let new_current = Rc::new(self.current.iter().cloned().chain(iter::once(new_hash)).collect());
|
|
Ok(DCtx { e: inner_env, sus_env_stack, sus_prm_stack, real_set, force: self.force, current: new_current, ident: self.ident+1 })
|
|
}
|
|
}
|
|
|
|
pub fn can_progress(&self, ids: NeededIds) -> bool {
|
|
// check if ids is true || ids intersection EnvIDs in our stacks is non empty || ids.hashes - current is non empty
|
|
match ids {
|
|
NeededIds::True(hashes) => true,
|
|
NeededIds::None(hashes) => !self.current.is_superset(&hashes),
|
|
NeededIds::Some(ids,hashes) => (!self.real_set.is_disjoint(&ids)) || (!self.current.is_superset(&hashes)),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn new_base_ctxs() -> (BCtx,DCtx) {
|
|
let bctx = BCtx { id_counter: 0 };
|
|
let (bctx, root_env) = root_env().marked(bctx);
|
|
(bctx, DCtx { e: root_env, sus_env_stack: Rc::new(BTreeMap::new()), sus_prm_stack: Rc::new(BTreeMap::new()), real_set: Rc::new(BTreeSet::new()), force: false, current: Rc::new(BTreeSet::new()), ident: 0 } )
|
|
}
|
|
|
|
pub fn combiner_return_ok(x: Rc<MarkedForm>, check_id: EnvID) -> bool {
|
|
match match &*x {
|
|
MarkedForm::Nil => return true,
|
|
MarkedForm::Int(_) => return true,
|
|
MarkedForm::Bool(_) => return true,
|
|
MarkedForm::Symbol(_) => return true,
|
|
MarkedForm::Pair(h,ids,_,_) => ids,
|
|
|
|
MarkedForm::SuspendedSymbol(_) => return false,
|
|
MarkedForm::SuspendedParamLookup { id, .. } => return *id != check_id,
|
|
MarkedForm::SuspendedEnvLookup { id, .. } => return *id != check_id,
|
|
MarkedForm::SuspendedPair { .. } => {
|
|
// expand with (veval body {env}) and (func ...params) | func doesn't take de and func+params are return ok
|
|
return false
|
|
},
|
|
|
|
MarkedForm::PrimComb { .. } => return true,
|
|
MarkedForm::DeriComb { ids, .. } => ids,
|
|
} {
|
|
NeededIds::True(_hashes) => false,
|
|
NeededIds::None(_hashes) => true,
|
|
NeededIds::Some(ids,_hashes) => !ids.contains(&check_id),
|
|
}
|
|
//; Handles let 4.3 through macro level leaving it as (<comb wraplevel=1 (y) (+ y x 12)> 13)
|
|
//; need handling of symbols (which is illegal for eval but ok for calls) to push it farther
|
|
//(combiner_return_ok (rec-lambda combiner_return_ok (func_result env_id)
|
|
// (cond ((not (later_head? func_result)) (not (check_for_env_id_in_result env_id func_result)))
|
|
// ; special cases now
|
|
// ; *(veval body {env}) => (combiner_return_ok {env})
|
|
// ; The reason we don't have to check body is that this form is only creatable in ways that body was origionally a value and only need {env}
|
|
// ; Either it's created by eval, in which case it's fine, or it's created by something like (eval (array veval x de) de2) and the array has checked it,
|
|
// ; or it's created via literal vau invocation, in which case the body is a value.
|
|
// ((and (marked_array? func_result)
|
|
// (prim_comb? (idx (.marked_array_values func_result) 0))
|
|
// (= 'veval (.prim_comb_sym (idx (.marked_array_values func_result) 0)))
|
|
// (= 3 (len (.marked_array_values func_result)))
|
|
// (combiner_return_ok (idx (.marked_array_values func_result) 2) env_id)) true)
|
|
// ; (func ...params) => (and (doesn't take de func) (foldl combiner_return_ok (cons func params)))
|
|
// ;
|
|
// ((and (marked_array? func_result)
|
|
// (not (comb_takes_de? (idx (.marked_array_values func_result) 0) (len (.marked_array_values func_result))))
|
|
// (foldl (lambda (a x) (and a (combiner_return_ok x env_id))) true (.marked_array_values func_result))) true)
|
|
|
|
// ; So that's enough for macro like, but we would like to take it farther
|
|
// ; For like (let1 a 12 (wrap (vau (x) (let1 y (+ a 1) (+ y x a)))))
|
|
// ; we get to (+ 13 x 12) not being a value, and it reconstructs
|
|
// ; (<comb wraplevel=1 (y) (+ y x 12)> 13)
|
|
// ; and that's what eval gets, and eval then gives up as well.
|
|
|
|
// ; That will get caught by the above cases to remain the expansion (<comb wraplevel=1 (y) (+ y x 12)> 13),
|
|
// ; but ideally we really want another case to allow (+ 13 x 12) to bubble up
|
|
// ; I think it would be covered by the (func ...params) case if a case is added to allow symbols to be bubbled up if their
|
|
// ; needed for progress wasn't true or the current environment, BUT this doesn't work for eval, just for functions,
|
|
// ; since eval changes the entire env chain (but that goes back to case 1, and might be eliminated at compile if it's an env reachable from the func).
|
|
// ;
|
|
// ;
|
|
// ; Do note a key thing to be avoided is allowing any non-val inside a comb, since that can cause a fake env's ID to
|
|
// ; reference the wrong env/comb in the chain.
|
|
// ; We do allow calling eval with a fake env, but since it's only callable withbody value and is strict (by calling this)
|
|
// ; about it's return conditions, and the env it's called with must be ok in the chain, and eval doesn't introduce a new scope, it works ok.
|
|
// ; We do have to be careful about allowing returned later symbols from it though, since it could be an entirely different env chain.
|
|
|
|
// (true false)
|
|
// )
|
|
//))
|
|
}
|
|
|
|
pub fn partial_eval(bctx: BCtx, dctx: DCtx, x: Rc<MarkedForm>) -> Result<(BCtx,Rc<MarkedForm>), String> {
|
|
//println!("{:ident$}PE: {}", "", x, ident=dctx.ident*4);
|
|
let should_go = dctx.force || dctx.can_progress(x.ids());
|
|
if !should_go {
|
|
//println!("{:ident$}Shouldn't go!", "", ident=dctx.ident*4);
|
|
return Ok((bctx, x));
|
|
}
|
|
match &*x {
|
|
MarkedForm::SuspendedSymbol(name) => {
|
|
let mut t = Rc::clone(&dctx.e);
|
|
while name != t.car()?.car()?.sym()? {
|
|
t = t.cdr()?;
|
|
}
|
|
Ok((bctx, t.car()?.cdr()?.tag_name(name)))
|
|
},
|
|
MarkedForm::SuspendedEnvLookup { name, id } => {
|
|
if let Some(v) = dctx.sus_env_stack.get(id) {
|
|
if let Some(name) = name {
|
|
Ok((bctx, v.tag_name(name)))
|
|
} else {
|
|
Ok((bctx, Rc::clone(v)))
|
|
}
|
|
} else {
|
|
Ok((bctx, x))
|
|
}
|
|
},
|
|
MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } => {
|
|
if let Some(v) = dctx.sus_prm_stack.get(id) {
|
|
if let Some(name) = name {
|
|
Ok((bctx, v.tag_name(name)))
|
|
} else {
|
|
Ok((bctx, Rc::clone(v)))
|
|
}
|
|
} else {
|
|
Ok((bctx, x))
|
|
}
|
|
},
|
|
MarkedForm::SuspendedPair { hash, ids, attempted, car, cdr } => {
|
|
let ( bctx, mut car) = partial_eval(bctx, dctx.clone(), Rc::clone(car))?;
|
|
let (mut bctx, mut cdr) = partial_eval(bctx, dctx.clone(), Rc::clone(cdr))?;
|
|
let mut new_attempted = attempted.clone();
|
|
let mut maybe_rec_hash = None;
|
|
while let Some(wrap_level) = car.wrap_level() {
|
|
if wrap_level > 0 {
|
|
fn map_unval_peval(bctx: BCtx, dctx: DCtx, x: Rc<MarkedForm>) -> Result<(BCtx,Rc<MarkedForm>),String> {
|
|
match &*x {
|
|
MarkedForm::Pair(h, ids, x_car, x_cdr) => {
|
|
let (bctx, new_x_car) = partial_eval(bctx, dctx.clone(), x_car.unval()?)?;
|
|
let (bctx, new_x_cdr) = map_unval_peval(bctx, dctx.clone(), Rc::clone(x_cdr))?;
|
|
return Ok((bctx, MarkedForm::new_pair(new_x_car, new_x_cdr)));
|
|
},
|
|
MarkedForm::Nil => return Ok((bctx,x)),
|
|
_ => return Err("not a list".to_owned()),
|
|
}
|
|
}
|
|
if let Ok((new_bctx, new_cdr)) = map_unval_peval(bctx.clone(), dctx.clone(), Rc::clone(&cdr)) {
|
|
car = car.decrement_wrap_level().unwrap();
|
|
cdr = new_cdr;
|
|
bctx = new_bctx;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
// check to see if can do call
|
|
// not pure values are fine for -1 wraps, which we need to add? veval and vif?
|
|
if !cdr.is_value() {
|
|
break;
|
|
}
|
|
match &*car {
|
|
MarkedForm::PrimComb { name, takes_de, wrap_level, f} => {
|
|
new_attempted = Attempted::True(if *takes_de { Some(dctx.e.ids()) } else { None });
|
|
let ident_amount = dctx.ident*4;
|
|
//println!("{:ident$}doing a call eval of {}", "", name, ident=ident_amount);
|
|
match f(bctx.clone(), dctx.clone(), Rc::clone(&cdr)) {
|
|
Ok((bctx, r)) => {
|
|
match r {
|
|
PossibleMarkedTailCall::Result(result) => return Ok((bctx, result)),
|
|
// Sigh, no tail-callin right now
|
|
PossibleMarkedTailCall::TailCall(new_env, next) => {
|
|
//println!("{:ident$}doing a tail call of {} in {}", "", next, new_env, ident=ident_amount);
|
|
if let Ok((new_bctx, res)) = partial_eval(bctx.clone(), dctx.copy_set_env(&new_env), Rc::clone(&next)) {
|
|
//println!("{:ident$}doing a tail call result is {}", "", res, ident=ident_amount);
|
|
return Ok((new_bctx, res));
|
|
} else {
|
|
//println!("Tail call failed");
|
|
if new_env == dctx.e {
|
|
//println!("{:ident$}Tail call failed, but can emplace", "", ident=ident_amount);
|
|
return Ok((bctx, next));
|
|
} else {
|
|
//println!("{:ident$}Tail call failed, can't emplace", "", ident=ident_amount);
|
|
// maybe this should enplace the TailCall with an eval
|
|
break; // break out to reconstruction
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Err(msg) => {
|
|
//println!("{:ident$}failed {:?}", "", msg, ident=ident_amount);
|
|
break;
|
|
},
|
|
}
|
|
}
|
|
MarkedForm::DeriComb { hash, lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => {
|
|
new_attempted = Attempted::True(if de.is_some() { Some(dctx.e.ids()) } else { None });
|
|
// not yet supporting sequence params
|
|
// needs to check hash
|
|
match dctx.copy_push_frame(id.clone(), &se, &de, Some(Rc::clone(&dctx.e)), &rest_params, Some(Rc::clone(&cdr)), body) {
|
|
Ok(inner_dctx) => {
|
|
//println!("{:ident$}doing a call eval of {} in {}", "", body, inner_dctx.e, ident=inner_dctx.ident*4);
|
|
let ident_amount = inner_dctx.ident*4;
|
|
//println!("{:ident$}doing a call eval of {:?}", "", lookup_name, ident=ident_amount);
|
|
//if let Ok((bctx, r)) = partial_eval(bctx.clone(), inner_dctx, Rc::clone(body)) {
|
|
// if combiner_return_ok(Rc::clone(&r), id.clone()) {
|
|
// return Ok((bctx, r));
|
|
// }
|
|
//}
|
|
match partial_eval(bctx.clone(), inner_dctx, Rc::clone(body)) {
|
|
Ok((bctx, r)) => {
|
|
if combiner_return_ok(Rc::clone(&r), id.clone()) {
|
|
return Ok((bctx, r));
|
|
} else {
|
|
//println!("{:ident$}combiner return not ok {}", "", r, ident=ident_amount);
|
|
}
|
|
}
|
|
Err(msg) => {
|
|
//println!("{:ident$}failed {:?}", "", msg, ident=ident_amount);
|
|
}
|
|
}
|
|
},
|
|
Err(rec_stop_hash) => {
|
|
maybe_rec_hash = Some(rec_stop_hash);
|
|
},
|
|
}
|
|
break; // failed call for one reason or the other
|
|
},
|
|
_ => break,
|
|
}
|
|
}
|
|
}
|
|
Ok((bctx, MarkedForm::new_suspended_pair( new_attempted, car, cdr, maybe_rec_hash )))
|
|
},
|
|
MarkedForm::Pair(h,ids,car,cdr) => {
|
|
let (bctx, car) = partial_eval(bctx, dctx.clone(), Rc::clone(car))?;
|
|
let (bctx, cdr) = partial_eval(bctx, dctx, Rc::clone(cdr))?;
|
|
Ok((bctx, MarkedForm::new_pair(car, cdr)))
|
|
},
|
|
MarkedForm::DeriComb { hash, lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => {
|
|
if !se.ids().needs_nothing() {
|
|
// the current env is our new se
|
|
let se = Rc::clone(&dctx.e);
|
|
let inner_dctx = dctx.copy_push_frame(id.clone(), &se, &de, None, &rest_params, None, body).expect("Not sure if this can ever fail or not... maybe for Y comb recursion?");
|
|
let (bctx, body) = partial_eval(bctx, inner_dctx, Rc::clone(&body))?;
|
|
Ok((bctx, MarkedForm::new_deri_comb( se, lookup_name.clone(), de.clone(), id.clone(), *wrap_level, sequence_params.clone(), rest_params.clone(), body )))
|
|
} else {
|
|
Ok((bctx, x))
|
|
}
|
|
},
|
|
MarkedForm::PrimComb { .. } => Ok((bctx, x)),
|
|
_ => Ok((bctx, x)),
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
|
pub enum Attempted {
|
|
True(Option<NeededIds>),
|
|
False,
|
|
}
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub enum MarkedForm {
|
|
Nil,
|
|
Int(i32),
|
|
Bool(bool),
|
|
Symbol(String),
|
|
Pair(MFHash, NeededIds, Rc<MarkedForm>,Rc<MarkedForm>),
|
|
|
|
SuspendedSymbol(String),
|
|
SuspendedParamLookup { name: Option<String>, id: EnvID, cdr_num: i32, car: bool },
|
|
SuspendedEnvLookup { name: Option<String>, id: EnvID },
|
|
// resume hash is folded into ids
|
|
SuspendedPair { hash: MFHash, ids: NeededIds, attempted: Attempted, car: Rc<MarkedForm>, cdr: Rc<MarkedForm>},
|
|
|
|
PrimComb { name: String, takes_de: bool, wrap_level: i32, f: fn(BCtx,DCtx,Rc<MarkedForm>) -> Result<(BCtx,PossibleMarkedTailCall),String> },
|
|
DeriComb { hash: MFHash, lookup_name: Option<String>, ids: NeededIds, se: Rc<MarkedForm>, de: Option<String>, id: EnvID, wrap_level: i32, sequence_params: Vec<String>, rest_params: Option<String>, body: Rc<MarkedForm> },
|
|
}
|
|
impl MarkedForm {
|
|
pub fn new_pair(car: Rc<MarkedForm>, cdr: Rc<MarkedForm>) -> Rc<MarkedForm> {
|
|
let mut h = DefaultHasher::new();
|
|
"Pair(ids,car,cdr)".hash(&mut h); car.hash().hash(&mut h); cdr.hash().hash(&mut h);
|
|
Rc::new(MarkedForm::Pair(MFHash(h.finish()), car.ids().union(&cdr.ids()), car, cdr))
|
|
}
|
|
pub fn new_suspended_pair(attempted: Attempted, car: Rc<MarkedForm>, cdr: Rc<MarkedForm>, rec_hash: Option<MFHash>) -> Rc<MarkedForm> {
|
|
let mut h = DefaultHasher::new();
|
|
"SuspendedPair".hash(&mut h); attempted.hash(&mut h); car.hash().hash(&mut h); cdr.hash().hash(&mut h);
|
|
let ids = car.ids().union(&cdr.ids());
|
|
let ids = match ids {
|
|
NeededIds::True(_) => ids,
|
|
NeededIds::None(hashes) => match &attempted {
|
|
Attempted::False => NeededIds::True(hashes),
|
|
Attempted::True(Some(oids)) => oids.union_hashes(&hashes),
|
|
Attempted::True(None) => NeededIds::None(hashes),
|
|
},
|
|
NeededIds::Some(_,_) => ids,
|
|
};
|
|
if let Some(rec_hash) = rec_hash {
|
|
ids.add_hash(rec_hash);
|
|
}
|
|
Rc::new(MarkedForm::SuspendedPair{ hash: MFHash(h.finish()), attempted, ids, car, cdr })
|
|
}
|
|
pub fn new_deri_comb(se: Rc<MarkedForm>, lookup_name: Option<String>, de: Option<String>, id: EnvID, wrap_level: i32, sequence_params: Vec<String>, rest_params: Option<String>, body: Rc<MarkedForm>) -> Rc<MarkedForm> {
|
|
let mut h = DefaultHasher::new();
|
|
"DeriComb".hash(&mut h); se.hash().hash(&mut h); de.hash(&mut h); id.hash(&mut h); wrap_level.hash(&mut h);
|
|
sequence_params.hash(&mut h); rest_params.hash(&mut h); body.hash().hash(&mut h);
|
|
let ids = se.ids().union_without(&body.ids(), id.clone());
|
|
Rc::new(MarkedForm::DeriComb{ hash: MFHash(h.finish()), lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body })
|
|
}
|
|
pub fn tag_name(self: &Rc<MarkedForm>, name: &str) -> Rc<MarkedForm> {
|
|
match &**self {
|
|
MarkedForm::DeriComb { hash, lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } =>
|
|
MarkedForm::new_deri_comb(Rc::clone(se), Some(name.to_owned()), de.clone(), id.clone(), *wrap_level, sequence_params.clone(), rest_params.clone(), Rc::clone(body)),
|
|
_ => Rc::clone(self),
|
|
}
|
|
}
|
|
pub fn hash(&self) -> MFHash {
|
|
let mut h = DefaultHasher::new();
|
|
{
|
|
let state = &mut h;
|
|
match self {
|
|
MarkedForm::Nil => { "Nil".hash(state); },
|
|
MarkedForm::Int(i) => { "Int(i)".hash(state); i.hash(state); },
|
|
MarkedForm::Bool(b) => { "Bool(b)".hash(state); b.hash(state); },
|
|
MarkedForm::Symbol(s) => { "Symbol(s)".hash(state); s.hash(state); },
|
|
MarkedForm::Pair(hash,ids,car,cdr) => { return *hash; },
|
|
MarkedForm::SuspendedSymbol(name) => { "SuspendedSymbol(name)".hash(state); name.hash(state); },
|
|
MarkedForm::SuspendedParamLookup { name, id, cdr_num, car }=> { "SuspendedParamLookup".hash(state); name.hash(state); id.hash(state); cdr_num.hash(state); car.hash(state); },
|
|
MarkedForm::SuspendedEnvLookup { name, id } => { "SuspendedEnvLookup".hash(state); name.hash(state); id.hash(state); },
|
|
MarkedForm::SuspendedPair{ hash, .. } => { return *hash; },
|
|
MarkedForm::PrimComb { name, wrap_level, .. } => { "PrimComb".hash(state); name.hash(state); wrap_level.hash(state); },
|
|
MarkedForm::DeriComb { hash, .. } => { return *hash; },
|
|
}
|
|
}
|
|
MFHash(h.finish())
|
|
}
|
|
pub fn wrap_level(&self) -> Option<i32> {
|
|
match self {
|
|
MarkedForm::PrimComb { wrap_level, .. } => Some(*wrap_level),
|
|
MarkedForm::DeriComb { wrap_level, .. } => Some(*wrap_level),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn decrement_wrap_level(&self) -> Option<Rc<Self>> {
|
|
match self {
|
|
MarkedForm::PrimComb { name, takes_de, wrap_level, f } => Some(Rc::new(MarkedForm::PrimComb { name: name.clone(), takes_de: *takes_de, wrap_level: wrap_level-1, f: *f })),
|
|
MarkedForm::DeriComb { hash, lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => Some(MarkedForm::new_deri_comb(Rc::clone(se), lookup_name.clone(), de.clone(), id.clone(), wrap_level-1, sequence_params.clone(), rest_params.clone(), Rc::clone(body))),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn ids(&self) -> NeededIds {
|
|
match self {
|
|
MarkedForm::Nil => NeededIds::new_none(),
|
|
MarkedForm::Int(i) => NeededIds::new_none(),
|
|
MarkedForm::Bool(b) => NeededIds::new_none(),
|
|
MarkedForm::Symbol(s) => NeededIds::new_none(),
|
|
MarkedForm::Pair(hash,ids,car,cdr) => ids.clone(),
|
|
MarkedForm::SuspendedSymbol(name) => NeededIds::new_true(),
|
|
MarkedForm::SuspendedEnvLookup { id, .. } => NeededIds::new_single(id.clone()),
|
|
MarkedForm::SuspendedParamLookup { id, .. } => NeededIds::new_single(id.clone()),
|
|
MarkedForm::SuspendedPair{ ids, .. } => ids.clone(),
|
|
MarkedForm::PrimComb { .. } => NeededIds::new_none(),
|
|
MarkedForm::DeriComb { ids, .. } => ids.clone(),
|
|
}
|
|
}
|
|
// TODO: this might be essentially the same as NeededIds.nothing_needed()
|
|
pub fn is_value(&self) -> bool {
|
|
match match self {
|
|
MarkedForm::Nil => return true,
|
|
MarkedForm::Int(i) => return true,
|
|
MarkedForm::Bool(b) => return true,
|
|
MarkedForm::Symbol(s) => return true,
|
|
MarkedForm::SuspendedSymbol(name) => return false,
|
|
MarkedForm::SuspendedEnvLookup { id, .. } => return false,
|
|
MarkedForm::SuspendedParamLookup { id, .. } => return false,
|
|
MarkedForm::SuspendedPair{ ids, .. } => return false,
|
|
MarkedForm::PrimComb { .. } => return true,
|
|
MarkedForm::Pair(hash,ids,car,cdr) => ids.clone(),
|
|
MarkedForm::DeriComb { ids, .. } => ids.clone(),
|
|
} {
|
|
NeededIds::True(hashes) => false,
|
|
NeededIds::None(hashes) => true,
|
|
NeededIds::Some(ids,hashes) => false,
|
|
}
|
|
}
|
|
pub fn unval(self: &Rc<MarkedForm>) -> Result<Rc<MarkedForm>, &'static str> {
|
|
match &**self {
|
|
MarkedForm::Nil => Ok(Rc::clone(self)),
|
|
MarkedForm::Int(i) => Ok(Rc::clone(self)),
|
|
MarkedForm::Bool(b) => Ok(Rc::clone(self)),
|
|
MarkedForm::Symbol(s) => Ok(Rc::new(MarkedForm::SuspendedSymbol(s.clone()))),
|
|
MarkedForm::Pair(hash,ids,car,cdr) => Ok(MarkedForm::new_suspended_pair( Attempted::False, car.unval()?, Rc::clone(cdr), None)),
|
|
MarkedForm::SuspendedSymbol(name) => Err("trying to unval a suspended symbol"),
|
|
MarkedForm::SuspendedEnvLookup { .. } => Err("trying to unval a suspended env lookup"),
|
|
MarkedForm::SuspendedParamLookup { .. } => Err("trying to unval a suspended param lookup"),
|
|
MarkedForm::SuspendedPair{ ids, .. } => Err("trying to unval a suspended pair"),
|
|
MarkedForm::PrimComb { .. } => Ok(Rc::clone(self)),
|
|
MarkedForm::DeriComb { .. } => Ok(Rc::clone(self)),
|
|
}
|
|
}
|
|
pub fn truthy(&self) -> Result<bool,&'static str> {
|
|
match self {
|
|
MarkedForm::Nil => Ok(false),
|
|
MarkedForm::Int(i) => Ok(true),
|
|
MarkedForm::Bool(b) => Ok(*b),
|
|
MarkedForm::Symbol(s) => Ok(true),
|
|
MarkedForm::Pair(hash,ids,car,cdr) => Ok(true),
|
|
MarkedForm::SuspendedSymbol(name) => Err("trying to truthy a suspended symbol"),
|
|
MarkedForm::SuspendedEnvLookup { .. } => Err("trying to truthy a suspended env lookup"),
|
|
MarkedForm::SuspendedParamLookup { .. } => Err("trying to truthy a suspended param lookup"),
|
|
MarkedForm::SuspendedPair{ ids, .. } => Err("trying to truthy a suspended pair"),
|
|
MarkedForm::PrimComb { .. } => Ok(true),
|
|
MarkedForm::DeriComb { .. } => Ok(true),
|
|
}
|
|
}
|
|
pub fn sym(&self) -> Result<&str,&'static str> {
|
|
match self {
|
|
MarkedForm::Symbol(s) => Ok(s),
|
|
_ => Err("not a symbol"),
|
|
}
|
|
}
|
|
pub fn int(&self) -> Result<i32,&'static str> {
|
|
match self {
|
|
MarkedForm::Int(i) => Ok(*i),
|
|
_ => Err("not a int"),
|
|
}
|
|
}
|
|
pub fn car(&self) -> Result<Rc<MarkedForm>, &'static str> {
|
|
match self {
|
|
MarkedForm::Pair(hash,ids,car,cdr) => Ok(Rc::clone(car)),
|
|
_ => Err("not a pair"),
|
|
}
|
|
}
|
|
pub fn cdr(&self) -> Result<Rc<MarkedForm>, &'static str> {
|
|
match self {
|
|
MarkedForm::Pair(hash,ids,car,cdr) => Ok(Rc::clone(cdr)),
|
|
_ => Err("not a pair"),
|
|
}
|
|
}
|
|
}
|
|
impl fmt::Display for MarkedForm {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
MarkedForm::Nil => write!(f, "nil"),
|
|
MarkedForm::Int(i) => write!(f, "{}", i),
|
|
MarkedForm::Bool(b) => write!(f, "{}", b),
|
|
MarkedForm::Symbol(s) => write!(f, "{}", s),
|
|
MarkedForm::Pair(hash, ids, car, cdr) => {
|
|
//write!(f, "{:?}#({}", ids, car)?;
|
|
write!(f, "({}", car)?;
|
|
let mut traverse: Rc<MarkedForm> = Rc::clone(cdr);
|
|
loop {
|
|
match &*traverse {
|
|
MarkedForm::Pair(ref hash, ref ids, ref carp, ref cdrp) => {
|
|
write!(f, " {}", carp)?;
|
|
traverse = Rc::clone(cdrp);
|
|
},
|
|
MarkedForm::Nil => {
|
|
write!(f, ")")?;
|
|
return Ok(());
|
|
},
|
|
x => {
|
|
write!(f, ". {})", x)?;
|
|
return Ok(());
|
|
},
|
|
}
|
|
}
|
|
},
|
|
MarkedForm::SuspendedSymbol(name) => write!(f, "{}", name),
|
|
MarkedForm::SuspendedEnvLookup { name, id } => write!(f, "{:?}({:?}env)", name, id),
|
|
MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } => write!(f, "{:?}({:?}{}{})", name, id, cdr_num, car),
|
|
MarkedForm::PrimComb { name, wrap_level, .. } => write!(f, "<{}{}>", name, wrap_level),
|
|
|
|
//MarkedForm::DeriComb { ids, se, de, id, wrap_level, sequence_params, rest_params, body } => write!(f, "{:?}#[{}/{:?}/{:?}/{}/{:?}/{:?}/{}]", ids, se, de, id, wrap_level, sequence_params, rest_params, body),
|
|
MarkedForm::DeriComb { hash, lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => write!(f, "{:?}#[{:?}/{:?}/{:?}/{}/{:?}/{:?}/{}]", ids, lookup_name, de, id, wrap_level, sequence_params, rest_params, body),
|
|
|
|
MarkedForm::SuspendedPair{ hash, ids, attempted, car, cdr } => {
|
|
//write!(f, "{:?}{:?}#{{{}", ids, attempted, car)?;
|
|
write!(f, "{{{}", car)?;
|
|
let mut traverse: Rc<MarkedForm> = Rc::clone(cdr);
|
|
loop {
|
|
match &*traverse {
|
|
MarkedForm::Pair(ref hash, ref ids, ref carp, ref cdrp) => {
|
|
write!(f, " {}", carp)?;
|
|
traverse = Rc::clone(cdrp);
|
|
},
|
|
MarkedForm::Nil => {
|
|
write!(f, "}}")?;
|
|
return Ok(());
|
|
},
|
|
x => {
|
|
write!(f, ". {}}}", x)?;
|
|
return Ok(());
|
|
},
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn eval(e: Rc<Form>, f: Rc<Form>) -> Rc<Form> {
|
|
let mut e = e;
|
|
let mut x = Option::Some(f);
|
|
loop {
|
|
let cur = x.take().unwrap();
|
|
//println!("Evaluating {:?} in {:?}", cur, e);
|
|
match *cur {
|
|
Form::Symbol(ref s) => {
|
|
let mut t = e;
|
|
//println!("Looking up {} in {:?}", s, t);
|
|
//println!("Looking up {}", s);
|
|
while s != t.car().unwrap().car().unwrap().sym().unwrap() {
|
|
t = t.cdr().unwrap();
|
|
}
|
|
return t.car().unwrap().cdr().unwrap();
|
|
},
|
|
Form::Pair(ref c, ref p) => {
|
|
let comb = eval(Rc::clone(&e), Rc::clone(c));
|
|
match *comb {
|
|
Form::PrimComb(ref _n, ref f) => match f(e, Rc::clone(p)) {
|
|
PossibleTailCall::Result(r) => return r,
|
|
PossibleTailCall::TailCall(ne, nx) => {
|
|
e = ne;
|
|
x = Some(nx);
|
|
},
|
|
},
|
|
Form::DeriComb{ref se, ref de, ref params, ref body } => {
|
|
let mut new_e = Rc::clone(se);
|
|
if let Some(de) = de {
|
|
new_e = assoc(de, Rc::clone(&e), new_e);
|
|
}
|
|
new_e = assoc(params, Rc::clone(p), new_e);
|
|
// always a tail call
|
|
e = new_e;
|
|
x = Some(Rc::clone(body));
|
|
},
|
|
_ => panic!("Tried to call not a Prim/DeriComb {:?}", comb),
|
|
}
|
|
},
|
|
_ => return cur,
|
|
}
|
|
}
|
|
}
|
|
fn massoc(k: &str, v: Rc<MarkedForm>, l: Rc<MarkedForm>) -> Rc<MarkedForm> {
|
|
MarkedForm::new_pair(
|
|
MarkedForm::new_pair(
|
|
Rc::new(MarkedForm::Symbol(k.to_owned())),
|
|
v),
|
|
l)
|
|
}
|
|
fn assoc(k: &str, v: Rc<Form>, l: Rc<Form>) -> Rc<Form> {
|
|
Rc::new(Form::Pair(
|
|
Rc::new(Form::Pair(
|
|
Rc::new(Form::Symbol(k.to_owned())),
|
|
v)),
|
|
l))
|
|
}
|
|
fn assoc_vec(kvs: Vec<(&str, Rc<Form>)>) -> Rc<Form> {
|
|
let mut to_ret = Rc::new(Form::Nil);
|
|
for (k, v) in kvs {
|
|
to_ret = assoc(k, v, to_ret);
|
|
}
|
|
to_ret
|
|
}
|
|
|
|
pub fn root_env() -> Rc<Form> {
|
|
assoc_vec(vec![
|
|
// TODO: Should be properly tail recursive
|
|
("eval", Rc::new(Form::PrimComb("eval".to_owned(), |e, p| {
|
|
let b = eval(Rc::clone(&e), p.car().unwrap());
|
|
let e = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::TailCall(e, b)
|
|
}))),
|
|
// (vau de params body)
|
|
("vau", Rc::new(Form::PrimComb("vau".to_owned(), |e, p| {
|
|
let de = p.car().unwrap().sym().map(|s| s.to_owned());
|
|
let params = p.cdr().unwrap().car().unwrap().sym().unwrap().to_owned();
|
|
let body = p.cdr().unwrap().cdr().unwrap().car().unwrap();
|
|
|
|
PossibleTailCall::Result(Rc::new(Form::DeriComb { se: e, de, params, body }))
|
|
}))),
|
|
("=", Rc::new(Form::PrimComb("=".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap());
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(a == b)))
|
|
}))),
|
|
("<", Rc::new(Form::PrimComb("<".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap());
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(a.int().unwrap() < b.int().unwrap())))
|
|
}))),
|
|
(">", Rc::new(Form::PrimComb(">".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap());
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(a.int().unwrap() > b.int().unwrap())))
|
|
}))),
|
|
("<=", Rc::new(Form::PrimComb("<=".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap());
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(a.int().unwrap() <= b.int().unwrap())))
|
|
}))),
|
|
(">=", Rc::new(Form::PrimComb(">=".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap());
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(a.int().unwrap() >= b.int().unwrap())))
|
|
}))),
|
|
("if", Rc::new(Form::PrimComb("if".to_owned(), |e, p| {
|
|
if eval(Rc::clone(&e), p.car().unwrap()).truthy() {
|
|
PossibleTailCall::TailCall(e, p.cdr().unwrap().car().unwrap())
|
|
} else {
|
|
PossibleTailCall::TailCall(e, p.cdr().unwrap().cdr().unwrap().car().unwrap())
|
|
}
|
|
}))),
|
|
("cons", Rc::new(Form::PrimComb("cons".to_owned(), |e, p| {
|
|
let h = eval(Rc::clone(&e), p.car().unwrap());
|
|
let t = eval(e, p.cdr().unwrap().car().unwrap());
|
|
PossibleTailCall::Result(Rc::new(Form::Pair(h, t)))
|
|
}))),
|
|
("car", Rc::new(Form::PrimComb("car".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(eval(Rc::clone(&e), p.car().unwrap()).car().unwrap())
|
|
}))),
|
|
("cdr", Rc::new(Form::PrimComb("cdr".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(eval(Rc::clone(&e), p.car().unwrap()).cdr().unwrap())
|
|
}))),
|
|
("quote", Rc::new(Form::PrimComb("quote".to_owned(), |_e, p| {
|
|
PossibleTailCall::Result(p.car().unwrap())
|
|
}))),
|
|
|
|
("debug", Rc::new(Form::PrimComb("debug".to_owned(), |e, p| {
|
|
//println!("Debug: {:?}", eval(Rc::clone(&e), p.car().unwrap()));
|
|
println!("Debug: {}", eval(Rc::clone(&e), p.car().unwrap()));
|
|
PossibleTailCall::TailCall(e, p.cdr().unwrap().car().unwrap())
|
|
}))),
|
|
("assert", Rc::new(Form::PrimComb("assert".to_owned(), |e, p| {
|
|
let thing = eval(Rc::clone(&e), p.car().unwrap());
|
|
if !thing.truthy() {
|
|
println!("Assert failed: {:?}", thing);
|
|
}
|
|
assert!(thing.truthy());
|
|
PossibleTailCall::TailCall(e, p.cdr().unwrap().car().unwrap())
|
|
}))),
|
|
|
|
("+", Rc::new(Form::PrimComb("+".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a + b)))
|
|
}))),
|
|
("-", Rc::new(Form::PrimComb("-".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a - b)))
|
|
}))),
|
|
("*", Rc::new(Form::PrimComb("*".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a * b)))
|
|
}))),
|
|
("/", Rc::new(Form::PrimComb("/".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a / b)))
|
|
}))),
|
|
("%", Rc::new(Form::PrimComb("%".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a % b)))
|
|
}))),
|
|
("&", Rc::new(Form::PrimComb("&".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a & b)))
|
|
}))),
|
|
("|", Rc::new(Form::PrimComb("|".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a | b)))
|
|
}))),
|
|
("^", Rc::new(Form::PrimComb("^".to_owned(), |e, p| {
|
|
let a = eval(Rc::clone(&e), p.car().unwrap()).int().unwrap();
|
|
let b = eval(e, p.cdr().unwrap().car().unwrap()).int().unwrap();
|
|
PossibleTailCall::Result(Rc::new(Form::Int(a ^ b)))
|
|
}))),
|
|
|
|
("comb?", Rc::new(Form::PrimComb("comb?".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
|
|
Form::PrimComb(_n, _f) => true,
|
|
Form::DeriComb { .. } => true,
|
|
_ => false,
|
|
})))
|
|
}))),
|
|
("pair?", Rc::new(Form::PrimComb("pair?".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
|
|
Form::Pair(_a,_b) => true,
|
|
_ => false,
|
|
})))
|
|
}))),
|
|
("symbol?", Rc::new(Form::PrimComb("symbol?".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
|
|
Form::Symbol(_) => true,
|
|
_ => false,
|
|
})))
|
|
}))),
|
|
("int?", Rc::new(Form::PrimComb("int?".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
|
|
Form::Int(_) => true,
|
|
_ => false,
|
|
})))
|
|
}))),
|
|
// maybe bool? but also could be derived. Nil def
|
|
("bool?", Rc::new(Form::PrimComb("bool?".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
|
|
Form::Bool(_) => true,
|
|
_ => false,
|
|
})))
|
|
}))),
|
|
("nil?", Rc::new(Form::PrimComb("nil?".to_owned(), |e, p| {
|
|
PossibleTailCall::Result(Rc::new(Form::Bool(match &*eval(e, p.car().unwrap()) {
|
|
Form::Nil => true,
|
|
_ => false,
|
|
})))
|
|
}))),
|
|
|
|
// consts
|
|
("true", Rc::new(Form::Bool(true))),
|
|
("false", Rc::new(Form::Bool(false))),
|
|
("nil", Rc::new(Form::Nil)),
|
|
])
|
|
}
|