diff --git a/crates/program_builder/src/lib.rs b/crates/program_builder/src/lib.rs index 4a0dc9c9..90244d98 100644 --- a/crates/program_builder/src/lib.rs +++ b/crates/program_builder/src/lib.rs @@ -1,5 +1,7 @@ #![cfg_attr(test, allow(non_snake_case))] +use std::cell::{Cell, RefCell}; +use std::collections::HashMap; use uplc::ast::{Constant, Name, Program, Term, Unique}; #[cfg(test)] @@ -10,12 +12,16 @@ pub struct Builder { term: Term, } -pub struct Empty { +pub struct NeedsTerm { version: (usize, usize, usize), + // TODO: Hide these two behind interface + next_unique: Cell, + names: RefCell>, } pub struct LambdaBuilder { outer: T, + parameter_name: Name, } pub trait WithTerm @@ -25,25 +31,23 @@ where type Next; fn next(self, term: Term) -> Self::Next; + fn get_name(&self, name_str: &str) -> Name; fn with_constant_int(self, int: isize) -> Self::Next { let term = Term::Constant(Constant::Integer(int)); self.next(term) } - fn with_lambda(self) -> Self::Next { - let text = "i_0".to_string(); - let unique = Unique::new(0); - let parameter_name = Name { text, unique }; - let term = Term::Lambda { + fn with_lambda(self, name_str: &str) -> LambdaBuilder { + let parameter_name = self.get_name(name_str); + LambdaBuilder { + outer: self, parameter_name, - body: Box::new(Term::Constant(Constant::Integer(1))), - }; - self.next(term) + } } } -impl WithTerm for Empty { +impl WithTerm for NeedsTerm { type Next = Builder; fn next(self, term: Term) -> Self::Next { Builder { @@ -51,20 +55,50 @@ impl WithTerm for Empty { term, } } + + // TODO: Remove mut? + fn get_name(&self, name_str: &str) -> Name { + let mut names = self.names.borrow_mut(); + if let Some(unique) = names.get(name_str) { + Name { + text: name_str.to_string(), + unique: *unique, + } + } else { + let next_unique = self.next_unique.get(); + self.next_unique.set(next_unique + 1); + let unique = Unique::new(next_unique); + names.insert(name_str.to_string(), unique.clone()); + Name { + text: name_str.to_string(), + unique, + } + } + } } impl WithTerm for LambdaBuilder { type Next = T::Next; fn next(self, term: Term) -> Self::Next { + let term = Term::Lambda { + parameter_name: self.parameter_name, + body: Box::new(term), + }; self.outer.next(term) } + + fn get_name(&self, name_str: &str) -> Name { + self.outer.get_name(name_str) + } } impl Builder { - pub fn new(maj: usize, min: usize, patch: usize) -> Empty { - Empty { + pub fn new(maj: usize, min: usize, patch: usize) -> NeedsTerm { + NeedsTerm { version: (maj, min, patch), + next_unique: Cell::new(0), + names: RefCell::new(HashMap::new()), } } diff --git a/crates/program_builder/src/tests.rs b/crates/program_builder/src/tests.rs index ded6559c..116b8de2 100644 --- a/crates/program_builder/src/tests.rs +++ b/crates/program_builder/src/tests.rs @@ -41,20 +41,24 @@ fn build_named__with_lam() { (lam i_0 (con integer 1)) )"; let expected = parser::program(code).unwrap(); - let actual = Builder::new(1, 2, 3).with_lambda().build_named(); + let actual = Builder::new(1, 2, 3) + .with_lambda("i_0") + .with_constant_int(1) + .build_named(); assert_eq!(expected, actual); } -// #[test] -// fn build_named__with_nested_lam() { -// let code = r"(program -// 1.2.3 -// (lam i_0 (lam i_1 (con integer 1))) -// )"; -// let expected = parser::program(code).unwrap(); -// let actual = Builder::default() -// .with_version(1, 2, 3) -// .with_lambda(1) -// .build_named(); -// assert_eq!(expected, actual); -// } +#[test] +fn build_named__with_nested_lam() { + let code = r"(program + 1.2.3 + (lam i_0 (lam i_1 (con integer 1))) + )"; + let expected = parser::program(code).unwrap(); + let actual = Builder::new(1, 2, 3) + .with_lambda("i_0") + .with_lambda("i_1") + .with_constant_int(1) + .build_named(); + assert_eq!(expected, actual); // TODO: This should fail +}