Skip to content

Commit

Permalink
WIP: implementing abstract binding trees
Browse files Browse the repository at this point in the history
  • Loading branch information
mx00s committed Dec 5, 2020
1 parent daa9018 commit 1d6bf4e
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 29 deletions.
205 changes: 205 additions & 0 deletions src/abt.rs
@@ -0,0 +1,205 @@
//! Abstracting binding trees

// binding
// scope

// placeholder binding: variable
// operator index: parameter

// identifier property: reference to its binding

use crate::errors::{ArgumentSortMismatch, InvalidOperation};
use std::{
cmp::Ordering,
convert::{TryFrom, TryInto},
marker::PhantomData,
};

/// Invalid operation for [Abt]
pub type InvalidOperationError<V, O, S> = InvalidOperation<Valence<S>, Abstractor<V, O, S>>;

/// Leaf node of an abstract binding tree indexed by some sort, `S`
pub trait Variable<S> {
/// Sort of syntactic element
fn sort(&self) -> &S;
}

/// TODO
#[derive(Clone)]
pub struct Valence<S> {
/// TODO
pub variables: Vec<S>,
/// TODO
pub sort: S,
}

/// Non-leaf node of an abstract binding tree indexed by some sort, `S`
pub trait Operator<S> {
/// Sort of syntactic element
fn sort(&self) -> &S;

/// Expected sorts of the operands and their respective variables
fn arity(&self) -> Vec<Valence<S>>;

/// Apply to the expected number and sorts of operands to construct an [Abt]
///
/// # Errors
///
/// Returns an [`InvalidOperation`] if `args` has the wrong number or sorts of arguments (see [`Operator::arity`]).
fn apply<V>(
self,
args: &[Abstractor<V, Self, S>],
) -> Result<Abt<V, Self, S>, InvalidOperationError<V, Self, S>>
where
V: Clone + Variable<S>,
Self: Clone + Operator<S> + Sized,
S: Clone + PartialEq,
{
Abt::try_from((self, args))
}
}

/// TODO
#[derive(Clone, Debug)]
pub struct Abstractor<V, O, S> {
/// TODO
pub variables: Vec<V>,
/// TODO
pub tree: Abt<V, O, S>,
}

#[derive(Clone, Debug)]
pub(crate) enum Node<V, O, S> {
Variable(V, PhantomData<S>),
Operation(O, Vec<Abstractor<V, O, S>>),
}

/// TODO
#[derive(Clone, Debug)]
pub struct Abt<V, O, S>(pub(crate) Node<V, O, S>);

impl<V, O, S> From<V> for Abt<V, O, S> {
fn from(x: V) -> Self {
Self(Node::Variable(x, PhantomData))
}
}

impl<V, O, S> TryFrom<(O, &[Abstractor<V, O, S>])> for Node<V, O, S>
where
V: Clone + Variable<S>,
O: Clone + Operator<S>,
S: Clone + PartialEq,
{
type Error = InvalidOperationError<V, O, S>;

fn try_from((operator, args): (O, &[Abstractor<V, O, S>])) -> Result<Self, Self::Error> {
Self::from_op(operator, args)
}
}

impl<V, O, S> TryFrom<(O, &[Abstractor<V, O, S>])> for Abt<V, O, S>
where
V: Clone + Variable<S>,
O: Clone + Operator<S>,
S: Clone + PartialEq,
{
type Error = InvalidOperationError<V, O, S>;

fn try_from((operator, args): (O, &[Abstractor<V, O, S>])) -> Result<Self, Self::Error> {
Ok(Self((operator, args).try_into()?))
}
}

impl<V, O, S> Abstractor<V, O, S> {
fn sort(&self) -> &S
where
V: Variable<S>,
O: Operator<S>,
{
self.tree.sort()
}
}

impl<V, O, S> Abt<V, O, S> {
fn sort(&self) -> &S
where
V: Variable<S>,
O: Operator<S>,
{
self.0.sort()
}
}

