diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index fd4bb5e8..51352d60 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -11,7 +11,13 @@ fn main() -> anyhow::Result<()> { println!("{:#?}", program); - println!("{:?}", program.flat()?); + let flat_bytes = program.flat()?; + + for byte in flat_bytes { + print!("{:08b} ", byte); + } + + println!(); println!("{}", program.flat_hex()?); diff --git a/crates/flat/src/encode.rs b/crates/flat/src/encode.rs index 546a251f..bcf077f7 100644 --- a/crates/flat/src/encode.rs +++ b/crates/flat/src/encode.rs @@ -28,6 +28,14 @@ impl Encode for isize { } } +impl Encode for usize { + fn encode(&self, e: &mut Encoder) -> Result<(), String> { + e.word(*self); + + Ok(()) + } +} + impl Encode for char { fn encode(&self, e: &mut Encoder) -> Result<(), String> { e.char(*self)?; diff --git a/crates/flat/src/encoder.rs b/crates/flat/src/encoder.rs index 2bf0efa7..78e83a17 100644 --- a/crates/flat/src/encoder.rs +++ b/crates/flat/src/encoder.rs @@ -1,4 +1,4 @@ -use crate::encode::Encode; +use crate::{encode::Encode, zigzag}; pub struct Encoder { pub buffer: Vec, @@ -66,12 +66,13 @@ impl Encoder { } pub fn integer(&mut self, i: isize) -> Result<&mut Self, String> { + let i = zigzag::to_usize(i); self.word(i); Ok(self) } pub fn char(&mut self, c: char) -> Result<&mut Self, String> { - self.word(c as isize); + self.word(c as usize); Ok(self) } @@ -132,7 +133,7 @@ impl Encoder { self.write_blk(arr, src_ptr); } - fn word(&mut self, c: isize) { + pub fn word(&mut self, c: usize) { loop { let mut w = (c & 127) as u8; let c = c >> 7; diff --git a/crates/flat/src/lib.rs b/crates/flat/src/lib.rs index e9b3b107..8d7d14f1 100644 --- a/crates/flat/src/lib.rs +++ b/crates/flat/src/lib.rs @@ -1,6 +1,7 @@ mod encode; mod encoder; mod filler; +mod zigzag; pub mod en { pub use super::encode::*; diff --git a/crates/flat/src/zigzag.rs b/crates/flat/src/zigzag.rs new file mode 100644 index 00000000..ed8f3108 --- /dev/null +++ b/crates/flat/src/zigzag.rs @@ -0,0 +1,27 @@ +pub fn to_usize(x: isize) -> usize { + let double_x = x << 1; + + if x >= 0 { + double_x as usize + } else { + (-double_x - 1) as usize + } +} + +pub fn to_isize(u: usize) -> isize { + let s = u as isize; + + (s >> 1) ^ -(s & 1) +} + +#[cfg(test)] +mod test { + #[test] + fn convert() { + let n = -12; + let unsigned = super::to_usize(n); + let signed = super::to_isize(unsigned); + + assert_eq!(n, signed) + } +} diff --git a/crates/uplc/example/plutus-core b/crates/uplc/example/plutus-core index f060918b..7e4c5e38 100644 --- a/crates/uplc/example/plutus-core +++ b/crates/uplc/example/plutus-core @@ -1,3 +1,3 @@ -(program 1.0.0 - (con bool False) +(program 11.22.33 + (con integer 11) ) \ No newline at end of file diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index 69f61970..b5914863 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -8,7 +8,7 @@ const CONST_TAG_WIDTH: u32 = 4; #[derive(Debug, Clone)] pub struct Program { - pub version: String, + pub version: (usize, usize, usize), pub term: Term, } @@ -97,7 +97,12 @@ pub fn encode_constant_tag(tag: u8, e: &mut Encoder) -> Result<(), String> { impl Encode for Program { fn encode(&self, e: &mut Encoder) -> Result<(), String> { - self.version.encode(e)?; + let (major, minor, patch) = self.version; + + major.encode(e)?; + minor.encode(e)?; + patch.encode(e)?; + self.term.encode(e)?; Ok(()) @@ -123,7 +128,6 @@ impl Encode for Term { encode_term_tag(2, e)?; // need to create encoding for Binder todo!(); - body.encode(e)?; } Term::Apply { function, argument } => { encode_term_tag(3, e)?; @@ -169,12 +173,17 @@ impl Encode for &Constant { } Constant::String(s) => { encode_constant(2, e)?; - s.encode(e)?; + s.as_bytes().encode(e)?; } // there is no char constant tag Constant::Char(c) => { c.encode(e)?; - todo!() + + let mut b = [0; 4]; + + let s = c.encode_utf8(&mut b); + + s.as_bytes().encode(e)?; } Constant::Unit => encode_constant(3, e)?, Constant::Bool(b) => { @@ -186,3 +195,23 @@ impl Encode for &Constant { Ok(()) } } + +#[cfg(test)] +mod test { + use super::{Constant, Program, Term}; + + #[test] + fn flat_encode_integer() { + let program = Program { + version: (11, 22, 33), + term: Term::Constant(Constant::Integer(11)), + }; + + let bytes = program.flat().unwrap(); + + assert_eq!( + bytes, + vec![0b00001011, 0b00010110, 0b00100001, 0b01001000, 0b00000101, 0b10000001] + ) + } +} diff --git a/crates/uplc/src/parser.rs b/crates/uplc/src/parser.rs index 051f9dac..5e1b628b 100644 --- a/crates/uplc/src/parser.rs +++ b/crates/uplc/src/parser.rs @@ -16,7 +16,7 @@ use crate::{ pub fn program(src: &str) -> anyhow::Result { let mut parser = program_(); - let result = parser.easy_parse(position::Stream::new(src)); + let result = parser.easy_parse(position::Stream::new(src.trim())); match result { Ok((program, _)) => Ok(program), @@ -37,7 +37,7 @@ where between(token('('), token(')'), prog).skip(spaces()) } -fn version() -> impl Parser +fn version() -> impl Parser where Input: Stream, Input::Error: ParseError, @@ -51,7 +51,11 @@ where ) .map( |(major, _, minor, _, patch): (String, char, String, char, String)| { - format!("{}.{}.{}", major, minor, patch) + ( + major.parse::().unwrap(), + minor.parse::().unwrap(), + patch.parse::().unwrap(), + ) }, ) } @@ -203,7 +207,7 @@ where string("integer") .with(skip_many1(space())) .with(many1(digit())) - .map(|d: String| Constant::Integer(d.parse::().unwrap())) + .map(|d: String| Constant::Integer(d.parse::().unwrap())) } fn constant_bytestring() -> impl Parser @@ -253,18 +257,19 @@ where #[cfg(test)] mod test { - use combine::Parser; - - const CODE: &str = include_str!("../example/plutus-core"); - #[test] fn parse_program() { - let result = super::program_().parse(CODE); + let code = r#" + (program 11.22.33 + (con integer 11) + ) + "#; + let result = super::program(code); assert!(result.is_ok()); - let program = result.unwrap().0; + let program = result.unwrap(); - assert_eq!(program.version, "1.0.0"); + assert_eq!(program.version, (11, 22, 33)); } }