From 7b334e96df5479058fcfc488820542e0d968ad24 Mon Sep 17 00:00:00 2001 From: Nathan Braswell Date: Sun, 19 Mar 2023 00:59:46 -0400 Subject: [PATCH] Finally finished sketching out the new version, lets get on the compile errors --- README.md | 2 +- kr/Cargo.toml | 1 + kr/src/pe_ast.rs | 644 ++++++++++++++++++++++++----------------------- 3 files changed, 334 insertions(+), 313 deletions(-) diff --git a/README.md b/README.md index 7796725..84a0763 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ with partial evaluation during compilation to make it efficient. koka_bench: Licensed under Apache-2.0, as they are derived from the benchmarks of the Koka project, see the readme and license in koka_bench for more, or https://github.com/koka-lang/koka for the source project. -Kraken (everything else besides the benchmarks in koka_bench) Licensed under +Kraken (everything else besides the benchmarks in koka_bench, and Recursive/CodeJar/Highlight.js in website/) Licensed under SPDX-License-Identifier: BSD-2-Clause-Patent Note: This license is designed to provide: a) a simple permissive license; b) that is compatible with the GNU General Public License (GPL), version 2; and c) which also has an express patent grant included. diff --git a/kr/Cargo.toml b/kr/Cargo.toml index 13c10f6..e0d05fe 100644 --- a/kr/Cargo.toml +++ b/kr/Cargo.toml @@ -10,6 +10,7 @@ build = "build.rs" lalrpop-util = {version="0.19.7", features=["lexer"]} regex = "1" once_cell = "1.17.0" +anyhow = "1" [build-dependencies] lalrpop = "0.19.7" diff --git a/kr/src/pe_ast.rs b/kr/src/pe_ast.rs index 42d899a..499c18e 100644 --- a/kr/src/pe_ast.rs +++ b/kr/src/pe_ast.rs @@ -2,11 +2,13 @@ use std::fmt; use std::rc::Rc; use std::convert::From; use std::collections::{BTreeSet,BTreeMap}; -use std::result::Result; +use std::result; use std::iter; use crate::ast::{root_env,Form}; +use anyhow::{anyhow, bail, Result, Context}; + fn massoc(k: &str, v: Rc, l: Rc) -> Rc { MarkedForm::new_pair( @@ -61,15 +63,17 @@ fn massoc(k: &str, v: Rc, l: Rc) -> Rc { * non values that would be looked up in the upper env are itneresting, as I don't think we can tell... * wait no it's always ok to unwrap suspended_eval unless there is a True remaining * the Env or the subvalue would have been captured via partial eval + * ALSO UNLESS BELOW * * Also, I think it can only have a true if it hasn't been evaluated yet * technically, the env could end in a suspended-param or suspended-env * make sure the lookup accounts for that, that's a *WEIRD* one - * + * can't unwrap if it ends in a lookup, & that lookup ID is contained in the body * * ALL THE ENV reasoning only holds if ENVs are just cons chains with supsended params or suspended env params * * suspended if needs special gating head|tail attention + * mainly if rec-stopped, cond & rec takes head, relegating rest * * for suspended calls, the rec_mapping function needs to differentiate between a parameter * failing because of an error and failing because of a needed ID @@ -83,7 +87,6 @@ fn massoc(k: &str, v: Rc, l: Rc) -> Rc { * it's a bit trickey! * The result of a DeriCall's IDs, if not a value, needs to become (part of, depending on if non-val parameters are allowed) the SuspendedCall's head IDs * ditto for rec-stop, but the call-under bit - * */ // 0 is equiv of true, -1 is equiv to runtime @@ -105,28 +108,27 @@ impl NeededIds { fn new_true() -> Self { NeededIds { heads: iter::once(true_id).collect(), tails: BTreeSet::new(), body_stopped: BTreeSet::new(), if_stopped: BTreeSet::new() } fn new_none() -> Self { NeededIds { heads: BTreeSet::new(), tails: BTreeSet::new(), body_stopped: BTreeSet::new(), if_stopped: BTreeSet::new() } } fn new_single(i: EnvID) -> Self { NeededIds { heads: iter::once(i).collect(), tails: BTreeSet::new(), body_stopped: BTreeSet::new(), if_stopped: BTreeSet::new() } } - fn needs_nothing(&self) -> bool { heads.empty() && tails.empty() && body_stopped.empty() && if_stopped.empty() } fn union(&self, other: &NeededIds) -> Self { NeededIds { - heads: self.heads.union(&other.heads).cloned().collect(), - tails: self.tails.union(&other.tails).cloned().collect(), - body_stopped: self.body_stopped.union(&other.body_stopped).cloned().collect(), - if_stopped: self.if_stopped.union(&other.if_stopped).cloned().collect(), + heads: self.heads.union(&other.heads).cloned().collect(), + tails: self.tails.union(&other.tails).cloned().collect(), + body_stopped: self.body_stopped.union(&other.body_stopped).cloned().collect(), + if_stopped: self.if_stopped.union(&other.if_stopped).cloned().collect(), } } - fn union_without(&self, other: &NeededIds, without: EnvID) -> Self { + fn union_without(&self, other: &NeededIds, without: &EnvID) -> Self { NeededIds { - heads: self.heads.union(&other.heads) .filter(|&x| x != without).cloned().collect(), - tails: self.tails.union(&other.tails) .filter(|&x| x != without).cloned().collect(), - body_stopped: self.body_stopped.union(&other.body_stopped).filter(|&x| x != without).cloned().collect(), - if_stopped: self.if_stopped.union(&other.if_stopped) .filter(|&x| x != without).cloned().collect(), + heads: self.heads.union(&other.heads) .filter(|x| x != without).cloned().collect(), + tails: self.tails.union(&other.tails) .filter(|x| x != without).cloned().collect(), + body_stopped: self.body_stopped.union(&other.body_stopped).filter(|x| x != without).cloned().collect(), + if_stopped: self.if_stopped.union(&other.if_stopped) .filter(|x| x != without).cloned().collect(), } } fn union_into_tail(&self, ids: &NeededIds) -> Self { NeededIds { - heads: self.heads.clone() - tails: self.tails.union(&other.heads).chain(other.tails.iter()).cloned().collect(), - body_stopped: self.body_stopped.clone(), + heads: self.heads.clone() + tails: self.tails.union(&other.heads).chain(other.tails.iter()).cloned().collect(), + body_stopped: self.body_stopped.clone(), if_stopped: self.if_stopped.clone(), } } @@ -169,6 +171,9 @@ impl NeededIds { fn may_contain_id(&self, i: EnvID) -> bool { self.heads.contains(&i) || self.tails.contains(&i) } + fn contains_if_stop(&self, i: &EnvID) -> bool { + self.if_stopped.contains(i) + } } // 0 is equiv of true, -1 is equiv to runtime @@ -184,6 +189,11 @@ impl BCtx { } } +enum PushFrameResult { + Ok(Dctx), + UnderBody(EnvID), + UnderIf(EnvID), +} // memo is only for recording currently executing hashes (calls and if's, current for us) // only_head is not currently used @@ -200,16 +210,19 @@ pub struct DCtx { } impl DCtx { pub fn copy_set_env(&self, e: &Rc) -> Self { - DCtx { e: Rc::clone(e), current_id: self.current_id.clone(), 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), fake_set: Rc::clone(&self.fake_set), fake_if_set: Rc::clone(&self.fake_if_set), ident: self.ident+1 } + DCtx { e: Rc::clone(e), current_id: self.current_id.clone(), + 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), fake_set: Rc::clone(&self.fake_set), fake_if_set: Rc::clone(&self.fake_if_set), ident: self.ident+1 } } - pub fn copy_push_frame(&self, id: EnvID, se: &Rc, de: &Option, e: Option>, rest_params: &Option, prms: Option>, body: &Rc) -> Result { + pub fn copy_push_frame(&self, id: EnvID, se: &Rc, de: &Option, e: Option>, + rest_params: &Option, prms: Option>, body: &Rc) -> PushFrameResult { 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 = (*self.real_set).clone(); let mut fake_set = (*self.fake_set).clone(); if self.fake_if_set.contains(&id) { println!("Fake if real rec stopper"); - return Err(id); + return PushFrameResult::UnderIf(id); } if (e.is_some() || prms.is_some()) { real_set.insert(id.clone()); @@ -217,7 +230,7 @@ impl DCtx { //fake_set.remove(&id); } else { if fake_set.contains(&id) { - return Err(id.clone()); + return PushFrameResult::UnderBody(id.clone()); } fake_set.insert(id.clone()); real_set.remove(&id); @@ -243,38 +256,33 @@ impl DCtx { }; massoc(p, p_val, inner_env) } else { inner_env }; - Ok(DCtx { e: inner_env, current_id: Some(id), sus_env_stack, sus_prm_stack, real_set: Rc::new(real_set), fake_set: Rc::new(fake_set), fake_if_set: Rc::clone(&self.fake_if_set), ident: self.ident+1 }) + PushFrameResult::Ok(DCtx { e: inner_env, current_id: Some(id), sus_env_stack, sus_prm_stack, + real_set: Rc::new(real_set), fake_set: Rc::new(fake_set), fake_if_set: Rc::clone(&self.fake_if_set), ident: self.ident+1 }) } - pub fn copy_push_fake_if(&self) -> Self { - let new_fake_if_set = if let Some(current_id) = self.current_id.as_ref() { + pub fn copy_push_fake_if(&self) -> (Option, Self) { + let (could_stop, new_fake_if_set) = if let Some(current_id) = self.current_id.as_ref() { let mut x = (*self.fake_if_set).clone(); + let could_stop = if !x.contains(current_id) { Some(current_id.clone()) } else { None }; x.insert(current_id.clone()); - Rc::new(x) - } else { Rc::clone(&self.fake_if_set) }; - DCtx { e: Rc::clone(&self.e), current_id: self.current_id.clone(), 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), fake_set: Rc::clone(&self.fake_set), fake_if_set: new_fake_if_set, ident: self.ident+1 } + (could_stop, Rc::new(x)) + } else { (None, Rc::clone(&self.fake_if_set)) }; + (could_stop, DCtx { e: Rc::clone(&self.e), current_id: self.current_id.clone(), 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), fake_set: Rc::clone(&self.fake_set), fake_if_set: new_fake_if_set, 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( under) => under.is_empty() || (!(self.fake_set.union(&self.fake_if_set).cloned().collect::>()).is_superset(&under)), //true, - if we have hashes, that means we don't know what's in but can't progress b/c hashes - NeededIds::None( under) => !(self.fake_set.union(&self.fake_if_set).cloned().collect::>()).is_superset(&under), - NeededIds::Some(ids,under) => (!self.real_set.is_disjoint(&ids)) || (!(self.fake_set.union(&self.fake_if_set).cloned().collect::>()).is_superset(&under)), - } + ids.heads.contains(&true_id) || !self.real_set.is_disjoint(&ids.heads) || !self.fake_set.is_superset(&ids.body_stopped) || !self.fake_if_set.is_superset(&ids.if_stopped) } } pub fn new_base_ctxs() -> (BCtx,DCtx) { let bctx = BCtx { id_counter: true_id.0 + 1 }; let (bctx, root_env) = mark(root_env(), bctx); - (bctx, DCtx { e: root_env, current_id: None, sus_env_stack: Rc::new(BTreeMap::new()), sus_prm_stack: Rc::new(BTreeMap::new()), real_set: Rc::new(BTreeSet::new()), fake_set: Rc::new(BTreeSet::new()), fake_if_set: Rc::new(BTreeSet::new()), ident: 0 } ) + (bctx, DCtx { e: root_env, current_id: None, sus_env_stack: Rc::new(BTreeMap::new()), sus_prm_stack: Rc::new(BTreeMap::new()), + real_set: Rc::new(BTreeSet::new()), fake_set: Rc::new(BTreeSet::new()), fake_if_set: Rc::new(BTreeSet::new()), ident: 0 } ) } -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub enum Attempted { - True(Option), - False, -} #[derive(Debug, Clone, Eq, PartialEq)] pub enum MarkedForm { Nil, @@ -283,45 +291,41 @@ pub enum MarkedForm { Symbol(String), Pair(NeededIds, Rc,Rc), - SuspendedSymbol(String), + SuspendedSymbol(NeededIds, String), // Needs IDs if Env chains into suspended SuspendedParamLookup { name: Option, id: EnvID, cdr_num: i32, car: bool }, SuspendedEnvLookup { name: Option, id: EnvID }, - SuspendedPair { ids: NeededIds, car: Rc, cdr: Rc}, + SuspendedPair { ids: NeededIds, call_ids: Option, car: Rc, cdr: Rc}, - SuspendedEnvEval { ids: NeededIds, x: Rc, e: Rc }, - SuspendedIf { ids: NeededIds, c: Rc, t: Rc, e: Rc }, + SuspendedEnvEval { ids: NeededIds, x: Rc, e: Rc }, + SuspendedIf { ids: NeededIds, c: Rc, t: Rc, e: Rc }, - PrimComb { name: String, nonval_ok: bool, takes_de: bool, wrap_level: i32, f: fn(BCtx,DCtx,Rc) -> Result<(BCtx,Rc),String> }, - DeriComb { lookup_name: Option, ids: NeededIds, se: Rc, de: Option, id: EnvID, wrap_level: i32, sequence_params: Vec, rest_params: Option, body: Rc }, + PrimComb { name: String, nonval_ok: bool, takes_de: bool, wrap_level: i32, f: fn(BCtx,DCtx,Rc) -> Result<(BCtx,Rc)> }, + DeriComb { lookup_name: Option, ids: NeededIds, se: Rc, de: Option, id: EnvID, wrap_level: i32, + sequence_params: Vec, rest_params: Option, body: Rc }, } impl MarkedForm { pub fn new_suspended_env_eval(x: Rc, e: Rc) -> Rc { - Rc::new(MarkedForm::SuspendedEnvEval{ ids: e.ids(), x, e }) - } - pub fn new_suspended_if(c: Rc, t: Rc, e: Rc, rec_under: Option) -> Rc { - let new_ids = c.ids().union(&t.ids()).union(&e.ids()); - let new_ids = if let Some(rec_under) = rec_under { new_ids.add_if_under(rec_under) } else { new_ids }; - Rc::new(MarkedForm::SuspendedIf{ ids: new_ids, c, t, e }) + Rc::new(MarkedForm::SuspendedEnvEval { ids: e.ids(), x, e }) } pub fn new_pair(car: Rc, cdr: Rc) -> Rc { let new_ids = car.ids().union(&cdr.ids()); - //println!("For new pair, union of {:?} and {:?} is {:?}", car.ids(), cdr.ids(), new_ids); Rc::new(MarkedForm::Pair(new_ids, car, cdr)) } - pub fn new_suspended_pair(attempted: Option, car: Rc, cdr: Rc, rec_under: Option) -> Rc { - + pub fn new_suspended_pair(attempted: Option, car: Rc, cdr: Rc, under_body: Option, under_if: Option) -> Rc { + // differentiate between call and if rec_under let ids = car.ids().union(&cdr.ids()); let ids = if let Some(attempted) = attempted { attempted.union_into_tail(ids) } else { ids }; - let ids = if let Some(rec_under) = rec_under { ids.add_body_under(rec_under) } else { ids }; + let ids = if let Some(rec_under) = under_body { ids.add_body_under(rec_under) } else { ids }; + let ids = if let Some(rec_under) = under_if { ids.add_if_under(rec_under) } else { ids }; Rc::new(MarkedForm::SuspendedPair{ ids, car, cdr }) } pub fn new_deri_comb(se: Rc, lookup_name: Option, de: Option, id: EnvID, wrap_level: i32, sequence_params: Vec, rest_params: Option, body: Rc, rec_under: Option) -> Rc { - let ids = se.ids().union_without(&body.ids(), id.clone()); + let ids = se.ids().union_without(&body.ids(), &id); let ids = if let Some(rec_under) = rec_under { ids.add_body_under(rec_under) } else { @@ -348,7 +352,6 @@ impl MarkedForm { MarkedForm::PrimComb { name, nonval_ok, takes_de, wrap_level, f } => Some(Rc::new(MarkedForm::PrimComb { name: name.clone(), nonval_ok: *nonval_ok, takes_de: *takes_de, wrap_level: wrap_level-1, f: *f })), MarkedForm::DeriComb { lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => Some(Rc::new(MarkedForm::DeriComb { lookup_name: lookup_name.clone(), ids: ids.clone(), se: Rc::clone(se), de: de.clone(), id: id.clone(), wrap_level: *wrap_level-1, sequence_params: sequence_params.clone(), rest_params: rest_params.clone(), body: Rc::clone(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, } @@ -359,8 +362,8 @@ impl MarkedForm { MarkedForm::Int(i) => NeededIds::new_none(), MarkedForm::Bool(b) => NeededIds::new_none(), MarkedForm::Symbol(s) => NeededIds::new_none(), - MarkedForm::Pair(ids,car,cdr) => ids.clone(), - MarkedForm::SuspendedSymbol(name) => NeededIds::new_true(), + MarkedForm::Pair(ids,car,cdr) => ids.clone(), + MarkedForm::SuspendedSymbol(ids, name) => ids.clone(), MarkedForm::SuspendedEnvLookup { id, .. } => NeededIds::new_single(id.clone()), MarkedForm::SuspendedParamLookup { id, .. } => NeededIds::new_single(id.clone()), MarkedForm::SuspendedEnvEval { ids, ..} => ids.clone(), @@ -370,91 +373,134 @@ impl MarkedForm { MarkedForm::DeriComb { ids, .. } => ids.clone(), } } - // TODO: this might be essentially the same as NeededIds.nothing_needed() + // note, this can be entirely ID based, but this should be more efficient pub fn is_value(&self) -> bool { - match match self { + let ids = 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::SuspendedSymbol(ids, name) => return false, MarkedForm::SuspendedEnvLookup { id, .. } => return false, MarkedForm::SuspendedParamLookup { id, .. } => return false, MarkedForm::SuspendedEnvEval { ids, ..} => return false, MarkedForm::SuspendedIf { ids, ..} => return false, MarkedForm::SuspendedPair{ ids, .. } => return false, MarkedForm::PrimComb { .. } => return true, - // TODO ths might be wrong as it could have captured some suspended computation - // On the other hand, that would surely show up in ids, right? - MarkedForm::Pair(ids,car,cdr) => ids.clone(), + MarkedForm::Pair(ids,car,cdr) => ids.clone(), MarkedForm::DeriComb { ids, .. } => ids.clone(), - } { - NeededIds::True(hashes) => false, - NeededIds::None(hashes) => true, - NeededIds::Some(ids,hashes) => false, - } + }; + ids.heads.empty() && ids.tails.empty() } - pub fn unval(self: &Rc) -> Result, &'static str> { + pub fn unval(self: &Rc) -> Result> { 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(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::SuspendedEnvEval { .. } => Err("trying to unval a suspended env eval"), - MarkedForm::SuspendedIf { .. } => Err("trying to unval a suspended if"), - MarkedForm::SuspendedPair{ ids, .. } => Err("trying to unval a suspended pair"), + MarkedForm::Symbol(s) => Ok(Rc::new(MarkedForm::SuspendedSymbol(NeededIds::new_true(), s.clone()))), + MarkedForm::Pair(ids,car,cdr) => Ok(MarkedForm::new_suspended_pair(None, car.unval()?, Rc::clone(cdr), None, None)), + MarkedForm::SuspendedSymbol(ids, name) => bail!("trying to unval a suspended symbol"), + MarkedForm::SuspendedEnvLookup { .. } => bail!("trying to unval a suspended env lookup"), + MarkedForm::SuspendedParamLookup { .. } => bail!("trying to unval a suspended param lookup"), + MarkedForm::SuspendedEnvEval { .. } => bail!("trying to unval a suspended env eval"), + MarkedForm::SuspendedIf { .. } => bail!("trying to unval a suspended if"), + MarkedForm::SuspendedPair{ ids, .. } => bail!("trying to unval a suspended pair"), MarkedForm::PrimComb { .. } => Ok(Rc::clone(self)), MarkedForm::DeriComb { .. } => Ok(Rc::clone(self)), } } - pub fn truthy(&self) -> Result { + pub fn truthy(&self) -> Result { match self { MarkedForm::Nil => Ok(false), MarkedForm::Int(i) => Ok(true), MarkedForm::Bool(b) => Ok(*b), MarkedForm::Symbol(s) => Ok(true), MarkedForm::Pair(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::SuspendedEnvEval { .. } => Err("trying to truthy a suspended env eval"), - MarkedForm::SuspendedIf { .. } => Err("trying to truthy a suspended if"), - MarkedForm::SuspendedPair{ ids, .. } => Err("trying to truthy a suspended pair"), + MarkedForm::SuspendedSymbol(ids, name) => bail!("trying to truthy a suspended symbol"), + MarkedForm::SuspendedEnvLookup { .. } => bail!("trying to truthy a suspended env lookup"), + MarkedForm::SuspendedParamLookup { .. } => bail!("trying to truthy a suspended param lookup"), + MarkedForm::SuspendedEnvEval { .. } => bail!("trying to truthy a suspended env eval"), + MarkedForm::SuspendedIf { .. } => bail!("trying to truthy a suspended if"), + MarkedForm::SuspendedPair{ ids, .. } => bail!("trying to truthy a suspended pair"), MarkedForm::PrimComb { .. } => Ok(true), MarkedForm::DeriComb { .. } => Ok(true), } } - pub fn sym(&self) -> Result<&str,&'static str> { + pub fn sym(&self) -> Result<&str> { match self { MarkedForm::Symbol(s) => Ok(s), - _ => Err("not a symbol"), + _ => bail!("not a symbol"), } } - pub fn int(&self) -> Result { + pub fn int(&self) -> Result { match self { MarkedForm::Int(i) => Ok(*i), - _ => Err("not a int"), + _ => bail!("not a int"), } } - pub fn car(&self) -> Result, String> { + pub fn car(&self) -> Result> { match self { - MarkedForm::Pair(ids,car,cdr) => Ok(Rc::clone(car)), - MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } if !car => Ok(Rc::new(MarkedForm::SuspendedParamLookup { name: name.clone(), id: id.clone(), cdr_num: *cdr_num, car: true })), - _ => Err(format!("not a pair for car: {}", self)), + MarkedForm::Pair(ids,car,cdr) => Ok(Rc::clone(car)), + MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } if !car => Ok(Rc::new(MarkedForm::SuspendedParamLookup { name: name.clone(), id: id.clone(), + cdr_num: *cdr_num, car: true })), + _ => bail!("not a pair for car: {}", self), } } - pub fn cdr(&self) -> Result, String> { + pub fn cdr(&self) -> Result> { match self { - MarkedForm::Pair(ids,car,cdr) => Ok(Rc::clone(cdr)), - MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } => Ok(Rc::new(MarkedForm::SuspendedParamLookup { name: name.clone(), id: id.clone(), cdr_num: *cdr_num+1, car: *car })), - _ => Err(format!("not a pair for cdr: {}", self)), + MarkedForm::Pair(ids,car,cdr) => Ok(Rc::clone(cdr)), + MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } if !car => Ok(Rc::new(MarkedForm::SuspendedParamLookup { name: name.clone(), id: id.clone(), + cdr_num: *cdr_num+1, car: *car })), + _ => bail!("not a pair for cdr: {}", self), } } } +fn make_eval_prim() -> Rc { + Rc::new(MarkedForm::PrimComb { name: "eval".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: eval_func }) +} +fn eval_func(bctx: BCtx, dctx: DCtx, p: Rc) -> Result<(BCtx,Rc)> { + //println!("Ok, this is inside eval looking at {}", p); + if !p.car()?.is_value() { + Ok( MarkedForm::new_suspended_pair( Some(p.car().unwrap().ids()), make_eval_prim(), p, None, None )) + } else { + //println!("Ok, returning new suspended env eval with"); + //println!("\t{} {}", p.car()?.unval()?, p.cdr()?.car()?); + Ok((bctx, MarkedForm::new_suspended_env_eval(p.car()?.unval()?, p.cdr()?.car()?))) + } +} + + +// Implement the suspended param / suspended env traversal +fn make_cons_prim() -> Rc { + Rc::new(MarkedForm::PrimComb { name: "cons".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: cons_func}) +} +fn cons_func(bctx: BCtx, dctx: DCtx, p: Rc) -> Result<(BCtx,Rc)> { + let h = p.car()?; + let t = p.cdr()?.car()?; + Ok((bctx, MarkedForm::new_pair(h, t))) +} +fn make_car_prim()-> Rc { + Rc::new(MarkedForm::PrimComb { name: "car".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: car_func}) +} +fn car_func(bctx: BCtx, dctx: DCtx, p: Rc) -> Result<(BCtx,Rc)> { + Ok((bctx, p.car()?.car()?)) +} +fn make_cdr_prim() -> Rc { + Rc::new(MarkedForm::PrimComb { name: "cdr".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: cdr_func}) +} +fn cdr_func(bctx: BCtx, dctx: DCtx, p: Rc) -> Result<(BCtx,Rc)> { + Ok((bctx, p.car()?.cdr()?)) +} + + +fn make_debug_prim() -> Rc { + Rc::new(MarkedForm::PrimComb { name: "debug".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: debug_func}) +} +fn debug_func(bctx: BCtx, dctx: DCtx, p: Rc) -> Result<(BCtx,Rc)> { + // This one is a bit weird - we put the wrap level at 1 so both sides are pe'd, + // and then return runtime + Ok(MarkedForm::new_suspended_pair( Some(runtime.clone()), make_debug_prim(), p, None, None )) +} pub fn mark(form: Rc
, bctx: BCtx) -> (BCtx, Rc) { match &*form { @@ -472,16 +518,6 @@ pub fn mark(form: Rc, bctx: BCtx) -> (BCtx, Rc) { }, Form::PrimComb(name, _f) => { (bctx, match &name[..] { - "eval" => Rc::new(MarkedForm::PrimComb { name: "eval".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { - println!("Ok, this is inside eval looking at {}", p); - if !p.car()?.is_value() { - Err("can't eval without form being a value, since we're changing the env".to_owned()) - } else { - println!("Ok, returning new suspended env eval with"); - println!("\t{} {}", p.car()?.unval()?, p.cdr()?.car()?); - Ok((bctx, MarkedForm::new_suspended_env_eval(p.car()?.unval()?, p.cdr()?.car()?))) - } - }}), "vau" => Rc::new(MarkedForm::PrimComb { name: "vau".to_owned(), nonval_ok: false, 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(); @@ -493,44 +529,34 @@ pub fn mark(form: Rc, bctx: BCtx) -> (BCtx, Rc) { let rest_params = Some(params); Ok((bctx, MarkedForm::new_deri_comb( se, None, de, id, wrap_level, sequence_params, rest_params, body, None ))) }}), - "if" => Rc::new(MarkedForm::PrimComb { name: "if".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 0, f: |bctx, dctx, p| { - Ok((bctx, MarkedForm::new_suspended_if(p.car()?.unval()?, p.cdr()?.car()?.unval()?, p.cdr()?.cdr()?.car()?.unval()?, None))) - }}), - // TODO: handle these in the context of paritals - "cons" => Rc::new(MarkedForm::PrimComb { name: "cons".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { - let h = p.car()?; - //println!("Consing with head {}", h); - let t = p.cdr()?.car()?; - //println!("Consing with tail {}", t); - Ok((bctx, MarkedForm::new_pair(h, t))) - }}), - "car" => Rc::new(MarkedForm::PrimComb { name: "car".to_owned(), nonval_ok: true, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { - Ok((bctx, p.car()?.car()?)) - }}), - "cdr" => Rc::new(MarkedForm::PrimComb { name: "cdr".to_owned(), nonval_ok: true, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { - Ok((bctx, p.car()?.cdr()?)) - }}), - "quote" => Rc::new(MarkedForm::PrimComb { name: "quote".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 0, f: |bctx, dctx, p| { - Ok((bctx, p.car()?)) - }}), - "debug" => Rc::new(MarkedForm::PrimComb { name: "debug".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { - // This one is a bit weird - we put the wrap level at 1 so both sides are pe'd, - // and always returns Err so that it's not optimized away - Err("debug can't be partial-evaluated away".to_owned()) - }}), - // ditto + "eval" => make_eval_prim(), + "cons" => make_cons_prim(), + "car" => make_car_prim(), + "cdr" => make_cdr_prim(), + "debug" => make_debug_prim(), + // Like Debug, listed as wrap_level 1 so bothe sides are pe'd, even though it would + // be sequencing at runtime "assert" => Rc::new(MarkedForm::PrimComb { name: "assert".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { let cond = p.car()?; if !cond.truthy()? { - println!("Assert failed: {:?}", cond); + bail!("Assertion failed {}", cond) } - assert!(cond.truthy()?); Ok((bctx, p.cdr()?.car()?)) }}), + "if" => Rc::new(MarkedForm::PrimComb { name: "if".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 0, f: |bctx, dctx, p| { + let c = p.car()?.unval()?; + let t = p.cdr()?.car()?.unval()?; + let e = p.cdr()?.cdr()?.car()?.unval()?; + let ids = c.ids().union(&t.ids()).union(&e.ids()); + Ok((bctx, Rc::new(MarkedForm::SuspendedIf{ids, c, t, e}))) + }}), + // Non val is actually fine + "quote" => Rc::new(MarkedForm::PrimComb { name: "quote".to_owned(), nonval_ok: true, takes_de: false, wrap_level: 0, f: |bctx, dctx, p| { + Ok((bctx, p.car()?)) + }}), "=" => Rc::new(MarkedForm::PrimComb { name: "=".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { let a = p.car()?; let b = p.cdr()?.car()?; - // wrap_level=1 should mean that everything's a value Ok((bctx, Rc::new(MarkedForm::Bool(a == b)))) }}), "<" => Rc::new(MarkedForm::PrimComb { name: "<".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { @@ -593,6 +619,7 @@ pub fn mark(form: Rc, bctx: BCtx) -> (BCtx, Rc) { let b = p.cdr()?.car()?.int()?; Ok((bctx, Rc::new(MarkedForm::Int(a ^ b)))) }}), + // This could allow nonval with fallback "comb?" => Rc::new(MarkedForm::PrimComb { name: "comb?".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { Ok((bctx, Rc::new(MarkedForm::Bool(match &* p.car()? { MarkedForm::PrimComb { .. } => true, @@ -600,10 +627,11 @@ pub fn mark(form: Rc, bctx: BCtx) -> (BCtx, Rc) { _ => false, })))) }}), + // This could allow nonval with fallback "pair?" => Rc::new(MarkedForm::PrimComb { name: "pair?".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { Ok((bctx, Rc::new(MarkedForm::Bool(match &* p.car()? { MarkedForm::Pair(_i, _a,_b) => true, - _ => false, + _ => false, })))) }}), "symbol?" => Rc::new(MarkedForm::PrimComb { name: "symbol?".to_owned(), nonval_ok: false, takes_de: false, wrap_level: 1, f: |bctx, dctx, p| { @@ -638,139 +666,125 @@ pub fn mark(form: Rc, bctx: BCtx) -> (BCtx, Rc) { } pub fn combiner_return_ok(x: &Rc, check_id: Option) -> bool { - match match &**x { - MarkedForm::Nil => return true, - MarkedForm::Int(_) => return true, - MarkedForm::Bool(_) => return true, - MarkedForm::Symbol(_) => return true, - // Hmm, we allow Pair to included suspended now... - // so now we have to be extra careful - MarkedForm::Pair(ids,car,cdr) => ids, - - MarkedForm::SuspendedSymbol(_) => return false, - MarkedForm::SuspendedParamLookup { id, .. } => return check_id.map(|check_id| *id != check_id).unwrap_or(true), - MarkedForm::SuspendedEnvLookup { id, .. } => return check_id.map(|check_id| *id != check_id).unwrap_or(true), - - MarkedForm::SuspendedEnvEval { e, .. } => return combiner_return_ok(e, check_id), - MarkedForm::SuspendedIf { c, t, e, .. } => return combiner_return_ok(c, check_id.clone()) && - combiner_return_ok(t, check_id.clone()) && - combiner_return_ok(e, check_id), - MarkedForm::SuspendedPair { car, cdr, .. } => { - // expand with (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( _under) => false, - NeededIds::None( _under) => true, - NeededIds::Some(ids,_under) => check_id.map(|check_id| !ids.contains(&check_id)).unwrap_or(true), - } - //; Handles let 4.3 through macro level leaving it as ( 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 - // ; ( 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 ( 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) - // ) - //)) + let ids = x.ids(); + !ids.contains(&true_id) && check_id.map(|check_id| !ids.contains(&check_id)).unwrap_or(true) } -pub fn partial_eval(bctx_in: BCtx, dctx_in: DCtx, form: Rc) -> (BCtx,Rc) { +pub fn partial_eval(bctx_in: BCtx, dctx_in: DCtx, form: Rc) -> Result<(BCtx,Rc)> { let mut bctx = bctx_in; let mut dctx = dctx_in; let mut next_form = Some(form); - let mut force = false; loop { let x = next_form.take().unwrap(); //println!("{:ident$}PE: {}", "", x, ident=dctx.ident*4); - if !(force || dctx.can_progress(x.ids())) { + if !dctx.can_progress(x.ids()) { //println!("{:ident$}Shouldn't go!", "", ident=dctx.ident*4); - return (bctx, x); + return Ok((bctx, x)); } //println!("{:ident$}({}) PE(force:{}) {:?} (because of {:?})", "", dctx.ident, force, x, x.ids(), ident=dctx.ident*4); - println!("{:ident$}({}) PE(force:{}) {} (because of {:?})", "", dctx.ident, force, x, x.ids(), ident=dctx.ident*4); - match partial_eval_step(&x, force, bctx.clone(), &mut dctx) { - Ok((new_bctx,new_force,new_form)) => { - bctx = new_bctx; force = new_force; next_form = Some(new_form); - println!("{:ident$}({}) was ok, result was {}", "", dctx.ident, next_form.as_ref().unwrap(), ident=dctx.ident*4); - } - Err(msg) => { - println!("{:ident$}({}) was error, reconstructing (error was {})", "", dctx.ident, msg, ident=dctx.ident*4); - return (bctx, x); - } - } + //println!("{:ident$}({}) PE {} (because of {:?})", "", dctx.ident, x, x.ids(), ident=dctx.ident*4); + let (new_bctx, new_form) = partial_eval_step(&x, bctx.clone(), &mut dctx)?; // basic Drop redundent veval // Old one was recursive over parameters to combs, which we might need, since the redundent veval isn't captured by // ids. TODO! - if let Some(form) = next_form.as_ref() { - if let MarkedForm::SuspendedEnvEval { x, e, .. } = &**form { - if (combiner_return_ok(&x, None) || *e == dctx.e) { - next_form = Some(Rc::clone(x)); - force = true; - } + // + // Nowadays, dropping EnvEval is legal always *unless* + // - True is in ids + // - this current env ends in a suspended param/env, and that id is in ids (the *weird* case) + if let MarkedForm::SuspendedEnvEval { x, e, .. } = &new_form { + if !x.ids().may_contain_id(&true_id) { + next_form = Some(Rc::clone(x)); + // do we still need force for drop redundent veval? + // Not while it's not recursive, at elaset + } else { + next_form = Some(new_form); } } } } -fn partial_eval_step(x: &Rc, forced: bool, bctx: BCtx, dctx: &mut DCtx) -> Result<(BCtx,bool,Rc), String> { +enum MapUnvalPEvalResult { + Ok(BCtx,Rc), + NotYet(anyhow::Error), + BadError(anyhow::Error), +} +fn map_unval_peval(bctx: BCtx, dctx: DCtx, x: Rc) -> MapUnvalPEvalResult { + match &*x { + MarkedForm::Nil => MapUnvalPEvalResult::Ok(bctx,x), + MarkedForm::Pair(ids, x_car, x_cdr) => { + match x_car.unval() { + Ok(unval) => { + match partial_eval(bctx, dctx.clone(), unval) { + Ok((bctx, new_x_car)) => { + match map_unval_peval(bctx, dctx.clone(), Rc::clone(x_cdr)) { + MapUnvalPEvalResult::Ok(bctx, new_x_cdr) => MapUnvalPEvalResult::Ok(bctx, MarkedForm::new_pair(new_x_car, new_x_cdr)), + e => e, + } + } + Err(e) => MapUnvalPEvalResult::BadError(e), + } + }, + Err(e) => MapUnvalPEvalResult::NotYet(e), + } + }, + + MarkedForm::Int(i) => MapUnvalPEvalResult::BadError(anyhow!("map_unval_peval over not a list")), + MarkedForm::Bool(b) => MapUnvalPEvalResult::BadError(anyhow!("map_unval_peval over not a list")), + MarkedForm::Symbol(s) => MapUnvalPEvalResult::BadError(anyhow!("map_unval_peval over not a list")), + MarkedForm::PrimComb { .. } => MapUnvalPEvalResult::BadError(anyhow!("map_unval_peval over not a list")), + MarkedForm::Pair(ids,car,cdr) => MapUnvalPEvalResult::BadError(anyhow!("map_unval_peval over not a list")), + MarkedForm::DeriComb { ids, .. } => MapUnvalPEvalResult::BadError(anyhow!("map_unval_peval over not a list")), + + MarkedForm::SuspendedSymbol(ids, name) => MapUnvalPEvalResult::NotYet(anyhow!("map_unval_peval over not (yet) a list")), + MarkedForm::SuspendedEnvLookup { id, .. } => MapUnvalPEvalResult::NotYet(anyhow!("map_unval_peval over not (yet) a list")), + MarkedForm::SuspendedParamLookup { id, .. } => MapUnvalPEvalResult::NotYet(anyhow!("map_unval_peval over not (yet) a list")), + MarkedForm::SuspendedEnvEval { ids, ..} => MapUnvalPEvalResult::NotYet(anyhow!("map_unval_peval over not (yet) a list")), + MarkedForm::SuspendedIf { ids, ..} => MapUnvalPEvalResult::NotYet(anyhow!("map_unval_peval over not (yet) a list")), + MarkedForm::SuspendedPair{ ids, .. } => MapUnvalPEvalResult::NotYet(anyhow!("map_unval_peval over not (yet) a list")), + + } +} +fn partial_eval_step(x: &Rc, bctx: BCtx, dctx: &mut DCtx) -> Result<(BCtx,Rc)> { //println!("{:ident$}({}) {}", "", dctx.ident, x, ident=dctx.ident*4); match &**x { MarkedForm::Pair(ids,car,cdr) => { //println!("{:ident$}pair ({}) {}", "", dctx.ident, x, ident=dctx.ident*4); let (bctx, car) = partial_eval(bctx, dctx.clone(), Rc::clone(car)); let (bctx, cdr) = partial_eval(bctx, dctx.clone(), Rc::clone(cdr)); - Ok((bctx, false, MarkedForm::new_pair(car, cdr))) + Ok((bctx, MarkedForm::new_pair(car, cdr))) }, - MarkedForm::SuspendedSymbol(name) => { + MarkedForm::SuspendedSymbol(ids, name) => { + // Have to account for the *weird* case that the env chain ends in a suspended param / suspended env println!("Lookin up symbol {}", name); let mut t = Rc::clone(&dctx.e); - while name != t.car()?.car()?.sym()? { - t = t.cdr()?; + loop { + if let Some(cmp) = t.car().and_then(|kv| kv.car()).and_then(|s| s.sym()) { + if name == cmp { + return Ok((bctx, t.car()?.cdr()?.tag_name(name))); + } else { + t = t.cdr()?; + } + } else { + // bad env + match &*t { + MarkedForm::Nil => bail!("Lookup for {} not found!", name), + MarkedForm::SuspendedSymbol(ids, name) => break, + MarkedForm::SuspendedEnvLookup { .. } => break, + MarkedForm::SuspendedParamLookup { .. } => break, + MarkedForm::SuspendedEnvEval { .. } => break, + MarkedForm::SuspendedIf { .. } => break, + MarkedForm::SuspendedPair { .. } => break, + MarkedForm::Pair(ids,car,cdr) => break, + _ => bail!("Illegal lookup for {}", name), + } + } } - println!("found it, pair is {}", t.car()?); - Ok((bctx, false, t.car()?.cdr()?.tag_name(name))) + return Ok((bctx, Rc::new(MarkedForm::SuspendedSymbol(t.ids(), name)))); }, MarkedForm::SuspendedEnvLookup { name, id } => { if let Some(v) = dctx.sus_env_stack.get(id) { - Ok((bctx, false, if let Some(name) = name { v.tag_name(name) } else { Rc::clone(v) })) + Ok((bctx, if let Some(name) = name { v.tag_name(name) } else { Rc::clone(v) })) } else { - Err("failed env lookup (forced)".to_owned()) + panic!("failed env lookup (forced)"); } }, MarkedForm::SuspendedParamLookup { name, id, cdr_num, car } => { @@ -782,9 +796,9 @@ fn partial_eval_step(x: &Rc, forced: bool, bctx: BCtx, dctx: &mut DC if *car { translated_value = translated_value.car()?; } - Ok((bctx, false, translated_value)) + Ok((bctx, translated_value)) } else { - Err("failed param lookup (forced)".to_owned()) + panic!("failed param lookup (forced)"); } }, MarkedForm::SuspendedEnvEval { x, e, .. } => { @@ -793,89 +807,91 @@ fn partial_eval_step(x: &Rc, forced: bool, bctx: BCtx, dctx: &mut DC let (bctx, e) = partial_eval(bctx, dctx.clone(), Rc::clone(e)); let (bctx, x) = partial_eval(bctx, dctx.copy_set_env(&e), Rc::clone(x)); if x.is_value() { - Ok((bctx, false, x)) + Ok((bctx, x)) } else { - Ok((bctx, false, MarkedForm::new_suspended_env_eval(x, e))) + Ok((bctx, MarkedForm::new_suspended_env_eval(x, e))) } // Note also that we drop redundent vevals at the bottom of the loop tail-call loop // with force }, MarkedForm::SuspendedIf { c, t, e, .. } => { - let (bctx, c) = partial_eval(bctx, dctx.clone(), Rc::clone(c)); + let (bctx, c) = partial_eval(bctx, dctx.clone(), Rc::clone(c))?; if let Ok(b) = c.truthy() { if b { - Ok((bctx, false, Rc::clone(t))) + Ok((bctx, Rc::clone(t))) } else { - Ok((bctx, false, Rc::clone(e))) + Ok((bctx, Rc::clone(e))) } } else { - let dctx = dctx.copy_push_fake_if(); - let (bctx, t) = partial_eval(bctx, dctx.clone(), Rc::clone(t)); - let (bctx, e) = partial_eval(bctx, dctx.clone(), Rc::clone(e)); - Ok((bctx, false, MarkedForm::new_suspended_if(c,t,e, None))) + let (could_stop, dctx) = dctx.copy_push_fake_if(); + let (bctx, t) = partial_eval(bctx, dctx.clone(), Rc::clone(t))?; + let (bctx, e) = partial_eval(bctx, dctx.clone(), Rc::clone(e))?; + // if either t or e stopped because of our fake if (SO CHECK IF IT WAS US AT PUSH TIME), + // guard on our condition with that branch in tail + let c_ids = c.ids(); + let t_ids = t.ids(); + let e_ids = e.ids(); + let ids = if let Some(this_id) = could_stop { + let ids = if t_ids.contains_if_stop(this_id) { + ids.union_into_tail(&t_ids) + } else { c_ids.union(&t_ids) }; + if e_ids.contains_if_stop(this_id) { + ids.union_into_tail(&e_ids) + } else { ids.union(&e_ids) } + } else { + c_ids.union(&t_ids).union(&e_ids) + }; + + Ok((bctx, Rc::new(MarkedForm::SuspendedIf {ids, c, t, e }))) } }, MarkedForm::DeriComb { lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => { // TODO: figure out wrap level, sequence params, etc - if forced || !se.ids().needs_nothing() { - let old_se_ids = se.ids(); - let se = if !se.ids().needs_nothing() { - // the current env is our new se - Rc::clone(&dctx.e) - } else { - Rc::clone(se) - }; - + if !se.is_value() { + // the current env is our new se + let se = Rc::clone(&dctx.e); let ident_amount = dctx.ident*4; match dctx.copy_push_frame(id.clone(), &se, &de, None, &rest_params, None, body) { Ok(inner_dctx) => { - println!("{:ident$}Doing a body deri for {:?} because ({} || {:?}) which is {}", "", lookup_name, forced, old_se_ids, x, ident=ident_amount); + println!("{:ident$}Doing a body deri for {:?} because {:?} which is {}", "", lookup_name, old_se_ids, x, ident=ident_amount); println!("{:ident$}and also body ids is {:?}", "", body.ids(), ident=ident_amount); - //println!("{:ident$}and fake is {:?} and fake_if is {:?}", "", , ident=ident_amount); - let (bctx, body) = partial_eval(bctx, inner_dctx, Rc::clone(&body)); + let (bctx, body) = partial_eval(bctx, inner_dctx, Rc::clone(&body))?; println!("{:ident$}result was {}", "", body, ident=ident_amount); - Ok((bctx, false, MarkedForm::new_deri_comb(se, lookup_name.clone(), de.clone(), id.clone(), *wrap_level, sequence_params.clone(), rest_params.clone(), body, None))) + Ok((bctx, MarkedForm::new_deri_comb(se, lookup_name.clone(), de.clone(), id.clone(), *wrap_level, sequence_params.clone(), rest_params.clone(), body, None))) }, Err(rec_stop_under) => { println!("{:ident$}call of {:?} failed b/c rec_stop_under", "", lookup_name, ident=dctx.ident*4); - Ok((bctx, false, MarkedForm::new_deri_comb(se, lookup_name.clone(), de.clone(), id.clone(), *wrap_level, sequence_params.clone(), rest_params.clone(), Rc::clone(body), Some(rec_stop_under)))) + Ok((bctx, MarkedForm::new_deri_comb(se, lookup_name.clone(), de.clone(), id.clone(), *wrap_level, sequence_params.clone(), rest_params.clone(), Rc::clone(body), Some(rec_stop_under)))) }, } } else { - //panic!("impossible {}", x); - Err("impossible!?".to_owned()) + panic!("impossible {}", x); + //bail!("impossible!?") } }, - MarkedForm::SuspendedPair { 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(); + MarkedForm::SuspendedPair { ids, call_ids, 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 maybe_rec_under = None; - let mut return_ok = false; while let Some(wrap_level) = car.wrap_level() { if wrap_level > 0 { - fn map_unval_peval(bctx: BCtx, dctx: DCtx, x: Rc) -> Result<(BCtx,Rc),String> { - match &*x { - MarkedForm::Pair(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()), - } - } - match map_unval_peval(bctx.clone(), dctx.clone(), Rc::clone(&cdr)) { - Ok((new_bctx, new_cdr)) => { + // two types of errors here - real ones, and ones that just prevent evaluating + // the entire parameter list right now due to suspended + match map_unval_peval(bctx.clone(), dctx.clone(), Rc::clone(&cdr))? { + MapUnvalPEvalResult::Ok(new_bctx, new_cdr) => { car = car.decrement_wrap_level().unwrap(); cdr = new_cdr; bctx = new_bctx; - } - Err(msg) => { - println!("{:ident$} evaling parameters failed b/c {}", "", msg, ident=dctx.ident*4); + }, + MapUnvalPEvalResult::NotYet(e) => { + println!("{:ident$} evaling parameters failed (for now) b/c {:?}", "", e, ident=dctx.ident*4); break; - } + }, + MapUnvalPEvalResult::BadError(e) => { + println!("{:ident$} evaling parameters failed (FOREVER) b/c {:?}", "", e, ident=dctx.ident*4); + return Err(e); + }, } } else { // check to see if can do call @@ -885,37 +901,26 @@ fn partial_eval_step(x: &Rc, forced: bool, bctx: BCtx, dctx: &mut DC if !nonval_ok && !cdr.is_value() { break; } - 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); - println!("{:ident$}parameters {} are? a val because {:?}", "", cdr, cdr.ids(), ident=ident_amount); - match f(bctx.clone(), dctx.clone(), Rc::clone(&cdr)) { - Ok((bctxp, r)) => { - // force true b/c might be a tail call - return Ok((bctx, true, r)); - //return Ok((bctx, name == "eval" || name == "if", r)); - }, - Err(msg) => { - println!("{:ident$} call to {} failed {:?}", "", name, msg, ident=ident_amount); - }, - } + println!("{:ident$}doing a call eval of {}", "", name, ident=dctx.ident*4); + println!("{:ident$}parameters {} are? a val because {:?}", "", cdr, cdr.ids(), ident=dctx.ident*4); + return f(bctx.clone(), dctx.clone(), Rc::clone(&cdr)); } MarkedForm::DeriComb { lookup_name, ids, se, de, id, wrap_level, sequence_params, rest_params, body } => { if !cdr.is_value() { break; } - new_attempted = Attempted::True(if de.is_some() { Some(dctx.e.ids()) } else { None }); + //new_attempted = Attempted::True(if de.is_some() { Some(dctx.e.ids()) } else { None }); if de.is_some() && dctx.e.ids().may_contain_id(id.clone()) { // The current environment may contain a reference to our ID, which // means if we take that environment, if we then PE that // environment we will replace it with our real environment that // still has a dynamic reference to the current environment, which // will be an infinate loop - break; + return Ok((bctx, MarkedForm::new_suspended_pair( Some(NeededIds::new_single(id.clone())), car, cdr, None, None))); } // not yet supporting sequence params match dctx.copy_push_frame(id.clone(), &se, &de, Some(Rc::clone(&dctx.e)), &rest_params, Some(Rc::clone(&cdr)), body) { - Ok(inner_dctx) => { + PushFrameResult::Ok(inner_dctx) => { let ident_amount = inner_dctx.ident*4; //println!("{:ident$}doing a call eval of {} in {}", "", body, inner_dctx.e, ident=inner_dctx.ident*4); println!("{:ident$}doing a call eval of {:?}", "", lookup_name, ident=ident_amount); @@ -923,27 +928,42 @@ fn partial_eval_step(x: &Rc, forced: bool, bctx: BCtx, dctx: &mut DC //Here is where we could do a tail call instead, but there //would be no recovery back into the call-form... - let (bctx, r) = partial_eval(bctx.clone(), inner_dctx, Rc::clone(body)); + let (bctx, r) = partial_eval(bctx.clone(), inner_dctx, Rc::clone(body))?; if combiner_return_ok(&r, Some(id.clone())) { return Ok((bctx, false, r)); + } else { + return Ok((bctx, MarkedForm::new_suspended_pair( Some(r.ids()), car, cdr, None, None))); } }, - Err(rec_stop_under) => { - println!("{:ident$}call of {:?} failed b/c rec_stop_under", "", lookup_name, ident=dctx.ident*4); - maybe_rec_under = Some(rec_stop_under); + PushFrameResult::UnderBody(rec_stop_under) => { + println!("{:ident$}call of {:?} failed b/c rec_stop_under of body", "", lookup_name, ident=dctx.ident*4); + return Ok((bctx, MarkedForm::new_suspended_pair( None, car, cdr, Some(rec_stop_under), None))); + }, + PushFrameResult::UnderIf(rec_stop_under) => { + println!("{:ident$}call of {:?} failed b/c rec_stop_under of if", "", lookup_name, ident=dctx.ident*4); + return Ok((bctx, MarkedForm::new_suspended_pair( None, car, cdr, None, Some(rec_stop_under)))); }, } }, - _ => {}, + // These are illegal values + MarkedForm::Nil => bail("tried to call a bad value {:?}", car), + MarkedForm::Pair(ids, x_car, x_cdr) => bail("tried to call a bad value {:?}", car), + MarkedForm::Int(i) => bail("tried to call a bad value {:?}", car), + MarkedForm::Bool(b) => bail("tried to call a bad value {:?}", car), + MarkedForm::Symbol(s) => bail("tried to call a bad value {:?}", car), + MarkedForm::PrimComb { .. } => bail("tried to call a bad value {:?}", car), + MarkedForm::Pair(ids,car,cdr) => bail("tried to call a bad value {:?}", car), + MarkedForm::DeriComb { ids, .. } => bail("tried to call a bad value {:?}", car), + _ => {}, // suspended, so reform } break; } } - // Call failed, do the re-wrap-up ourselves b/c of our possibly advanced wrap/params - Ok((bctx, false, MarkedForm::new_suspended_pair( new_attempted, car, cdr, maybe_rec_under ))) + // Didn't manage to call + Ok((bctx, MarkedForm::new_suspended_pair( None, car, cdr, None, None))) }, // Values should never get here b/c ids UNLESS FORCE HAH - _ => Err("value evaled".to_owned()), + _ => panic!("value evaled! {}", x), } } @@ -988,7 +1008,7 @@ impl fmt::Display for MarkedForm { write!(f, "{:?}#[{:?}/{:?}/{:?}/{}/{:?}/{:?}/{}]", ids, lookup_name, de, id, wrap_level, sequence_params, rest_params, body) }, - MarkedForm::SuspendedPair{ ids, attempted, car, cdr } => { + MarkedForm::SuspendedPair{ ids, call_ids, car, cdr } => { //write!(f, "{:?}{:?}#{{{}", ids, attempted, car)?; write!(f, "{{{}", car)?; let mut traverse: Rc = Rc::clone(cdr);