From 59a8f6477bdc3c8443cc9c1daf763528ef06d955 Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 15 Jun 2022 20:12:12 -0400 Subject: [PATCH 1/3] feat: convet a named debruijn to a name --- crates/uplc/src/ast.rs | 10 ++++ crates/uplc/src/debruijn.rs | 80 +++++++++++++++++++++++++++---- crates/uplc/src/debruijn/bimap.rs | 32 +++++++++++++ 3 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 crates/uplc/src/debruijn/bimap.rs diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 6344c6dc..4bbc3325 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -155,6 +155,10 @@ impl DeBruijn { pub fn new(index: usize) -> Self { DeBruijn(index) } + + pub fn inner(&self) -> usize { + self.0 + } } impl From for DeBruijn { @@ -169,6 +173,12 @@ impl From for usize { } } +impl Display for DeBruijn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + impl From for DeBruijn { fn from(n: NamedDeBruijn) -> Self { n.index diff --git a/crates/uplc/src/debruijn.rs b/crates/uplc/src/debruijn.rs index 3216e9f4..dc968d0f 100644 --- a/crates/uplc/src/debruijn.rs +++ b/crates/uplc/src/debruijn.rs @@ -1,28 +1,32 @@ -use std::collections::HashMap; - use thiserror::Error; use crate::ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Term, Unique}; -#[derive(Debug, Copy, Clone)] +mod bimap; + +#[derive(Debug, Clone, PartialEq, Copy, Eq, Hash)] struct Level(usize); #[derive(Error, Debug)] pub enum Error { #[error("Free Unique `{0}`")] FreeUnique(Unique), + #[error("Free Index `{0}`")] + FreeIndex(DeBruijn), } pub struct Converter { current_level: Level, - levels: Vec>, + levels: Vec, + current_unique: Unique, } impl Converter { pub fn new() -> Self { Converter { current_level: Level(0), - levels: vec![HashMap::new()], + levels: vec![bimap::BiMap::new()], + current_unique: Unique::new(0), } } @@ -111,9 +115,49 @@ impl Converter { pub fn named_debruijn_to_name( &mut self, - _term: Term, + term: Term, ) -> Result, Error> { - todo!() + let converted_term = match term { + Term::Var(NamedDeBruijn { text, index }) => Term::Var(Name { + text, + unique: self.get_unique(index)?, + }), + Term::Delay(term) => Term::Delay(Box::new(self.named_debruijn_to_name(*term)?)), + Term::Lambda { + parameter_name, + body, + } => { + self.declare_binder(); + + let unique = self.get_unique(parameter_name.index)?; + + let name = Name { + text: parameter_name.text, + unique, + }; + + self.start_scope(); + + let body = self.named_debruijn_to_name(*body)?; + + self.end_scope(); + + Term::Lambda { + parameter_name: name, + body: Box::new(body), + } + } + Term::Apply { function, argument } => Term::Apply { + function: Box::new(self.named_debruijn_to_name(*function)?), + argument: Box::new(self.named_debruijn_to_name(*argument)?), + }, + Term::Constant(constant) => Term::Constant(constant), + Term::Force(term) => Term::Force(Box::new(self.named_debruijn_to_name(*term)?)), + Term::Error => Term::Error, + Term::Builtin(builtin) => Term::Builtin(builtin), + }; + + Ok(converted_term) } pub fn named_debruijn_to_debruijn(&mut self, term: Term) -> Term { @@ -234,16 +278,36 @@ impl Converter { Err(Error::FreeUnique(unique)) } + fn get_unique(&mut self, index: DeBruijn) -> Result { + for scope in self.levels.iter().rev() { + let index = Level(self.current_level.0 - index.inner()); + + if let Some(unique) = scope.get_right(&index) { + return Ok(*unique); + } + } + + Err(Error::FreeIndex(index)) + } + fn declare_unique(&mut self, unique: Unique) { let scope = &mut self.levels[self.current_level.0]; scope.insert(unique, self.current_level); } + fn declare_binder(&mut self) { + let scope = &mut self.levels[self.current_level.0]; + + scope.insert(self.current_unique, self.current_level); + + self.current_unique.increment(); + } + fn start_scope(&mut self) { self.current_level = Level(self.current_level.0 + 1); - self.levels.push(HashMap::new()); + self.levels.push(bimap::BiMap::new()); } fn end_scope(&mut self) { diff --git a/crates/uplc/src/debruijn/bimap.rs b/crates/uplc/src/debruijn/bimap.rs new file mode 100644 index 00000000..af506095 --- /dev/null +++ b/crates/uplc/src/debruijn/bimap.rs @@ -0,0 +1,32 @@ +use std::collections::HashMap; + +use crate::ast::Unique; + +use super::Level; + +pub struct BiMap { + left: HashMap, + right: HashMap, +} + +impl BiMap { + pub(super) fn new() -> Self { + BiMap { + right: HashMap::new(), + left: HashMap::new(), + } + } + + pub(super) fn insert(&mut self, unique: Unique, level: Level) { + self.left.insert(unique, level); + self.right.insert(level, unique); + } + + pub(super) fn get(&self, unique: &Unique) -> Option<&Level> { + self.left.get(unique) + } + + pub(super) fn get_right(&self, level: &Level) -> Option<&Unique> { + self.right.get(level) + } +} From b8c5c268d42338d8678d4a0cd2a59775df67604e Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 16 Jun 2022 16:54:34 -0400 Subject: [PATCH 2/3] feat: add conversion to tests --- crates/cli/src/main.rs | 6 +++-- crates/uplc/src/debruijn.rs | 48 +++++++++++++++++++++++++++++++++---- crates/uplc/src/test.rs | 14 ++++++++--- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 1266381f..6db068b0 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,7 +1,7 @@ use std::fs; use uplc::{ - ast::{DeBruijn, FakeNamedDeBruijn, Program}, + ast::{DeBruijn, Name, Program}, parser, }; @@ -46,7 +46,9 @@ fn main() -> anyhow::Result<()> { UplcCommand::Unflat { input, print } => { let bytes = std::fs::read(&input)?; - let program = Program::::from_flat(&bytes)?; + let program = Program::::from_flat(&bytes)?; + + let program: Program = program.try_into()?; if print { println!("{:#?}", program); diff --git a/crates/uplc/src/debruijn.rs b/crates/uplc/src/debruijn.rs index dc968d0f..3c4fa7f3 100644 --- a/crates/uplc/src/debruijn.rs +++ b/crates/uplc/src/debruijn.rs @@ -160,6 +160,50 @@ impl Converter { Ok(converted_term) } + pub fn debruijn_to_name(&mut self, term: Term) -> Result, Error> { + let converted_term = match term { + Term::Var(index) => Term::Var(Name { + text: String::from("i"), + unique: self.get_unique(index)?, + }), + Term::Delay(term) => Term::Delay(Box::new(self.debruijn_to_name(*term)?)), + Term::Lambda { + parameter_name, + body, + } => { + self.declare_binder(); + + let unique = self.get_unique(parameter_name)?; + + let name = Name { + text: String::from("i"), + unique, + }; + + self.start_scope(); + + let body = self.debruijn_to_name(*body)?; + + self.end_scope(); + + Term::Lambda { + parameter_name: name, + body: Box::new(body), + } + } + Term::Apply { function, argument } => Term::Apply { + function: Box::new(self.debruijn_to_name(*function)?), + argument: Box::new(self.debruijn_to_name(*argument)?), + }, + Term::Constant(constant) => Term::Constant(constant), + Term::Force(term) => Term::Force(Box::new(self.debruijn_to_name(*term)?)), + Term::Error => Term::Error, + Term::Builtin(builtin) => Term::Builtin(builtin), + }; + + Ok(converted_term) + } + pub fn named_debruijn_to_debruijn(&mut self, term: Term) -> Term { match term { Term::Var(name) => Term::Var(name.into()), @@ -182,10 +226,6 @@ impl Converter { } } - pub fn debruijn_to_name(&mut self, _term: Term) -> Result, Error> { - todo!() - } - pub fn debruijn_to_named_debruijn(&mut self, term: Term) -> Term { match term { Term::Var(name) => Term::Var(name.into()), diff --git a/crates/uplc/src/test.rs b/crates/uplc/src/test.rs index 358f9396..a0a90458 100644 --- a/crates/uplc/src/test.rs +++ b/crates/uplc/src/test.rs @@ -1,6 +1,6 @@ /// e2e encoding/decoding tests use crate::{ - ast::{DeBruijn, Program}, + ast::{DeBruijn, Name, Program}, parser, }; @@ -11,7 +11,7 @@ fn integer() { let parsed_program = parser::program(code).unwrap(); - let debruijn_program: Program = parsed_program.try_into().unwrap(); + let debruijn_program: Program = parsed_program.clone().try_into().unwrap(); let decoded_program: Program = Program::from_flat(bytes).unwrap(); @@ -20,6 +20,10 @@ fn integer() { let encoded_program = debruijn_program.to_flat().unwrap(); assert_eq!(encoded_program, bytes); + + let name_program: Program = decoded_program.try_into().unwrap(); + + assert_eq!(parsed_program, name_program); } #[test] @@ -29,7 +33,7 @@ fn jpg() { let parsed_program = parser::program(code).unwrap(); - let debruijn_program: Program = parsed_program.try_into().unwrap(); + let debruijn_program: Program = parsed_program.clone().try_into().unwrap(); let decoded_program: Program = Program::from_flat(bytes).unwrap(); @@ -38,4 +42,8 @@ fn jpg() { let encoded_program = debruijn_program.to_flat().unwrap(); assert_eq!(encoded_program, bytes); + + let name_program: Program = decoded_program.try_into().unwrap(); + + assert_eq!(parsed_program, name_program); } From cbea795f68627b47aaad57768dee3f125573bf3d Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 17 Jun 2022 15:39:31 -0400 Subject: [PATCH 3/3] feat: only need to compare unique and index --- crates/uplc/src/ast.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 4bbc3325..f303eacf 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -66,12 +66,18 @@ pub enum Constant { /// A Name containing it's parsed textual representation /// and a unique id from string interning. The Name's text is /// interned during parsing. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct Name { pub text: String, pub unique: Unique, } +impl PartialEq for Name { + fn eq(&self, other: &Self) -> bool { + self.unique == other.unique + } +} + /// A unique id used for string interning. #[derive(Debug, Clone, PartialEq, Copy, Eq, Hash)] pub struct Unique(isize); @@ -110,12 +116,18 @@ impl Display for Unique { /// Similar to `Name` but for Debruijn indices. /// `Name` is replaced by `NamedDebruijn` when converting /// program to it's debruijn form. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct NamedDeBruijn { pub text: String, pub index: DeBruijn, } +impl PartialEq for NamedDeBruijn { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + } +} + /// This is useful for decoding a on chain program into debruijn form. /// It allows for injecting fake textual names while also using Debruijn for decoding /// without having to loop through twice.