feat: builtin encoding

Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
Kasey White 2022-05-28 00:23:20 -04:00
parent c01469ea51
commit 41487733f7
6 changed files with 247 additions and 231 deletions

View File

@ -1,13 +1,13 @@
pub enum Filler { pub enum Filler {
FillerStart(Box<Filler>), // FillerStart(Box<Filler>),
FillerEnd, FillerEnd,
} }
impl Filler { // impl Filler {
pub fn len(&self) -> usize { // pub fn len(&self) -> usize {
match self { // match self {
Filler::FillerStart(f) => f.len() + 1, // Filler::FillerStart(f) => f.len() + 1,
Filler::FillerEnd => 1, // Filler::FillerEnd => 1,
} // }
} // }
} // }

View File

@ -1,7 +1,7 @@
mod encode; mod encode;
mod encoder; mod encoder;
mod filler; mod filler;
mod zigzag; pub mod zigzag;
pub mod en { pub mod en {
pub use super::encode::*; pub use super::encode::*;

View File

@ -1,33 +1,11 @@
use anyhow::anyhow;
use flat::en::{Encode, Encoder};
use crate::builtins::DefaultFunction; use crate::builtins::DefaultFunction;
const TERM_TAG_WIDTH: u32 = 4;
const CONST_TAG_WIDTH: u32 = 4;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Program { pub struct Program {
pub version: (usize, usize, usize), pub version: (usize, usize, usize),
pub term: Term, pub term: Term,
} }
impl Program {
pub fn flat(&self) -> anyhow::Result<Vec<u8>> {
let bytes = flat::encode(self.clone()).map_err(|err| anyhow!("{}", err))?;
Ok(bytes)
}
pub fn flat_hex(&self) -> anyhow::Result<String> {
let bytes = self.flat()?;
let hex = hex::encode(&bytes);
Ok(hex)
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Term { pub enum Term {
// tag: 0 // tag: 0
@ -54,22 +32,6 @@ pub enum Term {
Builtin(DefaultFunction), Builtin(DefaultFunction),
} }
pub fn encode_term_tag(tag: u8, e: &mut Encoder) -> Result<(), String> {
safe_encode_bits(TERM_TAG_WIDTH, tag, e)
}
pub fn safe_encode_bits(num_bits: u32, byte: u8, e: &mut Encoder) -> Result<(), String> {
if 2_u8.pow(num_bits) < byte {
Err(format!(
"Overflow detected, cannot fit {} in {} bits.",
byte, num_bits
))
} else {
e.bits(num_bits as i64, byte);
Ok(())
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Constant { pub enum Constant {
// TODO: figure out the right size for this // TODO: figure out the right size for this
@ -86,132 +48,3 @@ pub enum Constant {
// tag: 5 // tag: 5
Bool(bool), Bool(bool),
} }
pub fn encode_constant(tag: u8, e: &mut Encoder) -> Result<(), String> {
e.encode_list_with(encode_constant_tag, [tag].to_vec())
}
pub fn encode_constant_tag(tag: u8, e: &mut Encoder) -> Result<(), String> {
safe_encode_bits(CONST_TAG_WIDTH, tag, e)
}
impl Encode for Program {
fn encode(&self, e: &mut Encoder) -> Result<(), String> {
let (major, minor, patch) = self.version;
major.encode(e)?;
minor.encode(e)?;
patch.encode(e)?;
self.term.encode(e)?;
Ok(())
}
}
impl Encode for Term {
fn encode(&self, e: &mut Encoder) -> Result<(), String> {
// still need annotation but here we have the term tags
match self {
Term::Var(name) => {
encode_term_tag(0, e)?;
name.encode(e)?;
}
Term::Delay(term) => {
encode_term_tag(1, e)?;
term.encode(e)?;
}
Term::Lambda {
parameter_name,
body,
} => {
encode_term_tag(2, e)?;
// need to create encoding for Binder
todo!();
}
Term::Apply { function, argument } => {
encode_term_tag(3, e)?;
function.encode(e)?;
argument.encode(e)?;
}
Term::Constant(constant) => {
encode_term_tag(4, e)?;
constant.encode(e)?;
}
Term::Force(term) => {
encode_term_tag(5, e)?;
term.encode(e)?;
}
Term::Error => {
encode_term_tag(6, e)?;
todo!()
}
Term::Builtin(b) => {
encode_term_tag(7, e)?;
// implement encode for builtins
todo!()
}
}
Ok(())
}
}
impl Encode for &Constant {
fn encode(&self, e: &mut Encoder) -> Result<(), String> {
match self {
Constant::Integer(i) => {
encode_constant(0, e)?;
i.encode(e)?;
}
Constant::ByteString(bytes) => {
encode_constant(1, e)?;
bytes.encode(e)?;
}
Constant::String(s) => {
encode_constant(2, e)?;
s.as_bytes().encode(e)?;
}
// there is no char constant tag
Constant::Char(c) => {
c.encode(e)?;
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) => {
encode_constant(4, e)?;
b.encode(e)?;
}
}
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]
)
}
}

View File

@ -1,79 +1,80 @@
use strum_macros::EnumString; use strum_macros::EnumString;
#[repr(u8)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Debug, Clone, EnumString)] #[derive(Debug, Clone, EnumString)]
#[strum(serialize_all = "camelCase")] #[strum(serialize_all = "camelCase")]
pub enum DefaultFunction { pub enum DefaultFunction {
// Integer functions // Integer functions
AddInteger, AddInteger = 0,
SubtractInteger, SubtractInteger = 1,
MultiplyInteger, MultiplyInteger = 2,
DivideInteger, DivideInteger = 3,
QuotientInteger, QuotientInteger = 4,
RemainderInteger, RemainderInteger = 5,
ModInteger, ModInteger = 6,
EqualsInteger, EqualsInteger = 7,
LessThanInteger, LessThanInteger = 8,
LessThanEqualsInteger, LessThanEqualsInteger = 9,
// ByteString functions // ByteString functions
AppendByteString, AppendByteString = 10,
ConsByteString, ConsByteString = 11,
SliceByteString, SliceByteString = 12,
LengthOfByteString, LengthOfByteString = 13,
IndexByteString, IndexByteString = 14,
EqualsByteString, EqualsByteString = 15,
LessThanByteString, LessThanByteString = 16,
LessThanEqualsByteString, LessThanEqualsByteString = 17,
// Cryptography and hash functions // Cryptography and hash functions
#[strum(serialize = "sha2_256")] #[strum(serialize = "sha2_256")]
Sha2_256, Sha2_256 = 18,
Sha3_256, Sha3_256 = 19,
Blake2b_256, Blake2b_256 = 20,
VerifySignature, VerifySignature = 21,
VerifyEcdsaSecp256k1Signature, VerifyEcdsaSecp256k1Signature = 22,
VerifySchnorrSecp256k1Signature, VerifySchnorrSecp256k1Signature = 23,
// String functions // String functions
AppendString, AppendString = 24,
EqualsString, EqualsString = 25,
EncodeUtf8, EncodeUtf8 = 26,
DecodeUtf8, DecodeUtf8 = 27,
// Bool function // Bool function
IfThenElse, IfThenElse = 28,
// Unit function // Unit function
ChooseUnit, ChooseUnit = 29,
// Tracing function // Tracing function
Trace, Trace = 30,
// Pairs functions // Pairs functions
FstPair, FstPair = 31,
SndPair, SndPair = 32,
// List functions // List functions
ChooseList, ChooseList = 33,
MkCons, MkCons = 34,
HeadList, HeadList = 35,
TailList, TailList = 36,
NullList, NullList = 37,
// Data functions // Data functions
// It is convenient to have a "choosing" function for a data type that has more than two // It is convenient to have a "choosing" function for a data type that has more than two
// constructors to get pattern matching over it and we may end up having multiple such data // constructors to get pattern matching over it and we may end up having multiple such data
// types, hence we include the name of the data type as a suffix. // types, hence we include the name of the data type as a suffix.
ChooseData, ChooseData = 38,
ConstrData, ConstrData = 39,
MapData, MapData = 40,
ListData, ListData = 41,
IData, IData = 42,
BData, BData = 43,
UnConstrData, UnConstrData = 44,
UnMapData, UnMapData = 45,
UnListData, UnListData = 46,
UnIData, UnIData = 47,
UnBData, UnBData = 48,
EqualsData, EqualsData = 49,
SerialiseData, SerialiseData = 50,
// Misc constructors // Misc constructors
// Constructors that we need for constructing e.g. Data. Polymorphic builtin // Constructors that we need for constructing e.g. Data. Polymorphic builtin
// constructors are often problematic (See note [Representable built-in // constructors are often problematic (See note [Representable built-in
// functions over polymorphic built-in types]) // functions over polymorphic built-in types])
MkPairData, MkPairData = 51,
MkNilData, MkNilData = 52,
MkNilPairData, MkNilPairData = 53,
} }

181
crates/uplc/src/flat.rs Normal file
View File

@ -0,0 +1,181 @@
use anyhow::anyhow;
use flat::en::{Encode, Encoder};
use crate::{
ast::{Constant, Program, Term},
builtins::DefaultFunction,
};
const BUILTIN_TAG_WIDTH: u32 = 7;
const CONST_TAG_WIDTH: u32 = 4;
const TERM_TAG_WIDTH: u32 = 4;
impl Program {
pub fn flat(&self) -> anyhow::Result<Vec<u8>> {
let bytes = flat::encode(self.clone()).map_err(|err| anyhow!("{}", err))?;
Ok(bytes)
}
pub fn flat_hex(&self) -> anyhow::Result<String> {
let bytes = self.flat()?;
let hex = hex::encode(&bytes);
Ok(hex)
}
}
impl Encode for Program {
fn encode(&self, e: &mut Encoder) -> Result<(), String> {
let (major, minor, patch) = self.version;
major.encode(e)?;
minor.encode(e)?;
patch.encode(e)?;
self.term.encode(e)?;
Ok(())
}
}
impl Encode for Term {
fn encode(&self, e: &mut Encoder) -> Result<(), String> {
// still need annotation but here we have the term tags
match self {
Term::Var(name) => {
encode_term_tag(0, e)?;
name.encode(e)?;
}
Term::Delay(term) => {
encode_term_tag(1, e)?;
term.encode(e)?;
}
Term::Lambda {
parameter_name: _,
body: _,
} => {
encode_term_tag(2, e)?;
// need to create encoding for Binder
todo!();
}
Term::Apply { function, argument } => {
encode_term_tag(3, e)?;
function.encode(e)?;
argument.encode(e)?;
}
Term::Constant(constant) => {
encode_term_tag(4, e)?;
constant.encode(e)?;
}
Term::Force(term) => {
encode_term_tag(5, e)?;
term.encode(e)?;
}
Term::Error => {
encode_term_tag(6, e)?;
todo!()
}
Term::Builtin(builtin) => {
encode_term_tag(7, e)?;
builtin.encode(e)?;
}
}
Ok(())
}
}
impl Encode for &Constant {
fn encode(&self, e: &mut Encoder) -> Result<(), String> {
match self {
Constant::Integer(i) => {
encode_constant(0, e)?;
i.encode(e)?;
}
Constant::ByteString(bytes) => {
encode_constant(1, e)?;
bytes.encode(e)?;
}
Constant::String(s) => {
encode_constant(2, e)?;
s.as_bytes().encode(e)?;
}
// there is no char constant tag
Constant::Char(c) => {
c.encode(e)?;
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) => {
encode_constant(4, e)?;
b.encode(e)?;
}
}
Ok(())
}
}
impl Encode for DefaultFunction {
fn encode(&self, e: &mut flat::en::Encoder) -> Result<(), String> {
e.bits(BUILTIN_TAG_WIDTH as i64, self.clone() as u8);
Ok(())
}
}
fn encode_term_tag(tag: u8, e: &mut Encoder) -> Result<(), String> {
safe_encode_bits(TERM_TAG_WIDTH, tag, e)
}
fn safe_encode_bits(num_bits: u32, byte: u8, e: &mut Encoder) -> Result<(), String> {
if 2_u8.pow(num_bits) < byte {
Err(format!(
"Overflow detected, cannot fit {} in {} bits.",
byte, num_bits
))
} else {
e.bits(num_bits as i64, byte);
Ok(())
}
}
pub fn encode_constant(tag: u8, e: &mut Encoder) -> Result<(), String> {
e.encode_list_with(encode_constant_tag, [tag].to_vec())
}
pub fn encode_constant_tag(tag: u8, e: &mut Encoder) -> Result<(), String> {
safe_encode_bits(CONST_TAG_WIDTH, tag, e)
}
#[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]
)
}
}

View File

@ -1,5 +1,6 @@
pub mod ast; pub mod ast;
pub mod builtins; pub mod builtins;
mod flat;
pub mod parser; pub mod parser;
#[macro_use] #[macro_use]