impl<V, O, S> Node<V, O, S> {
fn sort(&self) -> &S
where
V: Variable<S>,
O: Operator<S>,
{
match self {
Self::Variable(x, _) => x.sort(),
Self::Operation(operator, _) => operator.sort(),
}
}

fn from_op(
operator: O,
args: &[Abstractor<V, O, S>],
) -> Result<Self, InvalidOperationError<V, O, S>>
where
V: Clone + Variable<S>,
O: Clone + Operator<S>,
S: Clone + PartialEq,
{
// TODO: address the "freshness condition on binders" refinement of part 2 of the definition on p. 8

let arity = operator.arity();

match args.len().cmp(&arity.len()) {
Ordering::Greater => Err(InvalidOperation::TooManyArguments(args.len() - arity.len())),
Ordering::Less => Err(InvalidOperation::TooFewArguments(arity.len() - args.len())),
Ordering::Equal => {
let bad_args: Vec<ArgumentSortMismatch<Valence<S>, Abstractor<V, O, S>>> = args
.iter()
.enumerate()
.zip(arity.iter())
.filter_map(|((index, arg), valence)| {
if *arg.sort() == valence.sort
&& arg
.variables
.iter()
.map(|v| v.sort().clone())
.collect::<Vec<S>>()
== valence.variables
{
None
} else {
Some(ArgumentSortMismatch {
index,
parameter: (*valence).clone(),
argument: (*arg).clone(),
})
}
})
.collect();

if bad_args.is_empty() {
Ok(Self::Operation(operator, args.to_vec()))
} else {
Err(InvalidOperation::SortMismatches(bad_args))
}
}
}
}
}

#[cfg(tesT)]
mod tests {
#[test]
fn example_abt() -> Abt {
// let x be a_1 in a_2
// let(a_1; x.a_2) // the x is bound within a_2, but not a_1

// let x be x * x in x + x
}
}
3 changes: 0 additions & 3 deletions src/ast.rs
Expand Up @@ -11,9 +11,6 @@ mod tests;
mod types;
pub use self::types::*;

mod errors;
pub use self::errors::*;

#[cfg(feature = "with-proptest")]
mod with_proptest;
#[cfg(feature = "with-proptest")]
Expand Down
1 change: 1 addition & 0 deletions src/ast/tests.rs
@@ -1,6 +1,7 @@
#![allow(non_snake_case)]

use super::*;
use crate::errors::{ArgumentSortMismatch, InvalidOperation, InvalidSubstitution};

use serde::{Deserialize, Serialize};
use std::fmt;
Expand Down
17 changes: 11 additions & 6 deletions src/ast/types.rs
@@ -1,20 +1,25 @@
use super::{ArgumentSortMismatch, InvalidOperation, InvalidSubstitution};
use crate::errors::{ArgumentSortMismatch, InvalidOperation, InvalidSubstitution};

use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, convert::TryFrom, marker::PhantomData};

// TODO: Determine whether `Ast`s and `Operator`s need another generic for the active parameters described on p. 6, 10, and ch. 34.

/// Leaf node of an abstract syntax tree indexed by some sort, `S`
pub trait Variable<S> {
/// Sort of syntactic element
fn sort(&self) -> &S;
}

/// Invalid operation for [Ast]
pub type InvalidOperationError<V, O, S> = InvalidOperation<S, Ast<V, O, S>>;

