Update attenuations

This commit is contained in:
Tony Garnock-Jones 2023-02-06 14:48:18 +01:00
parent 12eaeb8f62
commit 833be7b293
5 changed files with 177 additions and 116 deletions

View File

@ -101,7 +101,7 @@ pub enum Expr {
#[derive(Debug, Clone)]
enum RewriteTemplate {
Filter {
Accept {
pattern_template: AnyValue,
},
Rewrite {
@ -110,6 +110,16 @@ enum RewriteTemplate {
},
}
#[derive(Debug, Clone)]
enum CaveatTemplate {
Alts {
alternatives: Vec<RewriteTemplate>,
},
Reject {
pattern_template: AnyValue,
},
}
#[derive(Debug)]
enum Symbolic {
Reference(String),
@ -174,55 +184,77 @@ fn tlit(value: AnyValue) -> sturdy::Template {
sturdy::Template::Lit(Box::new(sturdy::Lit { value }))
}
fn parse_attenuation(r: &Record<AnyValue>) -> io::Result<Option<(String, Vec<RewriteTemplate>)>> {
fn parse_rewrite(raw_base_name: &AnyValue, e: &AnyValue) -> io::Result<RewriteTemplate> {
if let Some(fields) = e.value().as_simple_record("accept", Some(1)) {
return Ok(RewriteTemplate::Accept {
pattern_template: fields[0].clone(),
});
}
if let Some(fields) = e.value().as_simple_record("rewrite", Some(2)) {
return Ok(RewriteTemplate::Rewrite {
pattern_template: fields[0].clone(),
template_template: fields[1].clone(),
});
}
Err(bad_instruction(&format!("Bad rewrite in attenuation of {:?}: {:?}", raw_base_name, e)))
}
fn parse_caveat(raw_base_name: &AnyValue, e: &AnyValue) -> io::Result<CaveatTemplate> {
if let Some(fields) = e.value().as_simple_record("or", Some(1)) {
let raw_rewrites = match fields[0].value().as_sequence() {
None => Err(bad_instruction(&format!(
"Alternatives in <or> in attenuation of {:?} must have sequence of rewrites; got {:?}",
raw_base_name,
fields[0])))?,
Some(vs) => vs,
};
let alternatives =
raw_rewrites.iter().map(|r| parse_rewrite(raw_base_name, r)).collect::<Result<Vec<_>, _>>()?;
return Ok(CaveatTemplate::Alts{ alternatives });
}
if let Some(fields) = e.value().as_simple_record("reject", Some(1)) {
return Ok(CaveatTemplate::Reject{ pattern_template: fields[0].clone() });
}
if let Ok(r) = parse_rewrite(raw_base_name, e) {
return Ok(CaveatTemplate::Alts { alternatives: vec![r] });
}
Err(bad_instruction(&format!("Bad caveat in attenuation of {:?}: {:?}", raw_base_name, e)))
}
fn parse_attenuation(r: &Record<AnyValue>) -> io::Result<Option<(String, Vec<CaveatTemplate>)>> {
if r.label() != &AnyValue::symbol("*") {
return Ok(None);
}
if r.fields().len() != 2 {
Err(bad_instruction(&format!(
"Attenuation requires a reference and a sequence of rewrites; got {:?}",
"Attenuation requires a reference and a sequence of caveats; got {:?}",
r)))?;
}
let base_name = match r.fields()[0].value().as_symbol().map(|s| analyze(&s)) {
let raw_base_name = &r.fields()[0];
let base_name = match raw_base_name.value().as_symbol().map(|s| analyze(&s)) {
Some(Symbolic::Reference(s)) => s,
_ => Err(bad_instruction(&format!(
"Attenuation must have variable reference as first argument; got {:?}",
r.fields()[0])))?,
raw_base_name)))?,
};
let raw_alternatives = match r.fields()[1].value().as_sequence() {
let raw_caveats = match r.fields()[1].value().as_sequence() {
None => Err(bad_instruction(&format!(
"Attenuation of {:?} must have sequence of rewrites; got {:?}",
r.fields()[0],
"Attenuation of {:?} must have sequence of caveats; got {:?}",
raw_base_name,
r.fields()[1])))?,
Some(vs) => vs,
};
let mut alternatives = Vec::new();
for e in raw_alternatives.iter() {
match e.value().as_simple_record("filter", Some(1)) {
Some(fields) =>
alternatives.push(RewriteTemplate::Filter {
pattern_template: fields[0].clone()
}),
None => match e.value().as_simple_record("rewrite", Some(2)) {
Some(fields) =>
alternatives.push(RewriteTemplate::Rewrite {
pattern_template: fields[0].clone(),
template_template: fields[1].clone(),
}),
None => Err(bad_instruction(&format!(
"Bad rewrite in attenuation of {:?}: {:?}",
r.fields()[0],
e)))?,
}
}
}
Ok(Some((base_name, alternatives)))
let caveats = raw_caveats.iter().map(|c| parse_caveat(raw_base_name, c)).collect::<Result<Vec<_>, _>>()?;
Ok(Some((base_name, caveats)))
}
impl<'env> PatternInstantiator<'env> {
@ -250,8 +282,8 @@ impl<'env> PatternInstantiator<'env> {
},
Value::Record(r) => match parse_attenuation(r)? {
Some((base_name, alternatives)) =>
dlit(self.env.eval_attenuation(base_name, alternatives)?),
Some((base_name, caveats)) =>
dlit(self.env.eval_attenuation(base_name, caveats)?),
None => match self.maybe_binder_with_pattern(r)? {
Some(pat) => pat,
None => {
@ -359,8 +391,8 @@ impl Env {
},
Value::Record(r) => match parse_attenuation(r)? {
Some((base_name, alternatives)) =>
self.eval_attenuation(base_name, alternatives)?,
Some((base_name, caveats)) =>
self.eval_attenuation(base_name, caveats)?,
None =>
Value::Record(Record(r.fields_vec().iter().map(|a| self.instantiate_value(a))
.collect::<Result<Vec<_>, _>>()?)).wrap(),
@ -398,7 +430,7 @@ impl Env {
fn eval_attenuation(
&self,
base_name: String,
alternatives: Vec<RewriteTemplate>,
caveats: Vec<CaveatTemplate>,
) -> io::Result<AnyValue> {
let base_value = self.lookup(&base_name, "attenuation-base variable")?;
match base_value.value().as_embedded() {
@ -406,9 +438,7 @@ impl Env {
"Value to be attenuated is {:?} but must be capability",
base_value))),
Some(base_cap) => {
match base_cap.attenuate(&sturdy::Attenuation(vec![
self.instantiate_caveat(&alternatives)?]))
{
match base_cap.attenuate(&caveats.iter().map(|c| self.instantiate_caveat(c)).collect::<Result<Vec<_>, _>>()?) {
Ok(derived_cap) => Ok(AnyValue::domain(derived_cap)),
Err(caveat_error) =>
Err(bad_instruction(&format!("Attenuation of {:?} failed: {:?}",
@ -517,35 +547,50 @@ impl Env {
}
}
fn instantiate_caveat(
fn instantiate_rewrite(
&self,
alternatives: &Vec<RewriteTemplate>,
) -> io::Result<sturdy::Caveat> {
let mut rewrites = Vec::new();
for rw in alternatives {
match rw {
RewriteTemplate::Filter { pattern_template } => {
let (_binding_names, pattern) = self.instantiate_pattern(pattern_template)?;
rewrites.push(sturdy::Rewrite {
pattern: embed_pattern(&P::Pattern::DBind(Box::new(P::DBind { pattern }))),
template: sturdy::Template::TRef(Box::new(sturdy::TRef { binding: 0.into() })),
})
}
RewriteTemplate::Rewrite { pattern_template, template_template } => {
let (binding_names, pattern) = self.instantiate_pattern(pattern_template)?;
rewrites.push(sturdy::Rewrite {
pattern: embed_pattern(&pattern),
template: self.instantiate_template(&binding_names, template_template)?,
})
}
rw: &RewriteTemplate,
) -> io::Result<sturdy::Rewrite> {
match rw {
RewriteTemplate::Accept { pattern_template } => {
let (_binding_names, pattern) = self.instantiate_pattern(pattern_template)?;
Ok(sturdy::Rewrite {
pattern: embed_pattern(&P::Pattern::DBind(Box::new(P::DBind { pattern }))),
template: sturdy::Template::TRef(Box::new(sturdy::TRef { binding: 0.into() })),
})
}
RewriteTemplate::Rewrite { pattern_template, template_template } => {
let (binding_names, pattern) = self.instantiate_pattern(pattern_template)?;
Ok(sturdy::Rewrite {
pattern: embed_pattern(&pattern),
template: self.instantiate_template(&binding_names, template_template)?,
})
}
}
if rewrites.len() == 1 {
Ok(sturdy::Caveat::Rewrite(Box::new(rewrites.pop().unwrap())))
} else {
Ok(sturdy::Caveat::Alts(Box::new(sturdy::Alts {
alternatives: rewrites,
})))
}
fn instantiate_caveat(
&self,
c: &CaveatTemplate,
) -> io::Result<sturdy::Caveat> {
match c {
CaveatTemplate::Alts { alternatives } => {
let mut rewrites =
alternatives.iter().map(|r| self.instantiate_rewrite(r)).collect::<Result<Vec<_>, _>>()?;
if rewrites.len() == 1 {
Ok(sturdy::Caveat::Rewrite(Box::new(rewrites.pop().unwrap())))
} else {
Ok(sturdy::Caveat::Alts(Box::new(sturdy::Alts {
alternatives: rewrites,
})))
}
}
CaveatTemplate::Reject { pattern_template } => {
Ok(sturdy::Caveat::Reject(Box::new(
sturdy::Reject {
pattern: embed_pattern(&self.instantiate_pattern(pattern_template)?.1),
})))
}
}
}
@ -584,18 +629,19 @@ impl Env {
},
Value::Record(r) => match parse_attenuation(r)? {
Some((base_name, alternatives)) =>
Some((base_name, caveats)) =>
match find_bound(&base_name) {
Some(i) =>
sturdy::Template::TAttenuate(Box::new(sturdy::TAttenuate {
template: sturdy::Template::TRef(Box::new(sturdy::TRef {
binding: i.into(),
})),
attenuation: sturdy::Attenuation(vec![
self.instantiate_caveat(&alternatives)?]),
attenuation: caveats.iter()
.map(|c| self.instantiate_caveat(c))
.collect::<Result<Vec<_>, _>>()?,
})),
None =>
tlit(self.eval_attenuation(base_name, alternatives)?),
tlit(self.eval_attenuation(base_name, caveats)?),
},
None => {
// TODO: properly consolidate constant templates into literals.

View File

@ -2270,11 +2270,11 @@ impl Cap {
}
/// Yields a fresh `Cap` for `self`'s `underlying`, copying the
/// existing attenuation of `self` to the new `Cap` and adding
/// `attenuation` to it.
pub fn attenuate(&self, attenuation: &sturdy::Attenuation) -> Result<Arc<Self>, CaveatError> {
/// existing attenuation of `self` to the new `Cap` and adding the
/// `caveats` to it.
pub fn attenuate(&self, caveats: &[sturdy::Caveat]) -> Result<Arc<Self>, CaveatError> {
let mut r = Cap { attenuation: self.attenuation.clone(), .. self.clone() };
r.attenuation.extend(attenuation.check()?);
r.attenuation.extend(sturdy::Caveat::check_many(caveats)?);
Ok(Arc::new(r))
}
@ -2282,7 +2282,7 @@ impl Cap {
/// `a` is filtered out, or `Some(_)` if it is accepted (and
/// possibly transformed).
pub fn rewrite(&self, mut a: AnyValue) -> Option<AnyValue> {
for c in &self.attenuation {
for c in self.attenuation.iter().rev() {
match c.rewrite(&a) {
Some(v) => a = v,
None => return None,

View File

@ -580,7 +580,7 @@ impl Membranes {
match self.exported.oid_map.get(&oid) {
None => self.exported.insert_inert_entity(t, oid),
Some(ws) => {
let attenuated_obj = ws.obj.attenuate(&sturdy::Attenuation(attenuation))
let attenuated_obj = ws.obj.attenuate(&attenuation)
.map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidInput,

View File

@ -16,7 +16,10 @@ pub type CheckedRewrite = (usize, Pattern, Template);
/// A safety-checked [`Caveat`]: none of the errors enumerated in
/// `CaveatError` apply.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CheckedCaveat { alts: Vec<CheckedRewrite> }
pub enum CheckedCaveat {
Alts(Vec<CheckedRewrite>),
Reject(Pattern),
}
/// Represents any detected error in a [`Caveat`]; that is, in a
/// [`Pattern`] or a [`Template`].
@ -28,43 +31,44 @@ pub enum CaveatError {
BindingUnderNegation,
}
impl Attenuation {
/// Yields `Ok(())` iff `self` has no [`CaveatError`].
pub fn validate(&self) -> Result<(), CaveatError> {
for c in &self.0 { c.validate()? }
impl Caveat {
/// Yields `Ok(())` iff `caveats` have no [`CaveatError`].
pub fn validate_many(caveats: &[Caveat]) -> Result<(), CaveatError> {
for c in caveats { c.validate()? }
Ok(())
}
/// Yields a vector of [`CheckedCaveat`s][CheckedCaveat] iff
/// `self` has no [`CaveatError`].
pub fn check(&self) -> Result<Vec<CheckedCaveat>, CaveatError> {
self.0.iter().map(Caveat::check).collect()
}
}
impl Caveat {
/// Yields `Ok(())` iff `self` has no [`CaveatError`].
pub fn validate(&self) -> Result<(), CaveatError> {
match self {
Caveat::Rewrite(b) => (&**b).validate(),
Caveat::Alts(b) => (&**b).alternatives.iter().map(Rewrite::validate).collect::<Result<(), _>>(),
Caveat::Reject(_) => Ok(()),
Caveat::Unknown(_) => Ok(()), /* it's valid to have unknown caveats, they just won't pass anything */
}
}
/// Yields a vector of [`CheckedCaveat`s][CheckedCaveat] iff
/// `caveats` have no [`CaveatError`].
pub fn check_many(caveats: &[Caveat]) -> Result<Vec<CheckedCaveat>, CaveatError> {
caveats.iter().map(Caveat::check).collect()
}
/// Yields a [`CheckedCaveat`] iff `self` has no [`CaveatError`].
pub fn check(&self) -> Result<CheckedCaveat, CaveatError> {
match self {
Caveat::Rewrite(b) =>
Ok(CheckedCaveat {
alts: vec![ (*b).check()? ]
}),
Ok(CheckedCaveat::Alts(vec![ (*b).check()? ])),
Caveat::Alts(b) => {
let Alts { alternatives } = &**b;
Ok(CheckedCaveat {
alts: alternatives.into_iter().map(Rewrite::check)
.collect::<Result<Vec<CheckedRewrite>, CaveatError>>()?
})
Ok(CheckedCaveat::Alts(
alternatives.into_iter().map(Rewrite::check)
.collect::<Result<Vec<CheckedRewrite>, CaveatError>>()?))
}
Caveat::Reject(b) =>
Ok(CheckedCaveat::Reject(b.pattern.clone())),
Caveat::Unknown(_) =>
Ok(CheckedCaveat::Reject(Pattern::PDiscard(Box::new(PDiscard)))),
}
}
}
@ -187,7 +191,7 @@ impl Template {
match self {
Template::TAttenuate(b) => {
let TAttenuate { template, attenuation } = &**b;
attenuation.validate()?;
Caveat::validate_many(attenuation)?;
Ok(template.implied_binding_count()?)
}
Template::TRef(b) => match usize::try_from(&(&**b).binding) {
@ -273,12 +277,24 @@ impl Rewrite {
impl CheckedCaveat {
/// Rewrites `a` using the patterns/templates contained in `self`.
pub fn rewrite(&self, a: &_Any) -> Option<_Any> {
for (n, p, t) in &self.alts {
let mut bindings = Vec::with_capacity(*n);
if let true = p.matches(a, &mut bindings) {
return t.instantiate(&bindings);
match self {
CheckedCaveat::Alts(alts) => {
for (n, p, t) in alts {
let mut bindings = Vec::with_capacity(*n);
if p.matches(a, &mut bindings) {
return t.instantiate(&bindings);
}
}
None
},
CheckedCaveat::Reject(pat) => {
let mut bindings = Vec::with_capacity(0);
if pat.matches(a, &mut bindings) {
None
} else {
Some(a.clone())
}
}
}
None
}
}

View File

@ -48,6 +48,14 @@ fn signature(key: &[u8], data: &[u8]) -> Vec<u8> {
result
}
fn chain_signature(key: &[u8], chain: &[Caveat]) -> Vec<u8> {
let mut key = key.to_vec();
for c in chain {
key = signature(&key, &encode(&language().unparse(c)));
}
key
}
pub fn new_key() -> Vec<u8> {
let mut buf = vec![0; KEY_LENGTH];
getrandom(&mut buf).expect("successful random number generation");
@ -83,24 +91,15 @@ impl SturdyRef {
unattenuated_target: &_Ptr,
) -> Result<_Ptr, ValidationError> {
self.validate(key).map_err(ValidationError::SignatureError)?;
let mut attenuation = Vec::new();
// TODO:: Make sure of the ordering here!!
for a in self.caveat_chain.iter().rev() {
attenuation.extend(a.0.iter().rev().cloned());
}
let target = unattenuated_target
.attenuate(&Attenuation(attenuation))
.attenuate(&self.caveat_chain)
.map_err(ValidationError::AttenuationError)?;
Ok(target)
}
pub fn validate(&self, key: &[u8]) -> Result<(), MacError> {
let SturdyRef { oid, caveat_chain, sig } = self;
let mut key = key.to_vec();
key = signature(&key, &encode(oid));
for c in caveat_chain {
key = signature(&key, &encode(&language().unparse(c)));
}
let key = chain_signature(&signature(&key, &encode(oid)), caveat_chain);
if &key == sig {
Ok(())
} else {
@ -108,13 +107,13 @@ impl SturdyRef {
}
}
pub fn attenuate(&self, attenuation: &Attenuation) -> Result<Self, CaveatError> {
attenuation.validate()?;
pub fn attenuate(&self, attenuation: &[Caveat]) -> Result<Self, CaveatError> {
Caveat::validate_many(attenuation)?;
let SturdyRef { oid, caveat_chain, sig } = self;
let oid = oid.clone();
let mut caveat_chain = caveat_chain.clone();
caveat_chain.push(attenuation.clone());
let sig = signature(&sig, &encode(&language().unparse(attenuation)));
caveat_chain.extend(attenuation.iter().cloned());
let sig = chain_signature(&sig, attenuation);
Ok(SturdyRef { oid, caveat_chain, sig })
}
}