Move program builder to uplc crate
This commit is contained in:
@@ -18,3 +18,7 @@ hex = "0.4.3"
|
||||
peg = "0.8.0"
|
||||
pretty = "0.11.3"
|
||||
thiserror = "1.0.31"
|
||||
|
||||
[features]
|
||||
unstable = []
|
||||
test = ["unstable"]
|
||||
|
||||
@@ -5,5 +5,5 @@ mod flat;
|
||||
pub mod parser;
|
||||
mod pretty;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
#[cfg(any(feature = "unstable", test))]
|
||||
pub mod program_builder;
|
||||
|
||||
111
crates/uplc/src/program_builder.rs
Normal file
111
crates/uplc/src/program_builder.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
#![cfg_attr(test, allow(non_snake_case))]
|
||||
|
||||
use crate::ast::{Constant, Name, Program, Term, Unique};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub struct Builder {
|
||||
version: (usize, usize, usize),
|
||||
term: Term<Name>,
|
||||
}
|
||||
|
||||
pub struct NeedsTerm {
|
||||
version: (usize, usize, usize),
|
||||
// TODO: Hide these two behind interface
|
||||
next_unique: Cell<isize>,
|
||||
names: RefCell<HashMap<String, Unique>>,
|
||||
}
|
||||
|
||||
pub struct LambdaBuilder<T> {
|
||||
outer: T,
|
||||
parameter_name: Name,
|
||||
}
|
||||
|
||||
pub trait WithTerm
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
type Next;
|
||||
|
||||
fn next(self, term: Term<Name>) -> 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, name_str: &str) -> LambdaBuilder<Self> {
|
||||
let parameter_name = self.get_name(name_str);
|
||||
LambdaBuilder {
|
||||
outer: self,
|
||||
parameter_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WithTerm for NeedsTerm {
|
||||
type Next = Builder;
|
||||
fn next(self, term: Term<Name>) -> Self::Next {
|
||||
Builder {
|
||||
version: self.version,
|
||||
term,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
Name {
|
||||
text: name_str.to_string(),
|
||||
unique,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WithTerm> WithTerm for LambdaBuilder<T> {
|
||||
type Next = T::Next;
|
||||
|
||||
fn next(self, term: Term<Name>) -> 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 {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
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()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_named(&self) -> Program<Name> {
|
||||
Program {
|
||||
version: self.version,
|
||||
term: self.term.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
64
crates/uplc/src/program_builder/tests.rs
Normal file
64
crates/uplc/src/program_builder/tests.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use super::*;
|
||||
use crate::parser;
|
||||
|
||||
#[test]
|
||||
fn build_named__with_const() {
|
||||
let code = r"(program
|
||||
11.22.33
|
||||
(con integer 11)
|
||||
)";
|
||||
let expected = parser::program(code).unwrap();
|
||||
let actual = Builder::new(11, 22, 33).with_constant_int(11).build_named();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_named__with_different_const() {
|
||||
let code = r"(program
|
||||
11.22.33
|
||||
(con integer 22)
|
||||
)";
|
||||
let expected = parser::program(code).unwrap();
|
||||
let actual = Builder::new(11, 22, 33).with_constant_int(22).build_named();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_named__with_const_different_version() {
|
||||
let code = r"(program
|
||||
44.55.66
|
||||
(con integer 11)
|
||||
)";
|
||||
let expected = parser::program(code).unwrap();
|
||||
let actual = Builder::new(44, 55, 66).with_constant_int(11).build_named();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_named__with_lam() {
|
||||
let code = r"(program
|
||||
1.2.3
|
||||
(lam i_0 (con integer 1))
|
||||
)";
|
||||
let expected = parser::program(code).unwrap();
|
||||
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::new(1, 2, 3)
|
||||
.with_lambda("i_0")
|
||||
.with_lambda("i_1")
|
||||
.with_constant_int(1)
|
||||
.build_named();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/// e2e encoding/decoding tests
|
||||
use crate::{
|
||||
ast::{DeBruijn, Name, Program},
|
||||
parser,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
let bytes = include_bytes!("../test_data/basic/integer/integer.flat");
|
||||
let code = include_str!("../test_data/basic/integer/integer.uplc");
|
||||
|
||||
let parsed_program = parser::program(code).unwrap();
|
||||
|
||||
let debruijn_program: Program<DeBruijn> = parsed_program.clone().try_into().unwrap();
|
||||
|
||||
let decoded_program: Program<DeBruijn> = Program::from_flat(bytes).unwrap();
|
||||
|
||||
assert_eq!(debruijn_program, decoded_program);
|
||||
|
||||
let encoded_program = debruijn_program.to_flat().unwrap();
|
||||
|
||||
assert_eq!(encoded_program, bytes);
|
||||
|
||||
let name_program: Program<Name> = decoded_program.try_into().unwrap();
|
||||
|
||||
assert_eq!(parsed_program, name_program);
|
||||
|
||||
let pretty = name_program.to_pretty();
|
||||
|
||||
assert_eq!(pretty, code);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn jpg() {
|
||||
let bytes = include_bytes!("../test_data/jpg/jpg.flat");
|
||||
let code = include_str!("../test_data/jpg/jpg.uplc");
|
||||
|
||||
let parsed_program = parser::program(code).unwrap();
|
||||
|
||||
let debruijn_program: Program<DeBruijn> = parsed_program.clone().try_into().unwrap();
|
||||
|
||||
let decoded_program: Program<DeBruijn> = Program::from_flat(bytes).unwrap();
|
||||
|
||||
assert_eq!(debruijn_program, decoded_program);
|
||||
|
||||
let encoded_program = debruijn_program.to_flat().unwrap();
|
||||
|
||||
assert_eq!(encoded_program, bytes);
|
||||
|
||||
let name_program: Program<Name> = decoded_program.try_into().unwrap();
|
||||
|
||||
assert_eq!(parsed_program, name_program);
|
||||
|
||||
let pretty = name_program.to_pretty();
|
||||
|
||||
assert_eq!(pretty, code);
|
||||
}
|
||||
@@ -74,7 +74,6 @@ fn fibonacci() {
|
||||
round_trip_test(bytes, code);
|
||||
}
|
||||
|
||||
// TODO: This is failing, see Bug: https://github.com/txpipe/aiken/issues/10
|
||||
#[test]
|
||||
fn one_way_fibonacci() {
|
||||
let bytes = include_bytes!("../test_data/fibonacci/fibonacci.flat");
|
||||
|
||||
Reference in New Issue
Block a user