/// Non-leaf node of an abstract syntax tree indexed by some sort, `S`
pub trait Operator<S> {
/// Sort of syntactic element
fn sort(&self) -> &S;

/// Expected sorts of [Ast] operands
/// Expected sorts of the operands
fn arity(&self) -> Vec<S>;

/// Apply to the expected number and sorts of operands to construct an [Ast]
Expand All @@ -25,10 +30,10 @@ pub trait Operator<S> {
fn apply<V>(
self,
args: &[Ast<V, Self, S>],
) -> Result<Ast<V, Self, S>, InvalidOperation<V, Self, S>>
) -> Result<Ast<V, Self, S>, InvalidOperationError<V, Self, S>>
where
V: Clone + Variable<S>,
Self: Clone + Operator<S> + Sized,
Self: Clone + Sized,
S: Clone + PartialEq,
{
Ast::try_from((self, args))
Expand Down Expand Up @@ -60,7 +65,7 @@ where
O: Clone + Operator<S>,
S: Clone + PartialEq,
{
type Error = InvalidOperation<V, O, S>;
type Error = InvalidOperation<S, Self>;

fn try_from((operator, args): (O, &[Self])) -> Result<Self, Self::Error> {
Ok(Self(Node::from_op(
Expand All @@ -82,7 +87,7 @@ impl<V, O, S> Node<V, O, S> {
}
}

fn from_op(operator: O, args: &[Self]) -> Result<Self, InvalidOperation<V, O, S>>
fn from_op(operator: O, args: &[Self]) -> Result<Self, InvalidOperation<S, Ast<V, O, S>>>
where
V: Clone + Variable<S>,
O: Clone + Operator<S>,
Expand Down
38 changes: 18 additions & 20 deletions src/ast/errors.rs → src/errors.rs
@@ -1,60 +1,58 @@
use super::Ast;
//! TODO

use std::{error::Error, fmt};

/// Error matching [Ast] with sort expected by [Operator](`super::Operator`)
/// Error matching tree with sort expected by operator
#[derive(Debug, PartialEq)]
pub struct ArgumentSortMismatch<V, O, S> {
pub struct ArgumentSortMismatch<S, A> {
/// Operand position
pub index: usize,
/// Expected sort
pub parameter: S,
/// Actual [Ast] operand
pub argument: Ast<V, O, S>,
/// Actual operand
pub argument: A,
}

/// Error constructing operation from [Operator](`super::Operator`) and [Ast] arguments
/// Error constructing operation from operator and arguments
#[derive(Debug, PartialEq)]
pub enum InvalidOperation<V, O, S> {
pub enum InvalidOperation<S, A> {
/// Operator expects more arguments
TooFewArguments(usize),
/// Operator expects fewer arguments
TooManyArguments(usize),
/// Operator expects arguments of different sorts
SortMismatches(Vec<ArgumentSortMismatch<V, O, S>>),
SortMismatches(Vec<ArgumentSortMismatch<S, A>>),
}

/// Error substituting [Ast] in place of [Variable](`super::Variable`) due to misaligned sorts, `S`
/// Error substituting tree in place of variable due to misaligned sorts, `S`
#[derive(Debug, PartialEq)]
pub struct InvalidSubstitution<S> {
/// Sort expected by the variable
pub subject: S,
/// Sort of the [Ast] to substitute for the variable
/// Sort of the tree to substitute for the variable
pub target: S,
}

impl<V, O, S> Error for ArgumentSortMismatch<V, O, S>
impl<S, A> Error for ArgumentSortMismatch<S, A>
where
V: fmt::Debug,
O: fmt::Debug,
S: fmt::Debug,
A: fmt::Debug,
{
}

impl<V, O, S> Error for InvalidOperation<V, O, S>
impl<S, A> Error for InvalidOperation<S, A>
where
V: fmt::Debug,
O: fmt::Debug,
S: fmt::Debug,
A: fmt::Debug,
{
}

impl<S> Error for InvalidSubstitution<S> where S: fmt::Debug {}

impl<V, O, S> fmt::Display for ArgumentSortMismatch<V, O, S>
impl<S, A> fmt::Display for ArgumentSortMismatch<S, A>
where
S: fmt::Debug,
Ast<V, O, S>: fmt::Debug,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
Expand All @@ -65,10 +63,10 @@ where
}
}

impl<V, O, S> fmt::Display for InvalidOperation<V, O, S>
impl<S, A> fmt::Display for InvalidOperation<S, A>
where
S: fmt::Debug,
Ast<V, O, S>: fmt::Debug,
A: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Expand Up @@ -17,6 +17,9 @@
#![deny(clippy::pedantic)]
#![allow(clippy::unit_arg)] // catches false positives due to single-element enum variants

pub mod errors;

pub mod ast;

// TODO: abstract binding trees
pub mod abt;

0 comments on commit 1d6bf4e

Please sign in to comment.