From b984dd15411eef5fdfbbe1bf9a3585d12294887b Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 27 Jan 2022 15:58:32 -0500 Subject: [PATCH 01/77] move Instruction(s) and Elem to own module (execution no longer depends on parsing) --- src/executor.rs | 2 +- src/lib.rs | 9 +++++---- src/parse.rs | 20 +++----------------- src/types.rs | 19 +++++++++++++++++++ 4 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 src/types.rs diff --git a/src/executor.rs b/src/executor.rs index 6072c4b..63e4701 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,4 +1,4 @@ -use crate::parse::{Elem, Instruction, Instructions}; +use crate::types::{Elem, Instruction, Instructions}; use thiserror::Error; diff --git a/src/lib.rs b/src/lib.rs index 34cc211..eebe093 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,13 @@ +mod types; +pub use types::{Elem, Instructions}; + mod parse; -pub use parse::{parse, Instructions}; +pub use parse::{parse}; mod executor; pub use executor::Executor; use generic_array::{typenum::U32, GenericArray}; -use hex::encode as hex_encode; -use hex_literal::hex; -use sha2::{Digest, Sha256, Sha512}; +use sha2::{Digest, Sha256}; use sha3::{Digest as Sha3_Digest, Sha3_256}; /** diff --git a/src/parse.rs b/src/parse.rs index 9307773..e66dab1 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -8,21 +8,14 @@ /// /// Where CHARS is any number of characters which aren't escaped double-quotes (\") and HEX is a 64 /// digit hexadecimal number. + +use crate::types::{Elem, Instruction, Instructions}; + use std::str::FromStr; use generic_array::{typenum::U32, GenericArray}; use thiserror::Error; -pub type Instructions = Vec; - -#[derive(Debug)] -pub enum Instruction { - Push(Elem), - FnHashSha256, - FnCheckEqual, - FnAssertTrue, -} - pub fn parse(input: &str) -> Result { input .split(';') @@ -44,13 +37,6 @@ fn parse_instruction(term: &str) -> Result { } } -#[derive(Debug, PartialEq)] -pub enum Elem { - Bool(bool), - Bytes32(GenericArray), - BytesN(Vec), -} - impl Elem { pub fn simple_type(&self) -> &'static str { match self { diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..f971d84 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,19 @@ +use generic_array::{typenum::U32, GenericArray}; + +#[derive(Debug, PartialEq)] +pub enum Elem { + Bool(bool), + Bytes32(GenericArray), + BytesN(Vec), +} + +#[derive(Debug)] +pub enum Instruction { + Push(Elem), + // FnRestack(GenericArray), + FnHashSha256, + FnCheckEqual, + FnAssertTrue, +} + +pub type Instructions = Vec; From 8a22e13e416c9a2bb074309eb2eed278007f1d32 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 1 Feb 2022 15:26:57 -0500 Subject: [PATCH 02/77] implement restacking primitive (backend) --- src/parse.rs | 2 +- src/types.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/parse.rs b/src/parse.rs index e66dab1..03056b4 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -13,7 +13,7 @@ use crate::types::{Elem, Instruction, Instructions}; use std::str::FromStr; -use generic_array::{typenum::U32, GenericArray}; +use generic_array::{GenericArray}; use thiserror::Error; pub fn parse(input: &str) -> Result { diff --git a/src/types.rs b/src/types.rs index f971d84..d93d1d9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,7 @@ use generic_array::{typenum::U32, GenericArray}; +use thiserror::Error; -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Elem { Bool(bool), Bytes32(GenericArray), @@ -16,4 +17,77 @@ pub enum Instruction { FnAssertTrue, } + +pub type StackIx = usize; +pub type Stack = Vec; + +// Stack manipulation: +// - All stack manipulations: +// + dig +// + dug +// + dip +// + dup +// + swap +// + drop +// - they all boil down to: +// 1. drop inputs +// 2. replicate inputs +// 3. reorder inputs +// - which conveniently boils down to: +// + xs : [ old_stack_index ] +// + map (\x -> xs !! x) xs +// - successful iff all old_stack_index's < length stack +// - pretty-printing? +// + REQUIRED: constant compile-time choice of manipulations +// + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] +// + global: keep track of stack indices (always possible?) and print where it's from??? + +#[derive(Debug, PartialEq)] +pub struct Restack { + restack_depth: StackIx, + restack_vec: Vec +} + +impl Restack { + pub fn run(&self, stack: Stack) -> Result { + if self.restack_depth <= stack.len() { + self.restack_vec.iter().map(|&restack_index| + match stack.get(restack_index) { + None => Err(RestackError::StackIndexInvalid{ restack_index: restack_index, restack_depth: self.restack_depth, }), + Some(stack_element) => Ok( stack_element.clone() ), + } + ).collect() + } else { + Err(RestackError::InvalidDepth{ stack_len: stack.len(), restack_depth: self.restack_depth, }) + } + } + + // self.valid_depth() -> + // self.restack_depth <= xs.len() -> + // self.run(xs).is_ok() == true + pub fn valid_depth(&self) -> bool { + !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) + } + +} + + + +#[derive(Debug, Error)] +pub enum RestackError { + #[error("invalid Restack: restack_index = {restack_index:?} out of bounds for restack_depth = {restack_depth:?}")] + StackIndexInvalid { + restack_index: usize, + restack_depth: usize, + }, + #[error("attempt to restack {restack_depth:?} elements of a stack with only {stack_len:?} elements")] + InvalidDepth { + stack_len: usize, + restack_depth: usize, + }, +} + + pub type Instructions = Vec; + + From 7d96a344b5ee1da8457f689310a901e016d1a879 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 1 Feb 2022 16:33:54 -0500 Subject: [PATCH 03/77] add restack append, executor, and instruction --- src/executor.rs | 14 +++++++++++++- src/types.rs | 22 +++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 63e4701..1ec9f20 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,4 +1,4 @@ -use crate::types::{Elem, Instruction, Instructions}; +use crate::types::{Elem, Instruction, Instructions, Restack, RestackError}; use thiserror::Error; @@ -12,6 +12,7 @@ impl Executor { for expr in expressions { match expr { Instruction::Push(elem) => self.push(elem), + Instruction::FnRestack(restack) => self.restack(restack)?, Instruction::FnAssertTrue => self.assert_true()?, Instruction::FnCheckEqual => self.check_equal()?, Instruction::FnHashSha256 => self.sha256()?, @@ -51,6 +52,15 @@ impl Executor { fn pop(&mut self) -> Result { self.stack.pop().ok_or_else(|| ExecError::EmptyStack) } + + fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { + match restack.run(&self.stack) { + Err(e) => Err(ExecError::RestackExecError(e)), + Ok(new_stack) => { + self.stack = new_stack; + Ok(()) }, + } + } } #[derive(Debug, Error)] @@ -61,4 +71,6 @@ pub enum ExecError { EmptyStack, #[error("attempted to hash an elem of an unsupported type ({0})")] HashUnsupportedType(&'static str), + #[error("restack failed ({0})")] + RestackExecError(RestackError), } diff --git a/src/types.rs b/src/types.rs index d93d1d9..a630b7d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,6 @@ use generic_array::{typenum::U32, GenericArray}; use thiserror::Error; +use std::cmp; #[derive(Clone, Debug, PartialEq)] pub enum Elem { @@ -11,7 +12,7 @@ pub enum Elem { #[derive(Debug)] pub enum Instruction { Push(Elem), - // FnRestack(GenericArray), + FnRestack(Restack), FnHashSha256, FnCheckEqual, FnAssertTrue, @@ -41,7 +42,6 @@ pub type Stack = Vec; // + REQUIRED: constant compile-time choice of manipulations // + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] // + global: keep track of stack indices (always possible?) and print where it's from??? - #[derive(Debug, PartialEq)] pub struct Restack { restack_depth: StackIx, @@ -49,14 +49,14 @@ pub struct Restack { } impl Restack { - pub fn run(&self, stack: Stack) -> Result { + pub fn run(&self, stack: &Stack) -> Result { if self.restack_depth <= stack.len() { self.restack_vec.iter().map(|&restack_index| match stack.get(restack_index) { None => Err(RestackError::StackIndexInvalid{ restack_index: restack_index, restack_depth: self.restack_depth, }), Some(stack_element) => Ok( stack_element.clone() ), } - ).collect() + ).collect::>() } else { Err(RestackError::InvalidDepth{ stack_len: stack.len(), restack_depth: self.restack_depth, }) } @@ -69,8 +69,20 @@ impl Restack { !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) } -} + // x.append(y).run(s) == x.run(y.run(s)) + pub fn append(&self, other: Restack) -> Restack { + Restack { + restack_depth: cmp::max(self.restack_depth, other.restack_depth), + restack_vec: self.restack_vec.iter().map(|&restack_index| + match other.restack_vec.get(restack_index) { + None => restack_index, + Some(stack_index) => stack_index.clone(), + } + ).collect() + } + } +} #[derive(Debug, Error)] From 7d3b847b393ae3f22712cfb862513e7136f5ece4 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 1 Feb 2022 17:38:17 -0500 Subject: [PATCH 04/77] add serde json (de)serializer --- Cargo.toml | 9 ++++++--- src/parse.rs | 10 ++++++++++ src/types.rs | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c046c94..a88166c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sha2 = "0.9" -sha3 = "0.9" -hex-literal = "0.3" +base64 = "0.13.0" generic-array = "0.14" hex = "0.4" +hex-literal = "0.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha2 = "0.9" +sha3 = "0.9" thiserror = "1.0" diff --git a/src/parse.rs b/src/parse.rs index 03056b4..bfe8825 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -16,6 +16,14 @@ use std::str::FromStr; use generic_array::{GenericArray}; use thiserror::Error; + +pub fn parse_json(input: &str) -> Result { + match serde_json::from_str(&input) { + Err(serde_error) => Err(ParseError::SerdeJsonError(serde_error)), + Ok(instructions) => Ok(instructions), + } +} + pub fn parse(input: &str) -> Result { input .split(';') @@ -109,4 +117,6 @@ pub enum ParseError { UnsupportedElem(String), #[error("instruction is malformed or cannot be parsed in this context")] UnsupportedInstruction(String), + #[error("error from serde_json ({0})")] + SerdeJsonError(serde_json::Error), } diff --git a/src/types.rs b/src/types.rs index a630b7d..ed91c4d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,15 +1,49 @@ use generic_array::{typenum::U32, GenericArray}; use thiserror::Error; + use std::cmp; -#[derive(Clone, Debug, PartialEq)] + +// extern crate base64; +use base64; +use serde::{de, Deserialize, Serialize}; +// use serde_json::Result; + +use serde::de::{Deserializer}; +use serde::ser::{Serializer}; + +fn serialize_generic_array_u8_u32(v: &T, serializer: S) -> Result +where + T: AsRef<[u8]>, + S: Serializer, +{ + serializer.serialize_str(&base64::encode(v.as_ref())) +} + +pub fn deserialize_generic_array_u8_u32<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + use serde::de::Error; + + String::deserialize(deserializer) + .and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|vec| { + GenericArray::from_exact_iter(vec).ok_or( + de::Error::custom("String::deserialize failed to produce an array of length 32")) + }) +} + + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Elem { Bool(bool), + #[serde(serialize_with = "serialize_generic_array_u8_u32", deserialize_with = "deserialize_generic_array_u8_u32")] Bytes32(GenericArray), BytesN(Vec), } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub enum Instruction { Push(Elem), FnRestack(Restack), @@ -42,7 +76,7 @@ pub type Stack = Vec; // + REQUIRED: constant compile-time choice of manipulations // + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] // + global: keep track of stack indices (always possible?) and print where it's from??? -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Restack { restack_depth: StackIx, restack_vec: Vec From 6c284b92abb1c1d4d04a4093937a5a0321453f87 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 7 Feb 2022 17:17:00 -0500 Subject: [PATCH 05/77] add first restack test (failing) --- src/lib.rs | 141 ++++++++++++++++++++++++++------------------------- src/types.rs | 84 ++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 72 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eebe093..7886f31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ mod types; pub use types::{Elem, Instructions}; mod parse; -pub use parse::{parse}; +pub use parse::parse; mod executor; pub use executor::Executor; @@ -10,51 +10,54 @@ use generic_array::{typenum::U32, GenericArray}; use sha2::{Digest, Sha256}; use sha3::{Digest as Sha3_Digest, Sha3_256}; -/** - * Types: - * - UnsignedInteger - * - Integer - * - Float64 - * - Bytes(N) - * - Multibase - * - Multihash - * - Multiaddr - * - KeyTypes={Ed25519,Secp256k1,Secp256r1,Bls12_381} - * - PublicKey(KeyType) - * - PrivateKey(KeyType) - * - JWT - * - JWS - * - JWE - * - LDP - * - JSON - * - CBOR - * - * Functions - * - Sign :: Bytes(N) -> PrivateKey(KeyType) => Bytes(SignatureSize[KeyType]) - * - VerifySignature :: Bytes(N) -> Bytes(SignatureSize[KeyType]) -> PublicKey(KeyType) => Boolean - * - VerifyRecoveredSignature :: Bytes(N) -> Bytes(SignatureSize[KeyType]) => Boolean - * - HashSha3_256 :: Bytes(N) => Bytes(32) - * - Equal - * - AssertTrue - * - * Example - * push b"I am the walrus."; - * hash_sha256; - * push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; - * check_equal; - * assert_true; - * - * Example - * setup { - * push b"I am the walrus."; - * } - * challenge { - * hash_sha256; - * push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; - * check_equal; - * assert_true; - * } - */ +// #[cfg(test)] +// use hex_literal::hex; + +// /** +// * Types: +// * - UnsignedInteger +// * - Integer +// * - Float64 +// * - Bytes(N) +// * - Multibase +// * - Multihash +// * - Multiaddr +// * - KeyTypes={Ed25519,Secp256k1,Secp256r1,Bls12_381} +// * - PublicKey(KeyType) +// * - PrivateKey(KeyType) +// * - JWT +// * - JWS +// * - JWE +// * - LDP +// * - JSON +// * - CBOR +// * +// * Functions +// * - Sign :: Bytes(N) -> PrivateKey(KeyType) => Bytes(SignatureSize[KeyType]) +// * - VerifySignature :: Bytes(N) -> Bytes(SignatureSize[KeyType]) -> PublicKey(KeyType) => Boolean +// * - VerifyRecoveredSignature :: Bytes(N) -> Bytes(SignatureSize[KeyType]) => Boolean +// * - HashSha3_256 :: Bytes(N) => Bytes(32) +// * - Equal +// * - AssertTrue +// * +// * Example +// * push b"I am the walrus."; +// * hash_sha256; +// * push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; +// * check_equal; +// * assert_true; +// * +// * Example +// * setup { +// * push b"I am the walrus."; +// * } +// * challenge { +// * hash_sha256; +// * push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; +// * check_equal; +// * assert_true; +// * } +// */ fn sha256(input: &Vec) -> GenericArray { // create a Sha256 object @@ -68,27 +71,28 @@ fn sha256(input: &Vec) -> GenericArray { return result; } -fn sha3_256(input: &Vec) -> GenericArray { - // create a Sha256 object - let mut hasher = Sha3_256::new(); +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; - // write input message - hasher.update(input); + fn sha3_256(input: &Vec) -> GenericArray { + // create a Sha256 object + let mut hasher = Sha3_256::new(); - // read hash digest and consume hasher - let result = hasher.finalize(); - return result; -} + // write input message + hasher.update(input); -fn drop_bytes(n: usize, input: &Vec) -> Vec { - let mut result = input.clone(); - result.drain(..n); - return result; -} + // read hash digest and consume hasher + let result = hasher.finalize(); + return result; + } -#[cfg(test)] -mod tests { - use super::*; + fn drop_bytes(n: usize, input: &Vec) -> Vec { + let mut result = input.clone(); + result.drain(..n); + return result; + } #[test] fn test_sha2() { @@ -117,9 +121,10 @@ mod tests { ); } - #[test] - fn test_drop_bytes() { - let result = drop_bytes(6, &b"hello world".to_vec()); - assert_eq!(&result[..], b"world"); - } + // UNUSED + // #[test] + // fn test_drop_bytes() { + // let result = drop_bytes(6, &b"hello world".to_vec()); + // assert_eq!(&result[..], b"world"); + // } } diff --git a/src/types.rs b/src/types.rs index ed91c4d..e483242 100644 --- a/src/types.rs +++ b/src/types.rs @@ -12,6 +12,19 @@ use serde::{de, Deserialize, Serialize}; use serde::de::{Deserializer}; use serde::ser::{Serializer}; + +// TODO: +// - restack: +// + add common stack manipulation constructors +// + test against common stack manipulations, e.g. swap; swap = id + +// - json +// + add to stack type +// + add construction/destruction primitives +// + add property based tests + + + fn serialize_generic_array_u8_u32(v: &T, serializer: S) -> Result where T: AsRef<[u8]>, @@ -79,10 +92,25 @@ pub type Stack = Vec; #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Restack { restack_depth: StackIx, - restack_vec: Vec + restack_vec: Vec, } impl Restack { + pub fn id() -> Self { + Restack { + restack_depth: 0, + restack_vec: vec![], + } + } + + pub fn swap() -> Self { + Restack { + restack_depth: 2, + restack_vec: vec![1usize, 0], + } + } + + pub fn run(&self, stack: &Stack) -> Result { if self.restack_depth <= stack.len() { self.restack_vec.iter().map(|&restack_index| @@ -90,7 +118,8 @@ impl Restack { None => Err(RestackError::StackIndexInvalid{ restack_index: restack_index, restack_depth: self.restack_depth, }), Some(stack_element) => Ok( stack_element.clone() ), } - ).collect::>() + ).collect::>()?.extend(stack.drain(self.restack_depth..)) + } else { Err(RestackError::InvalidDepth{ stack_len: stack.len(), restack_depth: self.restack_depth, }) } @@ -104,7 +133,7 @@ impl Restack { } // x.append(y).run(s) == x.run(y.run(s)) - pub fn append(&self, other: Restack) -> Restack { + pub fn append(&self, other: Self) -> Self { Restack { restack_depth: cmp::max(self.restack_depth, other.restack_depth), restack_vec: self.restack_vec.iter().map(|&restack_index| @@ -119,7 +148,7 @@ impl Restack { } -#[derive(Debug, Error)] +#[derive(Debug, PartialEq, Error)] pub enum RestackError { #[error("invalid Restack: restack_index = {restack_index:?} out of bounds for restack_depth = {restack_depth:?}")] StackIndexInvalid { @@ -137,3 +166,50 @@ pub enum RestackError { pub type Instructions = Vec; +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_id_restack() { + let example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; + assert_eq!(Ok(example_stack.clone()), Restack::id().run(&example_stack)) + + } + + // #[test] + // fn test_sha2() { + // let result = sha256(&b"hello world".to_vec()); + // assert_eq!( + // result[..], + // hex!( + // " + // b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 + // " + // )[..] + // ); + // } + + // #[test] + // fn test_sha3() { + // let result = sha3_256(&b"hello world".to_vec()); + // // println!("{:x?}", hex_encode(result.as_slice())); + // assert_eq!( + // result[..], + // hex!( + // " + // 644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938 + // " + // )[..] + // ); + // } + + // #[test] + // fn test_drop_bytes() { + // let result = drop_bytes(6, &b"hello world".to_vec()); + // assert_eq!(&result[..], b"world"); + // } + +} + + From d1465eb5a205acfdcbe53f6106b37bd3ad8872c3 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 7 Feb 2022 17:53:16 -0500 Subject: [PATCH 06/77] add granular stack manipulations, add tests, fix tests (passing) --- src/executor.rs | 2 +- src/lib.rs | 11 +++-- src/types.rs | 104 ++++++++++++++++++++++++++++++------------------ 3 files changed, 72 insertions(+), 45 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 1ec9f20..3828db7 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -54,7 +54,7 @@ impl Executor { } fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { - match restack.run(&self.stack) { + match restack.run(&mut self.stack) { Err(e) => Err(ExecError::RestackExecError(e)), Ok(new_stack) => { self.stack = new_stack; diff --git a/src/lib.rs b/src/lib.rs index 7886f31..e340f35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,10 +121,9 @@ mod tests { ); } - // UNUSED - // #[test] - // fn test_drop_bytes() { - // let result = drop_bytes(6, &b"hello world".to_vec()); - // assert_eq!(&result[..], b"world"); - // } + #[test] + fn test_drop_bytes() { + let result = drop_bytes(6, &b"hello world".to_vec()); + assert_eq!(&result[..], b"world"); + } } diff --git a/src/types.rs b/src/types.rs index e483242..3108a9d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -95,6 +95,7 @@ pub struct Restack { restack_vec: Vec, } + impl Restack { pub fn id() -> Self { Restack { @@ -110,15 +111,43 @@ impl Restack { } } + pub fn drop_n(n: usize) -> Self { + Restack { + restack_depth: n, + restack_vec: vec![] + } + } + + pub fn drop() -> Self { + Self::drop_n(1) + } + + // duplicates the (ix)th value onto the top of the stack (0-indexed) + pub fn dup_n(ix: usize) -> Self { + Restack { + restack_depth: ix+1, + restack_vec: (ix..ix).chain(0..ix).collect(), + } + } + + pub fn dup() -> Self { + Self::dup_n(0) + } - pub fn run(&self, stack: &Stack) -> Result { + pub fn run(&self, stack: &mut Stack) -> Result { if self.restack_depth <= stack.len() { - self.restack_vec.iter().map(|&restack_index| + let result = self.restack_vec.iter().map(|&restack_index| match stack.get(restack_index) { None => Err(RestackError::StackIndexInvalid{ restack_index: restack_index, restack_depth: self.restack_depth, }), Some(stack_element) => Ok( stack_element.clone() ), } - ).collect::>()?.extend(stack.drain(self.restack_depth..)) + ).collect::>(); + match result { + Ok(mut result_ok) => { + result_ok.extend(stack.drain(self.restack_depth..)); + Ok(result_ok) }, + Err(e) => Err(e) + } } else { Err(RestackError::InvalidDepth{ stack_len: stack.len(), restack_depth: self.restack_depth, }) @@ -171,44 +200,43 @@ mod tests { use super::*; #[test] - fn test_id_restack() { - let example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; - assert_eq!(Ok(example_stack.clone()), Restack::id().run(&example_stack)) + fn test_restack_id() { + let mut example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; + assert_eq!(Ok(example_stack.clone()), Restack::id().run(&mut example_stack)) + } + + #[test] + fn test_restack_drop() { + let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; + let example_stack_out = vec![Elem::Bool(true)]; + assert_eq!(Ok(example_stack_out), Restack::drop().run(&mut example_stack_in)) + } + #[test] + fn test_restack_drop_n() { + let example_stack_in = vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)]; + let example_stack_out_0 = vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)]; + let example_stack_out_1 = vec![Elem::Bool(true), Elem::Bool(false)]; + let example_stack_out_2 = vec![Elem::Bool(false)]; + let example_stack_out_3 = vec![]; + assert_eq!(Ok(example_stack_out_0), Restack::drop_n(0).run(&mut example_stack_in.clone())); + assert_eq!(Ok(example_stack_out_1), Restack::drop_n(1).run(&mut example_stack_in.clone())); + assert_eq!(Ok(example_stack_out_2), Restack::drop_n(2).run(&mut example_stack_in.clone())); + assert_eq!(Ok(example_stack_out_3), Restack::drop_n(3).run(&mut example_stack_in.clone())) } - // #[test] - // fn test_sha2() { - // let result = sha256(&b"hello world".to_vec()); - // assert_eq!( - // result[..], - // hex!( - // " - // b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 - // " - // )[..] - // ); - // } - - // #[test] - // fn test_sha3() { - // let result = sha3_256(&b"hello world".to_vec()); - // // println!("{:x?}", hex_encode(result.as_slice())); - // assert_eq!( - // result[..], - // hex!( - // " - // 644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938 - // " - // )[..] - // ); - // } - - // #[test] - // fn test_drop_bytes() { - // let result = drop_bytes(6, &b"hello world".to_vec()); - // assert_eq!(&result[..], b"world"); - // } + #[test] + fn test_restack_swap() { + let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; + let example_stack_out = vec![Elem::Bool(true), Elem::Bool(false)]; + assert_eq!(Ok(example_stack_out), Restack::swap().run(&mut example_stack_in)) + } + + #[test] + fn test_restack_swap_twice_append() { + let mut example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; + assert_eq!(Ok(example_stack.clone()), Restack::swap().append(Restack::swap()).run(&mut example_stack)) + } } From e64ac66d307dd0ca5bebe0bbc1bc9606bd665af0 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 8 Feb 2022 15:33:25 -0500 Subject: [PATCH 07/77] fix unused import, document stack primitives, add tests for each (passing) --- src/lib.rs | 6 ++-- src/types.rs | 87 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e340f35..17d2c9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,7 @@ pub use executor::Executor; use generic_array::{typenum::U32, GenericArray}; use sha2::{Digest, Sha256}; -use sha3::{Digest as Sha3_Digest, Sha3_256}; - -// #[cfg(test)] -// use hex_literal::hex; +// use sha3::{Digest as Sha3_Digest, Sha3_256}; // /** // * Types: @@ -75,6 +72,7 @@ fn sha256(input: &Vec) -> GenericArray { mod tests { use super::*; use hex_literal::hex; + use sha3::{Digest as Sha3_Digest, Sha3_256}; fn sha3_256(input: &Vec) -> GenericArray { // create a Sha256 object diff --git a/src/types.rs b/src/types.rs index 3108a9d..4ee883d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -15,7 +15,7 @@ use serde::ser::{Serializer}; // TODO: // - restack: -// + add common stack manipulation constructors +// + DONE: add common stack manipulation constructors // + test against common stack manipulations, e.g. swap; swap = id // - json @@ -95,8 +95,8 @@ pub struct Restack { restack_vec: Vec, } - impl Restack { + // identity pub fn id() -> Self { Restack { restack_depth: 0, @@ -104,6 +104,7 @@ impl Restack { } } + // swap first two stack elements pub fn swap() -> Self { Restack { restack_depth: 2, @@ -111,6 +112,7 @@ impl Restack { } } + // drop the first (n) stack elements pub fn drop_n(n: usize) -> Self { Restack { restack_depth: n, @@ -118,6 +120,7 @@ impl Restack { } } + // drop the first stack element pub fn drop() -> Self { Self::drop_n(1) } @@ -130,10 +133,30 @@ impl Restack { } } + // duplicates the 0th value onto the top of the stack (0-indexed) pub fn dup() -> Self { Self::dup_n(0) } + // pull the (ix)th element to the top of the stack + // dig 4 = { 5, [3, 0, 1, 2] } + pub fn dig(ix: usize) -> Self { + Restack { + restack_depth: ix+1, + restack_vec: (0..ix+1).cycle().skip(ix).take(ix+1).collect(), + } + } + + // push the top of the stack to the (ix)th position + // dug 4 = { 5, [1, 2, 3, 0] } + pub fn dug(ix: usize) -> Self { + Restack { + restack_depth: ix+1, + restack_vec: (1..ix+1).chain(std::iter::once(0)).collect() + } + } + + // restack a Stack pub fn run(&self, stack: &mut Stack) -> Result { if self.restack_depth <= stack.len() { let result = self.restack_vec.iter().map(|&restack_index| @@ -161,6 +184,7 @@ impl Restack { !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) } + // NOTE: unchecked (run valid_depth on arguments for safe version) // x.append(y).run(s) == x.run(y.run(s)) pub fn append(&self, other: Self) -> Self { Restack { @@ -173,7 +197,6 @@ impl Restack { ).collect() } } - } @@ -202,42 +225,68 @@ mod tests { #[test] fn test_restack_id() { let mut example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; - assert_eq!(Ok(example_stack.clone()), Restack::id().run(&mut example_stack)) + let restack = Restack::id(); + assert!(restack.valid_depth(), "Restack::id() has invalid depth"); + assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) } #[test] - fn test_restack_drop() { - let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; - let example_stack_out = vec![Elem::Bool(true)]; - assert_eq!(Ok(example_stack_out), Restack::drop().run(&mut example_stack_in)) + fn test_restack_dig() { + assert!(Restack::dig(4).valid_depth(), "Restack::dig(4) has invalid depth"); + assert_eq!(Restack { restack_depth: 5, restack_vec: vec![4, 0, 1, 2, 3] }, Restack::dig(4)); + let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(true)]; + let example_stack_out = vec![Elem::Bool(true), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false)]; + assert_eq!(Ok(example_stack_out.clone()), Restack::dig(4).run(&mut example_stack_in)) + } + + #[test] + fn test_restack_dug() { + assert!(Restack::dug(4).valid_depth(), "Restack::dug(4) has invalid depth"); + assert_eq!(Restack { restack_depth: 5, restack_vec: vec![1, 2, 3, 4, 0] }, Restack::dug(4)); + let mut example_stack_in = vec![Elem::Bool(true), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false)]; + let example_stack_out = vec![Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(true)]; + assert_eq!(Ok(example_stack_out.clone()), Restack::dug(4).run(&mut example_stack_in)) } #[test] fn test_restack_drop_n() { let example_stack_in = vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)]; - let example_stack_out_0 = vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)]; - let example_stack_out_1 = vec![Elem::Bool(true), Elem::Bool(false)]; - let example_stack_out_2 = vec![Elem::Bool(false)]; - let example_stack_out_3 = vec![]; - assert_eq!(Ok(example_stack_out_0), Restack::drop_n(0).run(&mut example_stack_in.clone())); - assert_eq!(Ok(example_stack_out_1), Restack::drop_n(1).run(&mut example_stack_in.clone())); - assert_eq!(Ok(example_stack_out_2), Restack::drop_n(2).run(&mut example_stack_in.clone())); - assert_eq!(Ok(example_stack_out_3), Restack::drop_n(3).run(&mut example_stack_in.clone())) + for example_stack_out in + [vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)], + vec![Elem::Bool(true), Elem::Bool(false)], + vec![Elem::Bool(false)], + vec![]] { + let restack = Restack::drop_n(3 - example_stack_out.len()); + assert!(restack.valid_depth(), "Restack::drop_n(_) has invalid depth"); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in.clone())); + } + } + + #[test] + fn test_restack_drop() { + let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; + let example_stack_out = vec![Elem::Bool(true)]; + let restack = Restack::drop(); + assert!(restack.valid_depth(), "Restack::drop() has invalid depth"); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) } #[test] fn test_restack_swap() { let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; let example_stack_out = vec![Elem::Bool(true), Elem::Bool(false)]; - assert_eq!(Ok(example_stack_out), Restack::swap().run(&mut example_stack_in)) + let restack = Restack::swap(); + assert!(restack.valid_depth(), "Restack::swap() has invalid depth"); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) } #[test] fn test_restack_swap_twice_append() { let mut example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; - assert_eq!(Ok(example_stack.clone()), Restack::swap().append(Restack::swap()).run(&mut example_stack)) + let restack = Restack::swap().append(Restack::swap()); + assert!(restack.valid_depth(), "Restack::swap().append(Restack::swap()) has invalid depth"); + assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) } } - From 9c27595018adbcfbc8069195dcc8b54b021431a8 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 8 Feb 2022 15:59:20 -0500 Subject: [PATCH 08/77] remove Bytes32: combine with Bytes(N) --- Cargo.toml | 1 - src/executor.rs | 4 ++-- src/lib.rs | 7 +++---- src/parse.rs | 16 +++------------- src/types.rs | 41 ++--------------------------------------- 5 files changed, 10 insertions(+), 59 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a88166c..962f85e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -base64 = "0.13.0" generic-array = "0.14" hex = "0.4" hex-literal = "0.3" diff --git a/src/executor.rs b/src/executor.rs index 3828db7..9f0ef44 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -37,8 +37,8 @@ impl Executor { fn sha256(&mut self) -> Result<(), ExecError> { match self.pop()? { - Elem::BytesN(bytes) => { - self.push(Elem::Bytes32(super::sha256(&bytes))); + Elem::Bytes(bytes) => { + self.push(Elem::Bytes(super::sha256(&bytes))); Ok(()) } elem => Err(ExecError::HashUnsupportedType(elem.simple_type())), diff --git a/src/lib.rs b/src/lib.rs index 17d2c9d..09600ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,9 +6,7 @@ pub use parse::parse; mod executor; pub use executor::Executor; -use generic_array::{typenum::U32, GenericArray}; use sha2::{Digest, Sha256}; -// use sha3::{Digest as Sha3_Digest, Sha3_256}; // /** // * Types: @@ -56,7 +54,7 @@ use sha2::{Digest, Sha256}; // * } // */ -fn sha256(input: &Vec) -> GenericArray { +fn sha256(input: &Vec) -> Vec { // create a Sha256 object let mut hasher = Sha256::new(); @@ -65,12 +63,13 @@ fn sha256(input: &Vec) -> GenericArray { // read hash digest and consume hasher let result = hasher.finalize(); - return result; + return result.to_vec(); } #[cfg(test)] mod tests { use super::*; + use generic_array::{typenum::U32, GenericArray}; use hex_literal::hex; use sha3::{Digest as Sha3_Digest, Sha3_256}; diff --git a/src/parse.rs b/src/parse.rs index bfe8825..da3127f 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -13,7 +13,6 @@ use crate::types::{Elem, Instruction, Instructions}; use std::str::FromStr; -use generic_array::{GenericArray}; use thiserror::Error; @@ -49,8 +48,7 @@ impl Elem { pub fn simple_type(&self) -> &'static str { match self { Self::Bool(_) => "bool", - Self::Bytes32(_) => "Bytes(32)", - Self::BytesN(_) => "Bytes(N)", + Self::Bytes(_) => "Bytes", } } } @@ -60,7 +58,7 @@ impl FromStr for Elem { fn from_str(s: &str) -> Result { match s.as_bytes() { [b'b', b'"', inner @ .., b'"'] => { - return Ok(Elem::BytesN(inner.to_vec())); + return Ok(Elem::Bytes(inner.to_vec())); } [b'0', b'x', hex_digits @ ..] => { if hex_digits.len() != 64 { @@ -91,15 +89,7 @@ impl FromStr for Elem { }, )?; - if let Some(array) = GenericArray::from_exact_iter(bytes) { - return Ok(Elem::Bytes32(array)); - } else { - use std::hint::unreachable_unchecked; - // if the 'bytes' vec has been constructed without error, then it is 32 bytes - // long, as the hex_digits slice is checked to be 64 digits long, and each pair - // of digits is used to make one byte. - unsafe { unreachable_unchecked() } - } + return Ok(Elem::Bytes(bytes)) } // No need to support booleans, but it is trivial to do so. _ => Err(ParseError::UnsupportedElem(s.to_string())), diff --git a/src/types.rs b/src/types.rs index 4ee883d..9865443 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,21 +1,11 @@ -use generic_array::{typenum::U32, GenericArray}; use thiserror::Error; use std::cmp; - -// extern crate base64; -use base64; -use serde::{de, Deserialize, Serialize}; -// use serde_json::Result; - -use serde::de::{Deserializer}; -use serde::ser::{Serializer}; - +use serde::{Deserialize, Serialize}; // TODO: // - restack: -// + DONE: add common stack manipulation constructors // + test against common stack manipulations, e.g. swap; swap = id // - json @@ -23,37 +13,10 @@ use serde::ser::{Serializer}; // + add construction/destruction primitives // + add property based tests - - -fn serialize_generic_array_u8_u32(v: &T, serializer: S) -> Result -where - T: AsRef<[u8]>, - S: Serializer, -{ - serializer.serialize_str(&base64::encode(v.as_ref())) -} - -pub fn deserialize_generic_array_u8_u32<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - use serde::de::Error; - - String::deserialize(deserializer) - .and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string()))) - .and_then(|vec| { - GenericArray::from_exact_iter(vec).ok_or( - de::Error::custom("String::deserialize failed to produce an array of length 32")) - }) -} - - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Elem { Bool(bool), - #[serde(serialize_with = "serialize_generic_array_u8_u32", deserialize_with = "deserialize_generic_array_u8_u32")] - Bytes32(GenericArray), - BytesN(Vec), + Bytes(Vec), } #[derive(Debug, Serialize, Deserialize)] From 3b93180d4dc3d371d51dd7a49e5f68862e3a3da1 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 8 Feb 2022 17:04:56 -0500 Subject: [PATCH 09/77] add le/lt primitives, use arbitrary_precision numbers for manually derived PartialOrd --- Cargo.toml | 2 +- src/executor.rs | 19 ++++++++++++ src/parse.rs | 7 ++++- src/types.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 962f85e..463b459 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ generic-array = "0.14" hex = "0.4" hex-literal = "0.3" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" +serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha2 = "0.9" sha3 = "0.9" thiserror = "1.0" diff --git a/src/executor.rs b/src/executor.rs index 9f0ef44..45b2b4a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -2,6 +2,8 @@ use crate::types::{Elem, Instruction, Instructions, Restack, RestackError}; use thiserror::Error; +// TODO: implement n-step executor && errors that tell you what step they're on +// to allow minimal step-by-step debugging #[derive(Debug, Default)] pub struct Executor { stack: Vec, @@ -14,6 +16,8 @@ impl Executor { Instruction::Push(elem) => self.push(elem), Instruction::FnRestack(restack) => self.restack(restack)?, Instruction::FnAssertTrue => self.assert_true()?, + Instruction::FnCheckLe => self.check_le()?, + Instruction::FnCheckLt => self.check_lt()?, Instruction::FnCheckEqual => self.check_equal()?, Instruction::FnHashSha256 => self.sha256()?, } @@ -28,6 +32,20 @@ impl Executor { } } + fn check_le(&mut self) -> Result<(), ExecError> { + let one = self.pop()?; + let other = self.pop()?; + self.push(Elem::Bool(one <= other)); + Ok(()) + } + + fn check_lt(&mut self) -> Result<(), ExecError> { + let one = self.pop()?; + let other = self.pop()?; + self.push(Elem::Bool(one < other)); + Ok(()) + } + fn check_equal(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; @@ -49,6 +67,7 @@ impl Executor { self.stack.push(elem) } + // TODO: since pop can fail, require passing debug info to it fn pop(&mut self) -> Result { self.stack.pop().ok_or_else(|| ExecError::EmptyStack) } diff --git a/src/parse.rs b/src/parse.rs index da3127f..2ccc080 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -47,8 +47,13 @@ fn parse_instruction(term: &str) -> Result { impl Elem { pub fn simple_type(&self) -> &'static str { match self { - Self::Bool(_) => "bool", + Self::Unit => "Unit", + Self::Bool(_) => "Bool", Self::Bytes(_) => "Bytes", + Self::Number(_) => "Number", + Self::String(_) => "String", + Self::Array(_) => "Array", + Self::Object(_) => "Object", } } } diff --git a/src/types.rs b/src/types.rs index 9865443..9e85e16 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,27 +3,100 @@ use thiserror::Error; use std::cmp; use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; // TODO: // - restack: -// + test against common stack manipulations, e.g. swap; swap = id +// + property based tests // - json -// + add to stack type // + add construction/destruction primitives // + add property based tests -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { + Unit, Bool(bool), Bytes(Vec), + Number(Number), + String(String), + Array(Vec), + Object(Map), +} + +impl PartialOrd for Elem { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::Unit, Self::Unit) => Some(cmp::Ordering::Equal), + (Self::Bool(x), Self::Bool(y)) => x.partial_cmp(y), + (Self::Bytes(x), Self::Bytes(y)) => x.partial_cmp(y), + (Self::Number(x), Self::Number(y)) => format!("{}", x).partial_cmp(&format!("{}", y)), + (Self::String(x), Self::String(y)) => x.partial_cmp(y), + (Self::Array(x), Self::Array(y)) => x.partial_cmp(y), + (Self::Object(x), Self::Object(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None } + (_, _) => None, + } + } } + +// TODO: + +// some: +// - concat (support cons) +// + bytes +// + string +// + array +// + object +// - slice +// + bytes +// + string +// + array +// + object +// - index +// + array : nat -> elem +// + object : string -> elem +// + bytes -> bit -->> PUNT +// + string -> byte/char?? -->> PUNT + +// Bool(bool), +// - neg +// - and +// - or + +// Bytes(Vec), + +// Number(Number), -->> later +// - to_int +// - add +// - sub +// - mul +// - div + +// String(String), + +// Array(Vec), +// - index + +// Object(Map), + + +// DONE: + +// all: +// - equals +// - compare + +// Unit, + #[derive(Debug, Serialize, Deserialize)] pub enum Instruction { Push(Elem), FnRestack(Restack), FnHashSha256, + FnCheckLe, + FnCheckLt, FnCheckEqual, FnAssertTrue, } From 970f65d560721a7d45cf9d93ebed5c405ec8d400 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Tue, 8 Feb 2022 17:50:42 -0500 Subject: [PATCH 10/77] prototype (concat, slice, index, lookup) and implement concat --- src/executor.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- src/types.rs | 21 ++++++++-------- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 45b2b4a..98720ac 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -19,6 +19,10 @@ impl Executor { Instruction::FnCheckLe => self.check_le()?, Instruction::FnCheckLt => self.check_lt()?, Instruction::FnCheckEqual => self.check_equal()?, + Instruction::FnConcat => self.concat()?, + Instruction::FnSlice => self.slice()?, + Instruction::FnIndex => self.index()?, + Instruction::FnLookup => self.lookup()?, Instruction::FnHashSha256 => self.sha256()?, } } @@ -53,6 +57,62 @@ impl Executor { Ok(()) } + fn concat(&mut self) -> Result<(), ExecError> { + let one = self.pop()?; + let other = self.pop()?; + match (one, other) { + (Elem::Bytes(x), Elem::Bytes(y)) => { + let mut result = x.clone(); + result.append(&mut y.clone()); + self.push(Elem::Bytes(result)); + Ok(()) }, + + (Elem::String(x), Elem::String(y)) => { + let mut result = x.clone(); + result.push_str(&mut y.clone()); + self.push(Elem::String(result)); + Ok(()) }, + + (Elem::Array(x), Elem::Array(y)) => { + let mut result = x.clone(); + result.append(&mut y.clone()); + self.push(Elem::Array(result)); + Ok(()) }, + + (Elem::Object(x), Elem::Object(y)) => { + let mut result = x.clone(); + result.append(&mut y.clone()); + self.push(Elem::Object(result)); + Ok(()) }, + + (some_x, some_y) => { + let lhs = &some_x.simple_type(); + let rhs = &some_y.simple_type(); + Err(ExecError::ConcatUnsupportedTypes { lhs: lhs, rhs: rhs }) }, + } + } + + fn slice(&mut self) -> Result<(), ExecError> { + let one = self.pop()?; + // let other = self.pop()?; + // self.push(Elem::Bool(one == other)); + Ok(()) + } + + fn index(&mut self) -> Result<(), ExecError> { + let one = self.pop()?; + // let other = self.pop()?; + // self.push(Elem::Bool(one == other)); + Ok(()) + } + + fn lookup(&mut self) -> Result<(), ExecError> { + let one = self.pop()?; + // let other = self.pop()?; + // self.push(Elem::Bool(one == other)); + Ok(()) + } + fn sha256(&mut self) -> Result<(), ExecError> { match self.pop()? { Elem::Bytes(bytes) => { @@ -90,6 +150,11 @@ pub enum ExecError { EmptyStack, #[error("attempted to hash an elem of an unsupported type ({0})")] HashUnsupportedType(&'static str), - #[error("restack failed ({0})")] + #[error("restack failed: {0}")] RestackExecError(RestackError), + #[error("concat applied to unsupported types: lhs: {lhs:?}; rhs: {rhs:?}")] + ConcatUnsupportedTypes { + lhs: &'static str, + rhs: &'static str, + }, } diff --git a/src/types.rs b/src/types.rs index 9e85e16..8418635 100644 --- a/src/types.rs +++ b/src/types.rs @@ -44,7 +44,7 @@ impl PartialOrd for Elem { // TODO: // some: -// - concat (support cons) +// - concat (support cons?) // + bytes // + string // + array @@ -65,8 +65,6 @@ impl PartialOrd for Elem { // - and // - or -// Bytes(Vec), - // Number(Number), -->> later // - to_int // - add @@ -74,14 +72,6 @@ impl PartialOrd for Elem { // - mul // - div -// String(String), - -// Array(Vec), -// - index - -// Object(Map), - - // DONE: // all: @@ -89,6 +79,11 @@ impl PartialOrd for Elem { // - compare // Unit, +// Array(Vec), +// Bytes(Vec), +// String(String), +// Object(Map), + #[derive(Debug, Serialize, Deserialize)] pub enum Instruction { @@ -98,6 +93,10 @@ pub enum Instruction { FnCheckLe, FnCheckLt, FnCheckEqual, + FnConcat, + FnSlice, + FnIndex, // Array + FnLookup, // Map FnAssertTrue, } From 6a7c49dc57449f25de0286af4c4c95eeeb9b5075 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 9 Feb 2022 12:10:27 -0500 Subject: [PATCH 11/77] implement concat for bytes, string, array, object; prototype slice and implement for bytes; prototype lookup --- src/executor.rs | 190 ++++++++++++++++++++++++++++++++++++++++-------- src/types.rs | 6 +- 2 files changed, 162 insertions(+), 34 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 98720ac..ba04ada 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,7 @@ use crate::types::{Elem, Instruction, Instructions, Restack, RestackError}; +use std::convert::TryFrom; +use serde_json::{Map, Number, Value}; use thiserror::Error; // TODO: implement n-step executor && errors that tell you what step they're on @@ -57,48 +59,120 @@ impl Executor { Ok(()) } + fn concat_bytes(&mut self, x: Vec, y: Vec) -> Result<(), ExecError> { + let mut result = x.clone(); + result.append(&mut y.clone()); + self.push(Elem::Bytes(result)); + Ok(()) + } + + fn concat_string(&mut self, x: String, y: String) -> Result<(), ExecError> { + let mut result = x.clone(); + result.push_str(&mut y.clone()); + self.push(Elem::String(result)); + Ok(()) + } + + fn concat_array(&mut self, x: Vec, y: Vec) -> Result<(), ExecError> { + let mut result = x.clone(); + result.append(&mut y.clone()); + self.push(Elem::Array(result)); + Ok(()) + } + + fn concat_object(&mut self, x: Map, y: Map) -> Result<(), ExecError> { + let mut result = x.clone(); + result.append(&mut y.clone()); + self.push(Elem::Object(result)); + Ok(()) + } + fn concat(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; match (one, other) { - (Elem::Bytes(x), Elem::Bytes(y)) => { - let mut result = x.clone(); - result.append(&mut y.clone()); - self.push(Elem::Bytes(result)); - Ok(()) }, + (Elem::Bytes(x), Elem::Bytes(y)) => self.concat_bytes(x, y), + (Elem::String(x), Elem::String(y)) => self.concat_string(x, y), + (Elem::Array(x), Elem::Array(y)) => self.concat_array(x, y), + (Elem::Object(x), Elem::Object(y)) => self.concat_object(x, y), + (some_x, some_y) => { + Err(ExecError::ConcatUnsupportedTypes { + lhs: &some_x.simple_type(), + rhs: &some_y.simple_type() + }) + }, + } + } - (Elem::String(x), Elem::String(y)) => { - let mut result = x.clone(); - result.push_str(&mut y.clone()); - self.push(Elem::String(result)); - Ok(()) }, - (Elem::Array(x), Elem::Array(y)) => { - let mut result = x.clone(); - result.append(&mut y.clone()); - self.push(Elem::Array(result)); - Ok(()) }, + fn slice_bytes(&mut self, offset: Number, length: Number, iterable: Vec) -> Result<(), ExecError> { + let u_offset = offset.as_u64() + .ok_or_else(|| ExecError::SliceOffsetNotU64(offset.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ExecError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + let u_length = length.as_u64() + .ok_or_else(|| ExecError::SliceLengthNotU64(length.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ExecError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + let u_offset_plus_length = u_offset.checked_add(u_length) + .ok_or_else(|| ExecError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; + if iterable.len() < u_offset_plus_length { + Err(ExecError::SliceTooShort { + offset: u_offset, + length: u_length, + iterable: &Elem::Bytes(vec![]).simple_type(), + }) + } else { + self.push(Elem::Bytes(iterable[u_offset..=u_offset_plus_length].to_vec())); + Ok(()) + } + } - (Elem::Object(x), Elem::Object(y)) => { - let mut result = x.clone(); - result.append(&mut y.clone()); - self.push(Elem::Object(result)); - Ok(()) }, + fn slice_string(&mut self, offset: Number, length: Number, iterable: String) -> Result<(), ExecError> { + let result = iterable.clone(); + // result.push_str(&mut y.clone()); + // self.push(Elem::String(result)); + Ok(()) + } - (some_x, some_y) => { - let lhs = &some_x.simple_type(); - let rhs = &some_y.simple_type(); - Err(ExecError::ConcatUnsupportedTypes { lhs: lhs, rhs: rhs }) }, - } + fn slice_array(&mut self, offset: Number, length: Number, iterable: Vec) -> Result<(), ExecError> { + let result = iterable.clone(); + // result.append(&mut y.clone()); + // self.push(Elem::Array(result)); + Ok(()) } - fn slice(&mut self) -> Result<(), ExecError> { - let one = self.pop()?; - // let other = self.pop()?; - // self.push(Elem::Bool(one == other)); + fn slice_object(&mut self, offset: Number, length: Number, iterable: Map) -> Result<(), ExecError> { + let result = iterable.clone(); + // result.append(&mut y.clone()); + // self.push(Elem::Object(result)); Ok(()) } + // slice : offset -> length -> iterable -> iterable + fn slice(&mut self) -> Result<(), ExecError> { + let maybe_offset = self.pop()?; + let maybe_length = self.pop()?; + let maybe_iterable = self.pop()?; + match (maybe_offset, maybe_length, maybe_iterable) { + (Elem::Number(offset), Elem::Number(length), Elem::Bytes(iterator)) => + self.slice_bytes(offset, length, iterator), + (Elem::Number(offset), Elem::Number(length), Elem::String(iterator)) => + self.slice_string(offset, length, iterator), + (Elem::Number(offset), Elem::Number(length), Elem::Array(iterator)) => + self.slice_array(offset, length, iterator), + (Elem::Number(offset), Elem::Number(length), Elem::Object(iterator)) => + self.slice_object(offset, length, iterator), + (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { + Err(ExecError::SliceUnsupportedTypes { + maybe_not_offset: &maybe_not_offset.simple_type(), + maybe_not_length: &maybe_not_length.simple_type(), + maybe_not_iterable: &maybe_not_iterable.simple_type(), + }) + } + } + } + + + // you can index any iterable fn index(&mut self) -> Result<(), ExecError> { let one = self.pop()?; // let other = self.pop()?; @@ -106,9 +180,35 @@ impl Executor { Ok(()) } + + + // TODO: + // - lookup_null : key -> map -> Result<(), ExecError> + // - lookup_bool : key -> map -> Result + // - lookup_number : .. -> Result + // - lookup_string : .. -> Result + // - lookup_array : .. -> Result, ..> + // - lookup_object : .. -> Result, ..> + + // you can lookup a key in a Map (or fail, no recovery) fn lookup(&mut self) -> Result<(), ExecError> { - let one = self.pop()?; - // let other = self.pop()?; + let maybe_key = self.pop()?; + let maybe_map = self.pop()?; + // match (maybe_key, maybe_map) { + // (Elem::String(key), Elem::Object(map)) => { + // match map.get(key) { + // Some(value) => { + // self.push() + + // Ok(()) } + // }, + + // (maybe_not_key, maybe_not_map) => Err(ExecError::LookupUnsupportedTypes { + // maybe_not_key: &maybe_not_key.simple_type(), + // maybe_not_map: &maybe_not_map.simple_type(), + // }) + // } + // self.push(Elem::Bool(one == other)); Ok(()) } @@ -157,4 +257,32 @@ pub enum ExecError { lhs: &'static str, rhs: &'static str, }, + + #[error("slice applied to unsupported types: maybe_not_offset: {maybe_not_offset:?}; maybe_not_length: {maybe_not_length:?}; maybe_not_iterable: {maybe_not_iterable:?}")] + SliceUnsupportedTypes { + maybe_not_offset: &'static str, + maybe_not_length: &'static str, + maybe_not_iterable: &'static str, + }, + #[error("slice applied to an 'offset' that can't be unpacked to u64: offset: {0:?}")] + SliceOffsetNotU64(Number), + #[error("slice applied to a 'length' that can't be unpacked to u64: length: {0:?}")] + SliceLengthNotU64(Number), + #[error("slice applied to an iterable that's too short for the given offset: offset: {offset:?} and length: {length:?}: iterable: {iterable:?}")] + SliceTooShort { + offset: usize, + length: usize, + iterable: &'static str, + }, + #[error("slice applied to offset and length whose sum overflows usize: offset: {offset:?} and length: {length:?}")] + SliceOverflow { + offset: Number, + length: Number, + }, + + #[error("lookup applied to unsupported types: maybe_not_key: {maybe_not_key:?}; maybe_not_map: {maybe_not_map:?}")] + LookupUnsupportedTypes { + maybe_not_key: &'static str, + maybe_not_map: &'static str, + }, } diff --git a/src/types.rs b/src/types.rs index 8418635..57b820a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -18,8 +18,8 @@ use serde_json::{Map, Number, Value}; pub enum Elem { Unit, Bool(bool), - Bytes(Vec), Number(Number), + Bytes(Vec), String(String), Array(Vec), Object(Map), @@ -178,7 +178,7 @@ impl Restack { pub fn dig(ix: usize) -> Self { Restack { restack_depth: ix+1, - restack_vec: (0..ix+1).cycle().skip(ix).take(ix+1).collect(), + restack_vec: (0..=ix).cycle().skip(ix).take(ix+1).collect(), } } @@ -187,7 +187,7 @@ impl Restack { pub fn dug(ix: usize) -> Self { Restack { restack_depth: ix+1, - restack_vec: (1..ix+1).chain(std::iter::once(0)).collect() + restack_vec: (1..=ix).chain(std::iter::once(0)).collect() } } From ac770e902494b36790463cb2118a01adea5a0452 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 11 Feb 2022 09:41:01 -0500 Subject: [PATCH 12/77] add to/from json for object/string/array, add instruction symbols, fix dup --- src/executor.rs | 261 +++++++-------------------- src/lib.rs | 2 +- src/main.rs | 109 +++++++++-- src/parse.rs | 2 + src/types.rs | 471 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 599 insertions(+), 246 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index ba04ada..ed1af29 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,7 +1,5 @@ -use crate::types::{Elem, Instruction, Instructions, Restack, RestackError}; -use std::convert::TryFrom; +use crate::types::{Elem, ElemError, Instruction, Instructions, Restack, RestackError}; -use serde_json::{Map, Number, Value}; use thiserror::Error; // TODO: implement n-step executor && errors that tell you what step they're on @@ -26,263 +24,144 @@ impl Executor { Instruction::FnIndex => self.index()?, Instruction::FnLookup => self.lookup()?, Instruction::FnHashSha256 => self.sha256()?, + Instruction::FnToJson => self.to_json()?, + Instruction::FnFromJson => self.from_json()?, + Instruction::FnObjectFromJson => self.object_from_json()?, + Instruction::FnArrayFromJson => self.array_from_json()?, + Instruction::FnStringFromJson => self.string_from_json()?, } } Ok(()) } - fn assert_true(&mut self) -> Result<(), ExecError> { - match self.pop()? { - Elem::Bool(true) => Ok(()), - found => Err(ExecError::AssertTrueFailed(found)), + pub fn push(&mut self, elem: Elem) { + self.stack.push(elem) + } + + fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { + match restack.run(&mut self.stack) { + Err(e) => Err(ExecError::RestackExecError(e)), + Ok(new_stack) => { + self.stack = new_stack; + Ok(()) }, } } + fn assert_true(&mut self) -> Result<(), ExecError> { + let x = self.pop()?; + x.assert_true()?; + Ok(()) + } + fn check_le(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; - self.push(Elem::Bool(one <= other)); + self.push(one.check_le(other)?); Ok(()) } fn check_lt(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; - self.push(Elem::Bool(one < other)); + self.push(one.check_lt(other)?); Ok(()) } fn check_equal(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; - self.push(Elem::Bool(one == other)); - Ok(()) - } - - fn concat_bytes(&mut self, x: Vec, y: Vec) -> Result<(), ExecError> { - let mut result = x.clone(); - result.append(&mut y.clone()); - self.push(Elem::Bytes(result)); - Ok(()) - } - - fn concat_string(&mut self, x: String, y: String) -> Result<(), ExecError> { - let mut result = x.clone(); - result.push_str(&mut y.clone()); - self.push(Elem::String(result)); - Ok(()) - } - - fn concat_array(&mut self, x: Vec, y: Vec) -> Result<(), ExecError> { - let mut result = x.clone(); - result.append(&mut y.clone()); - self.push(Elem::Array(result)); - Ok(()) - } - - fn concat_object(&mut self, x: Map, y: Map) -> Result<(), ExecError> { - let mut result = x.clone(); - result.append(&mut y.clone()); - self.push(Elem::Object(result)); + self.push(one.check_lt(other)?); Ok(()) } fn concat(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; - match (one, other) { - (Elem::Bytes(x), Elem::Bytes(y)) => self.concat_bytes(x, y), - (Elem::String(x), Elem::String(y)) => self.concat_string(x, y), - (Elem::Array(x), Elem::Array(y)) => self.concat_array(x, y), - (Elem::Object(x), Elem::Object(y)) => self.concat_object(x, y), - (some_x, some_y) => { - Err(ExecError::ConcatUnsupportedTypes { - lhs: &some_x.simple_type(), - rhs: &some_y.simple_type() - }) - }, - } - } - - - fn slice_bytes(&mut self, offset: Number, length: Number, iterable: Vec) -> Result<(), ExecError> { - let u_offset = offset.as_u64() - .ok_or_else(|| ExecError::SliceOffsetNotU64(offset.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ExecError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - let u_length = length.as_u64() - .ok_or_else(|| ExecError::SliceLengthNotU64(length.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ExecError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - let u_offset_plus_length = u_offset.checked_add(u_length) - .ok_or_else(|| ExecError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; - if iterable.len() < u_offset_plus_length { - Err(ExecError::SliceTooShort { - offset: u_offset, - length: u_length, - iterable: &Elem::Bytes(vec![]).simple_type(), - }) - } else { - self.push(Elem::Bytes(iterable[u_offset..=u_offset_plus_length].to_vec())); - Ok(()) - } - } - - fn slice_string(&mut self, offset: Number, length: Number, iterable: String) -> Result<(), ExecError> { - let result = iterable.clone(); - // result.push_str(&mut y.clone()); - // self.push(Elem::String(result)); - Ok(()) - } - - fn slice_array(&mut self, offset: Number, length: Number, iterable: Vec) -> Result<(), ExecError> { - let result = iterable.clone(); - // result.append(&mut y.clone()); - // self.push(Elem::Array(result)); - Ok(()) - } - - fn slice_object(&mut self, offset: Number, length: Number, iterable: Map) -> Result<(), ExecError> { - let result = iterable.clone(); - // result.append(&mut y.clone()); - // self.push(Elem::Object(result)); + self.push(one.concat(other)?); Ok(()) } - // slice : offset -> length -> iterable -> iterable fn slice(&mut self) -> Result<(), ExecError> { let maybe_offset = self.pop()?; let maybe_length = self.pop()?; let maybe_iterable = self.pop()?; - match (maybe_offset, maybe_length, maybe_iterable) { - (Elem::Number(offset), Elem::Number(length), Elem::Bytes(iterator)) => - self.slice_bytes(offset, length, iterator), - (Elem::Number(offset), Elem::Number(length), Elem::String(iterator)) => - self.slice_string(offset, length, iterator), - (Elem::Number(offset), Elem::Number(length), Elem::Array(iterator)) => - self.slice_array(offset, length, iterator), - (Elem::Number(offset), Elem::Number(length), Elem::Object(iterator)) => - self.slice_object(offset, length, iterator), - (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { - Err(ExecError::SliceUnsupportedTypes { - maybe_not_offset: &maybe_not_offset.simple_type(), - maybe_not_length: &maybe_not_length.simple_type(), - maybe_not_iterable: &maybe_not_iterable.simple_type(), - }) - } - } + self.push(Elem::slice(maybe_offset, maybe_length, maybe_iterable)?); + Ok(()) } - // you can index any iterable fn index(&mut self) -> Result<(), ExecError> { - let one = self.pop()?; - // let other = self.pop()?; - // self.push(Elem::Bool(one == other)); + let maybe_index = self.pop()?; + let maybe_iterable = self.pop()?; + self.push(Elem::index(maybe_index, maybe_iterable)?); Ok(()) } - - - // TODO: - // - lookup_null : key -> map -> Result<(), ExecError> - // - lookup_bool : key -> map -> Result - // - lookup_number : .. -> Result - // - lookup_string : .. -> Result - // - lookup_array : .. -> Result, ..> - // - lookup_object : .. -> Result, ..> - // you can lookup a key in a Map (or fail, no recovery) fn lookup(&mut self) -> Result<(), ExecError> { let maybe_key = self.pop()?; let maybe_map = self.pop()?; - // match (maybe_key, maybe_map) { - // (Elem::String(key), Elem::Object(map)) => { - // match map.get(key) { - // Some(value) => { - // self.push() + self.push(Elem::lookup(maybe_key, maybe_map)?); + Ok(()) + } - // Ok(()) } - // }, + fn sha256(&mut self) -> Result<(), ExecError> { + let hash_input = self.pop()?; + self.push(Elem::sha256(hash_input)?); + Ok(()) + } - // (maybe_not_key, maybe_not_map) => Err(ExecError::LookupUnsupportedTypes { - // maybe_not_key: &maybe_not_key.simple_type(), - // maybe_not_map: &maybe_not_map.simple_type(), - // }) - // } + fn to_json(&mut self) -> Result<(), ExecError> { + let to_json_input = self.pop()?; + self.push(Elem::to_json(to_json_input)?); + Ok(()) + } - // self.push(Elem::Bool(one == other)); + fn from_json(&mut self) -> Result<(), ExecError> { + let from_json_input = self.pop()?; + self.push(Elem::from_json(from_json_input)?); Ok(()) } - fn sha256(&mut self) -> Result<(), ExecError> { - match self.pop()? { - Elem::Bytes(bytes) => { - self.push(Elem::Bytes(super::sha256(&bytes))); - Ok(()) - } - elem => Err(ExecError::HashUnsupportedType(elem.simple_type())), - } + fn object_from_json(&mut self) -> Result<(), ExecError> { + let object_from_json_input = self.pop()?; + self.push(Elem::object_from_json(object_from_json_input)?); + Ok(()) } - fn push(&mut self, elem: Elem) { - self.stack.push(elem) + fn array_from_json(&mut self) -> Result<(), ExecError> { + let array_from_json_input = self.pop()?; + self.push(Elem::array_from_json(array_from_json_input)?); + Ok(()) + } + + fn string_from_json(&mut self) -> Result<(), ExecError> { + let string_from_json_input = self.pop()?; + self.push(Elem::string_from_json(string_from_json_input)?); + Ok(()) } // TODO: since pop can fail, require passing debug info to it + // (so we know what we were expecting) fn pop(&mut self) -> Result { self.stack.pop().ok_or_else(|| ExecError::EmptyStack) } - fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { - match restack.run(&mut self.stack) { - Err(e) => Err(ExecError::RestackExecError(e)), - Ok(new_stack) => { - self.stack = new_stack; - Ok(()) }, - } - } } #[derive(Debug, Error)] pub enum ExecError { - #[error("expected Elem::Bool(true), found {0:?}")] - AssertTrueFailed(Elem), + #[error("ElemError({0:?})")] + ElemError(ElemError), #[error("tried to pop from an empty stack")] EmptyStack, - #[error("attempted to hash an elem of an unsupported type ({0})")] - HashUnsupportedType(&'static str), #[error("restack failed: {0}")] RestackExecError(RestackError), - #[error("concat applied to unsupported types: lhs: {lhs:?}; rhs: {rhs:?}")] - ConcatUnsupportedTypes { - lhs: &'static str, - rhs: &'static str, - }, - - #[error("slice applied to unsupported types: maybe_not_offset: {maybe_not_offset:?}; maybe_not_length: {maybe_not_length:?}; maybe_not_iterable: {maybe_not_iterable:?}")] - SliceUnsupportedTypes { - maybe_not_offset: &'static str, - maybe_not_length: &'static str, - maybe_not_iterable: &'static str, - }, - #[error("slice applied to an 'offset' that can't be unpacked to u64: offset: {0:?}")] - SliceOffsetNotU64(Number), - #[error("slice applied to a 'length' that can't be unpacked to u64: length: {0:?}")] - SliceLengthNotU64(Number), - #[error("slice applied to an iterable that's too short for the given offset: offset: {offset:?} and length: {length:?}: iterable: {iterable:?}")] - SliceTooShort { - offset: usize, - length: usize, - iterable: &'static str, - }, - #[error("slice applied to offset and length whose sum overflows usize: offset: {offset:?} and length: {length:?}")] - SliceOverflow { - offset: Number, - length: Number, - }, +} - #[error("lookup applied to unsupported types: maybe_not_key: {maybe_not_key:?}; maybe_not_map: {maybe_not_map:?}")] - LookupUnsupportedTypes { - maybe_not_key: &'static str, - maybe_not_map: &'static str, - }, +impl From for ExecError { + fn from(error: ElemError) -> Self { + ExecError::ElemError(error) + } } + diff --git a/src/lib.rs b/src/lib.rs index 09600ce..5361448 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ mod types; -pub use types::{Elem, Instructions}; +pub use types::{Elem, Instruction, Instructions, Restack}; mod parse; pub use parse::parse; diff --git a/src/main.rs b/src/main.rs index eab9c35..3b3b8f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,100 @@ -use cryptoscript::{parse, Executor}; +use cryptoscript::{Elem, Executor, Instruction, Instructions, Restack}; + +#[cfg(test)] +mod tests { + use super::*; + use cryptoscript::{parse}; + + #[test] + #[should_panic] + fn test_parse_exec() { + let instructions = parse( + r#" + push b"I am the walrus."; + hash_sha256; + push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; + check_equal; + assert_true; + "#, + ) + .expect("failed to parse the input"); + Executor::default() + .consume(instructions) + .expect("error processing instructions"); + } +} fn main() { - let instructions = parse( - r#" - push b"I am the walrus."; - hash_sha256; - push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; - check_equal; - assert_true; - "#, - ) - .expect("failed to parse the input"); - Executor::default() - .consume(instructions) + + let input_json = r#" + { + "queries": [ + { + "uri": "https://api.etherscan.io/api", + "module": "account", + "action": "tokenbalance", + "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", + "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + "tag": "latest", + "blockno": "8000000", + "apikey": "YourApiKeyToken", + "response": + { + "status": "1", + "message": "OK", + "result": "135499" + } + } + ], + "prompts": [ + { + "action": "siwe", + "version": "1.1.0", + "data": { + "message": "service.org wants you to sign in with your Ethereum account:\n0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\n\nI accept the ServiceOrg Terms of Service: https://service.org/tos\n\nURI: https://service.org/login\nVersion: 1\nChain ID: 1\nNonce: 32891757\nIssued At: 2021-09-30T16:25:24.000Z\nResources:\n- ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu\n- https://example.com/my-web2-claim.json", + "fields": { + "domain": "service.org", + "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + "statement": "I accept the ServiceOrg Terms of Service: https://service.org/tos", + "uri": "https://service.org/login", + "version": "1", + "chainId": 1, + "nonce": "32891757", + "issuedAt": "2021-09-30T16:25:24.000Z", + "resources": ["ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu", "https://example.com/my-web2-claim.json"] + } + } + } + ] + } + "#; + + // let json_instructions = parse_json()" + let instructions: Instructions = vec![ + Instruction::FnObjectFromJson, + Instruction::FnRestack(Restack::dup()), + + Instruction::Push(Elem::String("queries".to_string())), + Instruction::FnLookup, + Instruction::FnArrayFromJson, + + Instruction::Push(Elem::Number(From::from(0u8))), + Instruction::FnIndex, + Instruction::FnObjectFromJson, + + Instruction::FnRestack(Restack::dup()), + Instruction::Push(Elem::String("action".to_string())), + Instruction::FnLookup, + Instruction::FnStringFromJson, + Instruction::Push(Elem::String("tokenbalance".to_string())), + Instruction::FnCheckEqual, + Instruction::FnAssertTrue, + + ]; + + let mut exec = Executor::default(); + exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); + exec.consume(instructions) .expect("error processing instructions"); + println!("{}", format!("{:?}", exec)); } diff --git a/src/parse.rs b/src/parse.rs index 2ccc080..1d25da6 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -54,6 +54,7 @@ impl Elem { Self::String(_) => "String", Self::Array(_) => "Array", Self::Object(_) => "Object", + Self::Json(_) => "JSON", } } } @@ -115,3 +116,4 @@ pub enum ParseError { #[error("error from serde_json ({0})")] SerdeJsonError(serde_json::Error), } + diff --git a/src/types.rs b/src/types.rs index 57b820a..d37bd25 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,18 +1,12 @@ use thiserror::Error; use std::cmp; +use std::convert::TryFrom; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; -// TODO: -// - restack: -// + property based tests - -// - json -// + add construction/destruction primitives -// + add property based tests - +// TODO: property based tests #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { @@ -21,8 +15,9 @@ pub enum Elem { Number(Number), Bytes(Vec), String(String), - Array(Vec), + Array(Vec), Object(Map), + Json(Value), } impl PartialOrd for Elem { @@ -33,32 +28,433 @@ impl PartialOrd for Elem { (Self::Bytes(x), Self::Bytes(y)) => x.partial_cmp(y), (Self::Number(x), Self::Number(y)) => format!("{}", x).partial_cmp(&format!("{}", y)), (Self::String(x), Self::String(y)) => x.partial_cmp(y), - (Self::Array(x), Self::Array(y)) => x.partial_cmp(y), + (Self::Array(x), Self::Array(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None }, (Self::Object(x), Self::Object(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None } (_, _) => None, } } } +impl Elem { + pub fn assert_true(&self) -> Result<(), ElemError> { + match self { + Self::Bool(x) => if *x { + Ok(()) + } else { + Err(ElemError::AssertTrueFailed()) + }, + found => Err(ElemError::AssertTrueUnsupportedType(found.clone())), + } + } + + pub fn check_le(&self, other: Self) -> Result { + let result = match self.partial_cmp(&other) + .ok_or_else(|| ElemError::CheckLeIncomparableTypes { + lhs: self.simple_type(), + rhs: other.simple_type() })? { + cmp::Ordering::Less => true, + cmp::Ordering::Equal => true, + cmp::Ordering::Greater => false, + }; + Ok(Self::Bool(result)) + } + + pub fn check_lt(&self, other: Self) -> Result { + let result = match self.partial_cmp(&other) + .ok_or_else(|| ElemError::CheckLtIncomparableTypes { + lhs: self.simple_type(), + rhs: other.simple_type() })? { + cmp::Ordering::Less => true, + _ => false, + }; + Ok(Self::Bool(result)) + } + + // TODO: fixme + pub fn check_eq(self, other: Self) -> Result { + match (self, other) { + (Self::String(x), Self::String(y)) => Ok(Self::Bool(x.chars().collect::>() == y.chars().collect::>())), + (x, y) => Ok(Self::Bool(x == y)), + } + + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckEqIncomparableTypes { + // lhs: self.simple_type(), + // rhs: other.simple_type() })? { + // cmp::Ordering::Equal => true, + // _ => false, + // }; + // Ok(Self::Bool(self == other)) + } + + fn concat_generic::Item>>(x: T, y: T) -> T { + x.into_iter().chain(y.into_iter()).collect() + } + + pub fn concat(self, other: Self) -> Result { + match (self, other) { + (Self::Bytes(x), Self::Bytes(y)) => Ok(Self::Bytes(Self::concat_generic(x, y))), + (Self::String(x), Self::String(y)) => { + Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) + .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) + }, + (Self::Array(x), Self::Array(y)) => Ok(Self::Array(Self::concat_generic(x, y))), + (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), + (some_x, some_y) => { + Err(ElemError::ConcatUnsupportedTypes { + lhs: &some_x.simple_type(), + rhs: &some_y.simple_type() + }) + }, + } + } + + + fn slice_generic::Item>>(offset: Number, length: Number, iterable: T) -> Result { + let u_offset = offset.as_u64() + .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + let u_length = length.as_u64() + .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + let u_offset_plus_length = u_offset.checked_add(u_length) + .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; + if iterable.clone().into_iter().count() < u_offset_plus_length { + panic!("slice_generic SliceTooShort unimplemented") + + // TODO: implement proper error + // Err(ElemError::SliceTooShort { + // offset: u_offset, + // length: u_length, + // iterable: &Self::Bytes(vec![]).simple_type(), + // }) + } else { + Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) + } + } + + pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { + match (maybe_offset, maybe_length, maybe_iterable) { + (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => + Ok(Self::Bytes(Self::slice_generic(offset, length, iterator)?)), + (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { + let iterator_vec = Vec::from(iterator.clone()); + Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec)?) + .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) + }, + (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => + Ok(Self::Array(Self::slice_generic(offset, length, iterator)?)), + (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => + Ok(Self::Object(Self::slice_generic(offset, length, iterator)?)), + (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { + Err(ElemError::SliceUnsupportedTypes { + maybe_not_offset: &maybe_not_offset.simple_type(), + maybe_not_length: &maybe_not_length.simple_type(), + maybe_not_iterable: &maybe_not_iterable.simple_type(), + }) + } + } + } + + fn index_generic::Item>>(index: Number, iterable: T) -> Result<::Item, ElemError> { + let u_index: usize = index.as_u64() + .ok_or_else(|| ElemError::IndexNotU64(index.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; + if iterable.clone().into_iter().count() <= u_index { + return Err(ElemError::IndexTooShort { + index: u_index, + iterable: &Self::Bytes(vec![]).simple_type(), + }) + } else { + match iterable.into_iter().skip(u_index).next() { + None => Err(ElemError::IndexTooShort { index: u_index, iterable: &Self::Bytes(vec![]).simple_type() }), + Some(x) => Ok(x), + } + } + } -// TODO: - -// some: -// - concat (support cons?) -// + bytes -// + string -// + array -// + object -// - slice -// + bytes -// + string -// + array -// + object -// - index -// + array : nat -> elem -// + object : string -> elem -// + bytes -> bit -->> PUNT -// + string -> byte/char?? -->> PUNT + pub fn index(self, maybe_iterable: Self) -> Result { + match (self, maybe_iterable) { + // (Self::Number(index), Self::Bytes(iterator)) => + // Ok(Self::Bytes(Self::slice_index(index, length, iterator)?)), + // (Self::Number(index), Self::String(iterator)) => { + // let iterator_vec = Vec::from(iterator.clone()); + // Ok(Self::String(String::from_utf8(Self::slice_generic(index.clone(), length.clone(), iterator_vec)?) + // .map_err(|_| ElemError::SliceInvalidUTF8 { index: index, length: length, iterator: iterator })?)) + // }, + (Self::Number(index), Self::Array(iterator)) => + Ok(Self::Json(Self::index_generic(index, iterator)?)), + (Self::Number(index), Self::Object(iterator)) => + Ok(Self::Json(Self::index_generic(index, iterator)?.1)), + (maybe_not_index, maybe_not_iterable) => { + Err(ElemError::IndexUnsupportedTypes { + maybe_not_index: &maybe_not_index.simple_type(), + maybe_not_iterable: &maybe_not_iterable.simple_type(), + }) + } + } + } + + // you can lookup a key in a Map (or fail, no recovery) + pub fn lookup(self, maybe_map: Self) -> Result { + match (self, maybe_map) { + (Self::String(key), Self::Object(map)) => { + Ok(Self::Json(map.get(&key) + .ok_or_else(|| ElemError::LookupKeyMissing { + key: key, + map: map.clone(), + }) + .map(|x|x.clone())?)) + }, + (maybe_not_key, maybe_not_map) => Err(ElemError::LookupUnsupportedTypes { + maybe_not_key: &maybe_not_key.simple_type(), + maybe_not_map: &maybe_not_map.simple_type(), + }), + } + } + + pub fn sha256(self) -> Result { + match self { + Self::Bytes(bytes) => { + Ok(Self::Bytes(super::sha256(&bytes))) + } + elem => Err(ElemError::HashUnsupportedType(elem.simple_type())), + } + } + + pub fn to_json(self) -> Result { + Ok(Self::Json(serde_json::to_value(self)?)) + } + + pub fn from_json(self) -> Result { + match self { + Self::Json(raw_json) => Ok(serde_json::from_value(raw_json)?), + non_json => Err(ElemError::FromJsonUnsupportedType(&non_json.simple_type())), + } + } + + // pub fn unit_from_json(self) -> Result { + // match self { + // Self::Json(serde_json::Null) => Ok(Elem::Unit), + // other => Err(ElemError::UnitFromJsonUnsupportedType(&other.simple_type())), + // } + // } + + pub fn object_from_json(self) -> Result { + match self { + Self::Json(serde_json::Value::Object(x)) => Ok(Elem::Object(x)), + Self::Json(x) => Err(ElemError::ObjectFromJsonUnexpecteJson(x)), + other => Err(ElemError::ObjectFromJsonUnsupportedType(&other.simple_type())), + } + } + + pub fn array_from_json(self) -> Result { + match self { + Self::Json(serde_json::Value::Array(x)) => Ok(Elem::Array(x)), + Self::Json(x) => Err(ElemError::ArrayFromJsonUnexpecteJson(x)), + other => Err(ElemError::ArrayFromJsonUnsupportedType(&other.simple_type())), + } + } + + pub fn string_from_json(self) -> Result { + match self { + Self::Json(serde_json::Value::String(x)) => Ok(Elem::String(x)), + Self::Json(x) => Err(ElemError::StringFromJsonUnexpecteJson(x)), + other => Err(ElemError::StringFromJsonUnsupportedType(&other.simple_type())), + } + } + +} + +#[derive(Debug, PartialEq, Error)] +pub enum ElemError { + #[error("expected Elem::Bool(true), found {0:?}")] + AssertTrueUnsupportedType(Elem), + #[error("expected true, but found false")] + AssertTrueFailed(), + #[error("check_le: incomparable types: {lhs:?}; {rhs:?}")] + CheckLeIncomparableTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("check_lt: incomparable types: {lhs:?}; {rhs:?}")] + CheckLtIncomparableTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("check_eq: incomparable types: {lhs:?}; {rhs:?}")] + CheckEqIncomparableTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("concat applied to unsupported types: lhs: {lhs:?}; rhs: {rhs:?}")] + ConcatUnsupportedTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("concat applied to strings that concatentate to invalid UTF8: lhs: {lhs:?}; rhs: {rhs:?}")] + ConcatInvalidUTF8 { + lhs: String, + rhs: String, + }, + + #[error("slice applied to unsupported types: maybe_not_offset: {maybe_not_offset:?}; maybe_not_length: {maybe_not_length:?}; maybe_not_iterable: {maybe_not_iterable:?}")] + SliceUnsupportedTypes { + maybe_not_offset: &'static str, + maybe_not_length: &'static str, + maybe_not_iterable: &'static str, + }, + #[error("slice applied to an 'offset' that can't be unpacked to u64: offset: {0:?}")] + SliceOffsetNotU64(Number), + #[error("slice applied to a 'length' that can't be unpacked to u64: length: {0:?}")] + SliceLengthNotU64(Number), + #[error("slice applied to an iterable that's too short for the given offset: offset: {offset:?} and length: {length:?}: iterable: {iterable:?}")] + SliceTooShort { + offset: usize, + length: usize, + iterable: &'static str, + }, + #[error("slice applied to offset and length whose sum overflows usize: offset: {offset:?} and length: {length:?}")] + SliceOverflow { + offset: Number, + length: Number, + }, + #[error("slice applied to arguments that produce invalid UTF8: offset: {offset:?}; length: {length:?}, iterator: {iterator:?}")] + SliceInvalidUTF8 { + offset: Number, + length: Number, + iterator: String, + }, + + #[error("index applied to unsupported types: maybe_not_index: {maybe_not_index:?}; maybe_not_iterable: {maybe_not_iterable:?}")] + IndexUnsupportedTypes { + maybe_not_index: &'static str, + maybe_not_iterable: &'static str, + }, + #[error("index applied to an 'index' that can't be unpacked to u64: {0:?}")] + IndexNotU64(Number), + #[error("index applied to an iterable that's too short for the given index: {index:?}; iterable: {iterable:?}")] + IndexTooShort { + index: usize, + iterable: &'static str, + }, + #[error("slice applied to offset and length whose sum overflows usize: {0:?}")] + IndexOverflow(Number), + + #[error("lookup applied to unsupported types: maybe_not_key: {maybe_not_key:?}; maybe_not_map: {maybe_not_map:?}")] + LookupUnsupportedTypes { + maybe_not_key: &'static str, + maybe_not_map: &'static str, + }, + #[error("lookup applied to a map that doesn't contain the given key: {key:?}; map: {map:?}")] + LookupKeyMissing { + key: String, + map: Map, + }, + + #[error("sha256 applied an Elem of an unsupported type ({0})")] + HashUnsupportedType(&'static str), + + #[error("to_json/from_json serialization failed: ({0})")] + ToFromJsonFailed(String), + + #[error("from_json applied an Elem of an unsupported type ({0})")] + FromJsonUnsupportedType(&'static str), + + #[error("object_from_json applied an Elem of an unsupported type ({0})")] + ObjectFromJsonUnsupportedType(&'static str), + #[error("object_from_json applied unexpected JSON: ({0})")] + ObjectFromJsonUnexpecteJson(Value), + + #[error("array_from_json applied an Elem of an unsupported type ({0})")] + ArrayFromJsonUnsupportedType(&'static str), + #[error("array_from_json applied unexpected JSON: ({0})")] + ArrayFromJsonUnexpecteJson(Value), + + #[error("string_from_json applied an Elem of an unsupported type ({0})")] + StringFromJsonUnsupportedType(&'static str), + #[error("string_from_json applied unexpected JSON: ({0})")] + StringFromJsonUnexpecteJson(Value), +} + +impl From for ElemError { + fn from(error: serde_json::Error) -> Self { + ElemError::ToFromJsonFailed(format!("{}", error)) + } +} + + + + +// TODO: implement simple_type on this instead of Elem +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum ElemSymbol { + UnitSymbol, + BoolSymbol, + NumberSymbol, + BytesSymbol, + StringSymbol, + ArraySymbol, + ObjectSymbol, + JsonSymbol, +} + +impl From for ElemSymbol { + fn from(x: Elem) -> Self { + match x { + Elem::Unit => Self::UnitSymbol, + Elem::Bool(_) => Self::BoolSymbol, + Elem::Number(_) => Self::NumberSymbol, + Elem::Bytes(_) => Self::BytesSymbol, + Elem::String(_) => Self::StringSymbol, + Elem::Array(_) => Self::ArraySymbol, + Elem::Object(_) => Self::ObjectSymbol, + Elem::Json(_) => Self::JsonSymbol, + } + } +} + +#[cfg(test)] +impl ElemSymbol { + // TODO: synchronize with Default trait + pub fn default_elem(self) -> Elem { + match self { + Self::UnitSymbol => Elem::Unit, + Self::BoolSymbol => Elem::Bool(false), + Self::NumberSymbol => Elem::Number(From::from(0u8)), + Self::BytesSymbol => Elem::Bytes(vec![]), + Self::StringSymbol => Elem::String("".to_string()), + Self::ArraySymbol => Elem::Array(vec![]), + Self::ObjectSymbol => Elem::Object(Map::new()), + Self::JsonSymbol => Elem::Json(serde_json::Value::Null), + } + } +} + +#[cfg(test)] +mod elem_symbol_tests { + use super::*; + + #[test] + fn test_from_default_elem() { + for symbol in [ + ElemSymbol::UnitSymbol, + ElemSymbol::BoolSymbol, + ElemSymbol::NumberSymbol, + ElemSymbol::BytesSymbol, + ElemSymbol::StringSymbol, + ElemSymbol::ArraySymbol, + ElemSymbol::ObjectSymbol, + ElemSymbol::JsonSymbol, + ] { + assert_eq!(symbol, From::from(symbol.default_elem())) + } + } +} + + + +// TODO: Bool/Number primitives // Bool(bool), // - neg @@ -72,18 +468,6 @@ impl PartialOrd for Elem { // - mul // - div -// DONE: - -// all: -// - equals -// - compare - -// Unit, -// Array(Vec), -// Bytes(Vec), -// String(String), -// Object(Map), - #[derive(Debug, Serialize, Deserialize)] pub enum Instruction { @@ -98,6 +482,11 @@ pub enum Instruction { FnIndex, // Array FnLookup, // Map FnAssertTrue, + FnToJson, + FnFromJson, + FnObjectFromJson, + FnArrayFromJson, + FnStringFromJson, } @@ -164,7 +553,7 @@ impl Restack { pub fn dup_n(ix: usize) -> Self { Restack { restack_depth: ix+1, - restack_vec: (ix..ix).chain(0..ix).collect(), + restack_vec: (ix..=ix).chain(0..=ix).collect(), } } From eea78763351864079191ae963e27de9a3143e5a6 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Fri, 11 Feb 2022 11:21:23 -0500 Subject: [PATCH 13/77] add debug method for stack, verbose execution, fix push/pop, extend demo --- src/executor.rs | 28 ++++- src/main.rs | 270 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 292 insertions(+), 6 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index ed1af29..ddc9f18 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -11,7 +11,10 @@ pub struct Executor { impl Executor { pub fn consume(&mut self, expressions: Instructions) -> Result<(), ExecError> { + self.debug()?; for expr in expressions { + println!("------------------------------------------------------------------------------------------"); + println!("#: {:?}", expr); match expr { Instruction::Push(elem) => self.push(elem), Instruction::FnRestack(restack) => self.restack(restack)?, @@ -30,12 +33,24 @@ impl Executor { Instruction::FnArrayFromJson => self.array_from_json()?, Instruction::FnStringFromJson => self.string_from_json()?, } + self.debug()?; + } + Ok(()) + } + + pub fn debug(&self) -> Result<(), ExecError> { + println!("------------------------------------------------------------------------------------------"); + for stack_elem in &self.stack { + println!("------------------------------"); + println!("{}", serde_json::to_string_pretty(stack_elem)?) } Ok(()) } pub fn push(&mut self, elem: Elem) { - self.stack.push(elem) + let mut memo = vec![elem]; + memo.append(&mut self.stack.clone()); + self.stack = memo; } fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { @@ -144,7 +159,10 @@ impl Executor { // TODO: since pop can fail, require passing debug info to it // (so we know what we were expecting) fn pop(&mut self) -> Result { - self.stack.pop().ok_or_else(|| ExecError::EmptyStack) + // self.stack.pop().ok_or_else(|| ExecError::EmptyStack) + let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; + self.stack = self.stack.drain(1..).collect(); + Ok(result.clone()) } } @@ -165,3 +183,9 @@ impl From for ExecError { } } +impl From for ExecError { + fn from(error: serde_json::Error) -> Self { + ExecError::ElemError(ElemError::ToFromJsonFailed(format!("{}", error))) + } +} + diff --git a/src/main.rs b/src/main.rs index 3b3b8f0..d9f84fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,7 +70,7 @@ fn main() { "#; // let json_instructions = parse_json()" - let instructions: Instructions = vec![ + let _instructions: Instructions = vec![ Instruction::FnObjectFromJson, Instruction::FnRestack(Restack::dup()), @@ -87,14 +87,276 @@ fn main() { Instruction::FnLookup, Instruction::FnStringFromJson, Instruction::Push(Elem::String("tokenbalance".to_string())), - Instruction::FnCheckEqual, + Instruction::FnCheckLe, + Instruction::FnAssertTrue, + + Instruction::FnRestack(Restack::dup()), + Instruction::Push(Elem::String("contractaddress".to_string())), + Instruction::FnLookup, + Instruction::FnStringFromJson, + Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), + Instruction::FnCheckLe, + Instruction::FnAssertTrue, + + Instruction::FnRestack(Restack::dup()), + Instruction::Push(Elem::String("response".to_string())), + Instruction::FnLookup, + Instruction::FnObjectFromJson, + Instruction::Push(Elem::String("result".to_string())), + Instruction::FnLookup, + Instruction::FnStringFromJson, + Instruction::Push(Elem::String("135499".to_string())), + Instruction::FnCheckLe, + Instruction::FnAssertTrue, + + Instruction::FnRestack(Restack::drop()), + Instruction::Push(Elem::String("prompts".to_string())), + Instruction::FnLookup, + Instruction::FnArrayFromJson, + + Instruction::Push(Elem::Number(From::from(0u8))), + Instruction::FnIndex, + Instruction::FnObjectFromJson, + + Instruction::FnRestack(Restack::dup()), + Instruction::Push(Elem::String("action".to_string())), + Instruction::FnLookup, + Instruction::FnStringFromJson, + Instruction::Push(Elem::String("siwe".to_string())), + Instruction::FnCheckLe, + Instruction::FnAssertTrue, + + Instruction::FnRestack(Restack::dup()), + Instruction::Push(Elem::String("version".to_string())), + Instruction::FnLookup, + Instruction::FnStringFromJson, + Instruction::Push(Elem::String("1.1.0".to_string())), + Instruction::FnCheckLe, + Instruction::FnAssertTrue, + + // Instruction::FnRestack(Restack::dup()), + Instruction::Push(Elem::String("data".to_string())), + Instruction::FnLookup, + Instruction::FnObjectFromJson, + Instruction::Push(Elem::String("fields".to_string())), + Instruction::FnLookup, + Instruction::FnObjectFromJson, + Instruction::Push(Elem::String("address".to_string())), + Instruction::FnLookup, + Instruction::FnStringFromJson, + Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), + Instruction::FnCheckLe, Instruction::FnAssertTrue, ]; + let json_instructions = r#" + [ + "FnObjectFromJson", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "queries" + } + }, + "FnLookup", + "FnArrayFromJson", + { + "Push": { + "Number": 0 + } + }, + "FnIndex", + "FnObjectFromJson", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "action" + } + }, + "FnLookup", + "FnStringFromJson", + { + "Push": { + "String": "tokenbalance" + } + }, + "FnCheckLe", + "FnAssertTrue", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "contractaddress" + } + }, + "FnLookup", + "FnStringFromJson", + { + "Push": { + "String": "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + } + }, + "FnCheckLe", + "FnAssertTrue", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "response" + } + }, + "FnLookup", + "FnObjectFromJson", + { + "Push": { + "String": "result" + } + }, + "FnLookup", + "FnStringFromJson", + { + "Push": { + "String": "135499" + } + }, + "FnCheckLe", + "FnAssertTrue", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [] + } + }, + { + "Push": { + "String": "prompts" + } + }, + "FnLookup", + "FnArrayFromJson", + { + "Push": { + "Number": 0 + } + }, + "FnIndex", + "FnObjectFromJson", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "action" + } + }, + "FnLookup", + "FnStringFromJson", + { + "Push": { + "String": "siwe" + } + }, + "FnCheckLe", + "FnAssertTrue", + { + "FnRestack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "version" + } + }, + "FnLookup", + "FnStringFromJson", + { + "Push": { + "String": "1.1.0" + } + }, + "FnCheckLe", + "FnAssertTrue", + { + "Push": { + "String": "data" + } + }, + "FnLookup", + "FnObjectFromJson", + { + "Push": { + "String": "fields" + } + }, + "FnLookup", + "FnObjectFromJson", + { + "Push": { + "String": "address" + } + }, + "FnLookup", + "FnStringFromJson", + { + "Push": { + "String": "0xe04f27eb70e025b78871a2ad7eabe85e61212761" + } + }, + "FnCheckLe", + "FnAssertTrue" + ] + "#; + + // assert_eq!(serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap().into_iter(), instructions); + // println!("{}", serde_json::to_string_pretty(&serde_json::to_value(instructions).unwrap()).unwrap()); + let mut exec = Executor::default(); exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); - exec.consume(instructions) + exec.consume(serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap()) .expect("error processing instructions"); - println!("{}", format!("{:?}", exec)); + + println!("FINAL STACK"); + println!("{:?}", exec); } From eff41f926c274fec4250d18ed2d6c27c75ee83a8 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Fri, 11 Feb 2022 14:13:29 -0500 Subject: [PATCH 14/77] fixed eq bug, kick out restack, cleanup example --- src/executor.rs | 10 +- src/lib.rs | 5 +- src/main.rs | 210 ++-------------------------------------- src/parse.rs | 2 +- src/restack.rs | 222 ++++++++++++++++++++++++++++++++++++++++++ src/types.rs | 249 +++--------------------------------------------- 6 files changed, 251 insertions(+), 447 deletions(-) create mode 100644 src/restack.rs diff --git a/src/executor.rs b/src/executor.rs index ddc9f18..822793f 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,5 @@ -use crate::types::{Elem, ElemError, Instruction, Instructions, Restack, RestackError}; - +use crate::restack::{Restack, RestackError}; +use crate::types::{Elem, ElemError, Instruction, Instructions}; use thiserror::Error; // TODO: implement n-step executor && errors that tell you what step they're on @@ -21,7 +21,7 @@ impl Executor { Instruction::FnAssertTrue => self.assert_true()?, Instruction::FnCheckLe => self.check_le()?, Instruction::FnCheckLt => self.check_lt()?, - Instruction::FnCheckEqual => self.check_equal()?, + Instruction::FnCheckEq => self.check_eq()?, Instruction::FnConcat => self.concat()?, Instruction::FnSlice => self.slice()?, Instruction::FnIndex => self.index()?, @@ -82,10 +82,10 @@ impl Executor { Ok(()) } - fn check_equal(&mut self) -> Result<(), ExecError> { + fn check_eq(&mut self) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; - self.push(one.check_lt(other)?); + self.push(one.check_eq(other)?); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 5361448..c0ccebd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ +mod restack; +pub use restack::Restack; mod types; -pub use types::{Elem, Instruction, Instructions, Restack}; - +pub use types::{Elem, Instruction, Instructions}; mod parse; pub use parse::parse; mod executor; diff --git a/src/main.rs b/src/main.rs index d9f84fc..8507a5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,7 +70,8 @@ fn main() { "#; // let json_instructions = parse_json()" - let _instructions: Instructions = vec![ + let instructions: Instructions = vec![ + Instruction::FnObjectFromJson, Instruction::FnRestack(Restack::dup()), @@ -144,217 +145,22 @@ fn main() { Instruction::Push(Elem::String("address".to_string())), Instruction::FnLookup, Instruction::FnStringFromJson, + + Instruction::FnRestack(Restack::drop()), Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), - Instruction::FnCheckLe, + Instruction::FnRestack(Restack::dup()), + Instruction::FnCheckEq, Instruction::FnAssertTrue, ]; - let json_instructions = r#" - [ - "FnObjectFromJson", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [ - 0, - 0 - ] - } - }, - { - "Push": { - "String": "queries" - } - }, - "FnLookup", - "FnArrayFromJson", - { - "Push": { - "Number": 0 - } - }, - "FnIndex", - "FnObjectFromJson", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [ - 0, - 0 - ] - } - }, - { - "Push": { - "String": "action" - } - }, - "FnLookup", - "FnStringFromJson", - { - "Push": { - "String": "tokenbalance" - } - }, - "FnCheckLe", - "FnAssertTrue", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [ - 0, - 0 - ] - } - }, - { - "Push": { - "String": "contractaddress" - } - }, - "FnLookup", - "FnStringFromJson", - { - "Push": { - "String": "0x57d90b64a1a57749b0f932f1a3395792e12e7055" - } - }, - "FnCheckLe", - "FnAssertTrue", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [ - 0, - 0 - ] - } - }, - { - "Push": { - "String": "response" - } - }, - "FnLookup", - "FnObjectFromJson", - { - "Push": { - "String": "result" - } - }, - "FnLookup", - "FnStringFromJson", - { - "Push": { - "String": "135499" - } - }, - "FnCheckLe", - "FnAssertTrue", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [] - } - }, - { - "Push": { - "String": "prompts" - } - }, - "FnLookup", - "FnArrayFromJson", - { - "Push": { - "Number": 0 - } - }, - "FnIndex", - "FnObjectFromJson", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [ - 0, - 0 - ] - } - }, - { - "Push": { - "String": "action" - } - }, - "FnLookup", - "FnStringFromJson", - { - "Push": { - "String": "siwe" - } - }, - "FnCheckLe", - "FnAssertTrue", - { - "FnRestack": { - "restack_depth": 1, - "restack_vec": [ - 0, - 0 - ] - } - }, - { - "Push": { - "String": "version" - } - }, - "FnLookup", - "FnStringFromJson", - { - "Push": { - "String": "1.1.0" - } - }, - "FnCheckLe", - "FnAssertTrue", - { - "Push": { - "String": "data" - } - }, - "FnLookup", - "FnObjectFromJson", - { - "Push": { - "String": "fields" - } - }, - "FnLookup", - "FnObjectFromJson", - { - "Push": { - "String": "address" - } - }, - "FnLookup", - "FnStringFromJson", - { - "Push": { - "String": "0xe04f27eb70e025b78871a2ad7eabe85e61212761" - } - }, - "FnCheckLe", - "FnAssertTrue" - ] - "#; - // assert_eq!(serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap().into_iter(), instructions); // println!("{}", serde_json::to_string_pretty(&serde_json::to_value(instructions).unwrap()).unwrap()); let mut exec = Executor::default(); exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); - exec.consume(serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap()) + // serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap() + exec.consume(instructions) .expect("error processing instructions"); println!("FINAL STACK"); diff --git a/src/parse.rs b/src/parse.rs index 1d25da6..4035b06 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -38,7 +38,7 @@ fn parse_instruction(term: &str) -> Result { } match term { "assert_true" => Ok(Instruction::FnAssertTrue), - "check_equal" => Ok(Instruction::FnCheckEqual), + "check_equal" => Ok(Instruction::FnCheckEq), "hash_sha256" => Ok(Instruction::FnHashSha256), _ => Err(ParseError::UnsupportedInstruction(term.to_string())), } diff --git a/src/restack.rs b/src/restack.rs new file mode 100644 index 0000000..e309760 --- /dev/null +++ b/src/restack.rs @@ -0,0 +1,222 @@ +use std::cmp; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +pub type StackIx = usize; + +// Stack manipulation: +// - All stack manipulations: +// + dig +// + dug +// + dip +// + dup +// + swap +// + drop +// - they all boil down to: +// 1. drop inputs +// 2. replicate inputs +// 3. reorder inputs +// - which conveniently boils down to: +// + xs : [ old_stack_index ] +// + map (\x -> xs !! x) xs +// - successful iff all old_stack_index's < length stack +// - pretty-printing? +// + REQUIRED: constant compile-time choice of manipulations +// + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] +// + global: keep track of stack indices (always possible?) and print where it's from??? +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Restack { + restack_depth: StackIx, + restack_vec: Vec, +} + +impl Restack { + // identity + pub fn id() -> Self { + Restack { + restack_depth: 0, + restack_vec: vec![], + } + } + + // swap first two stack elements + pub fn swap() -> Self { + Restack { + restack_depth: 2, + restack_vec: vec![1usize, 0], + } + } + + // drop the first (n) stack elements + pub fn drop_n(n: usize) -> Self { + Restack { + restack_depth: n, + restack_vec: vec![] + } + } + + // drop the first stack element + pub fn drop() -> Self { + Self::drop_n(1) + } + + // duplicates the (ix)th value onto the top of the stack (0-indexed) + pub fn dup_n(ix: usize) -> Self { + Restack { + restack_depth: ix+1, + restack_vec: (ix..=ix).chain(0..=ix).collect(), + } + } + + // duplicates the 0th value onto the top of the stack (0-indexed) + pub fn dup() -> Self { + Self::dup_n(0) + } + + // pull the (ix)th element to the top of the stack + // dig 4 = { 5, [3, 0, 1, 2] } + pub fn dig(ix: usize) -> Self { + Restack { + restack_depth: ix+1, + restack_vec: (0..=ix).cycle().skip(ix).take(ix+1).collect(), + } + } + + // push the top of the stack to the (ix)th position + // dug 4 = { 5, [1, 2, 3, 0] } + pub fn dug(ix: usize) -> Self { + Restack { + restack_depth: ix+1, + restack_vec: (1..=ix).chain(std::iter::once(0)).collect() + } + } + + // restack a Stack + pub fn run(&self, stack: &mut Vec) -> Result, RestackError> { + if self.restack_depth <= stack.len() { + let result = self.restack_vec.iter().map(|&restack_index| + match stack.get(restack_index) { + None => Err(RestackError::StackIndexInvalid{ restack_index: restack_index, restack_depth: self.restack_depth, }), + Some(stack_element) => Ok( stack_element.clone() ), + } + ).collect::, RestackError>>(); + match result { + Ok(mut result_ok) => { + result_ok.extend(stack.drain(self.restack_depth..)); + Ok(result_ok) }, + Err(e) => Err(e) + } + + } else { + Err(RestackError::InvalidDepth{ stack_len: stack.len(), restack_depth: self.restack_depth, }) + } + } + + // self.valid_depth() -> + // self.restack_depth <= xs.len() -> + // self.run(xs).is_ok() == true + pub fn valid_depth(&self) -> bool { + !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) + } + + // NOTE: unchecked (run valid_depth on arguments for safe version) + // x.append(y).run(s) == x.run(y.run(s)) + pub fn append(&self, other: Self) -> Self { + Restack { + restack_depth: cmp::max(self.restack_depth, other.restack_depth), + restack_vec: self.restack_vec.iter().map(|&restack_index| + match other.restack_vec.get(restack_index) { + None => restack_index, + Some(stack_index) => stack_index.clone(), + } + ).collect() + } + } +} + + +#[derive(Debug, PartialEq, Error)] +pub enum RestackError { + #[error("invalid Restack: restack_index = {restack_index:?} out of bounds for restack_depth = {restack_depth:?}")] + StackIndexInvalid { + restack_index: usize, + restack_depth: usize, + }, + #[error("attempt to restack {restack_depth:?} elements of a stack with only {stack_len:?} elements")] + InvalidDepth { + stack_len: usize, + restack_depth: usize, + }, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_restack_id() { + let mut example_stack = vec![false, true]; + let restack = Restack::id(); + assert!(restack.valid_depth(), "Restack::id() has invalid depth"); + assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) + } + + #[test] + fn test_restack_dig() { + assert!(Restack::dig(4).valid_depth(), "Restack::dig(4) has invalid depth"); + assert_eq!(Restack { restack_depth: 5, restack_vec: vec![4, 0, 1, 2, 3] }, Restack::dig(4)); + let mut example_stack_in = vec![false, false, false, false, true]; + let example_stack_out = vec![true, false, false, false, false]; + assert_eq!(Ok(example_stack_out.clone()), Restack::dig(4).run(&mut example_stack_in)) + } + + #[test] + fn test_restack_dug() { + assert!(Restack::dug(4).valid_depth(), "Restack::dug(4) has invalid depth"); + assert_eq!(Restack { restack_depth: 5, restack_vec: vec![1, 2, 3, 4, 0] }, Restack::dug(4)); + let mut example_stack_in = vec![true, false, false, false, false]; + let example_stack_out = vec![false, false, false, false, true]; + assert_eq!(Ok(example_stack_out.clone()), Restack::dug(4).run(&mut example_stack_in)) + } + + #[test] + fn test_restack_drop_n() { + let example_stack_in = vec![false, true, false]; + for example_stack_out in + [vec![false, true, false], + vec![true, false], + vec![false], + vec![]] { + let restack = Restack::drop_n(3 - example_stack_out.len()); + assert!(restack.valid_depth(), "Restack::drop_n(_) has invalid depth"); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in.clone())); + } + } + + #[test] + fn test_restack_drop() { + let mut example_stack_in = vec![false, true]; + let example_stack_out = vec![true]; + let restack = Restack::drop(); + assert!(restack.valid_depth(), "Restack::drop() has invalid depth"); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) + } + + #[test] + fn test_restack_swap() { + let mut example_stack_in = vec![false, true]; + let example_stack_out = vec![true, false]; + let restack = Restack::swap(); + assert!(restack.valid_depth(), "Restack::swap() has invalid depth"); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) + } + + #[test] + fn test_restack_swap_twice_append() { + let mut example_stack = vec![false, true]; + let restack = Restack::swap().append(Restack::swap()); + assert!(restack.valid_depth(), "Restack::swap().append(Restack::swap()) has invalid depth"); + assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) + } +} diff --git a/src/types.rs b/src/types.rs index d37bd25..2cf373d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,5 @@ +use crate::restack::Restack; + use thiserror::Error; use std::cmp; @@ -70,21 +72,15 @@ impl Elem { Ok(Self::Bool(result)) } - // TODO: fixme pub fn check_eq(self, other: Self) -> Result { - match (self, other) { - (Self::String(x), Self::String(y)) => Ok(Self::Bool(x.chars().collect::>() == y.chars().collect::>())), - (x, y) => Ok(Self::Bool(x == y)), - } - - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckEqIncomparableTypes { - // lhs: self.simple_type(), - // rhs: other.simple_type() })? { - // cmp::Ordering::Equal => true, - // _ => false, - // }; - // Ok(Self::Bool(self == other)) + let result = match self.partial_cmp(&other) + .ok_or_else(|| ElemError::CheckEqIncomparableTypes { + lhs: self.simple_type(), + rhs: other.simple_type() })? { + cmp::Ordering::Equal => true, + _ => false, + }; + Ok(Self::Bool(result)) } fn concat_generic::Item>>(x: T, y: T) -> T { @@ -476,7 +472,7 @@ pub enum Instruction { FnHashSha256, FnCheckLe, FnCheckLt, - FnCheckEqual, + FnCheckEq, FnConcat, FnSlice, FnIndex, // Array @@ -489,228 +485,7 @@ pub enum Instruction { FnStringFromJson, } - -pub type StackIx = usize; -pub type Stack = Vec; - -// Stack manipulation: -// - All stack manipulations: -// + dig -// + dug -// + dip -// + dup -// + swap -// + drop -// - they all boil down to: -// 1. drop inputs -// 2. replicate inputs -// 3. reorder inputs -// - which conveniently boils down to: -// + xs : [ old_stack_index ] -// + map (\x -> xs !! x) xs -// - successful iff all old_stack_index's < length stack -// - pretty-printing? -// + REQUIRED: constant compile-time choice of manipulations -// + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] -// + global: keep track of stack indices (always possible?) and print where it's from??? -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct Restack { - restack_depth: StackIx, - restack_vec: Vec, -} - -impl Restack { - // identity - pub fn id() -> Self { - Restack { - restack_depth: 0, - restack_vec: vec![], - } - } - - // swap first two stack elements - pub fn swap() -> Self { - Restack { - restack_depth: 2, - restack_vec: vec![1usize, 0], - } - } - - // drop the first (n) stack elements - pub fn drop_n(n: usize) -> Self { - Restack { - restack_depth: n, - restack_vec: vec![] - } - } - - // drop the first stack element - pub fn drop() -> Self { - Self::drop_n(1) - } - - // duplicates the (ix)th value onto the top of the stack (0-indexed) - pub fn dup_n(ix: usize) -> Self { - Restack { - restack_depth: ix+1, - restack_vec: (ix..=ix).chain(0..=ix).collect(), - } - } - - // duplicates the 0th value onto the top of the stack (0-indexed) - pub fn dup() -> Self { - Self::dup_n(0) - } - - // pull the (ix)th element to the top of the stack - // dig 4 = { 5, [3, 0, 1, 2] } - pub fn dig(ix: usize) -> Self { - Restack { - restack_depth: ix+1, - restack_vec: (0..=ix).cycle().skip(ix).take(ix+1).collect(), - } - } - - // push the top of the stack to the (ix)th position - // dug 4 = { 5, [1, 2, 3, 0] } - pub fn dug(ix: usize) -> Self { - Restack { - restack_depth: ix+1, - restack_vec: (1..=ix).chain(std::iter::once(0)).collect() - } - } - - // restack a Stack - pub fn run(&self, stack: &mut Stack) -> Result { - if self.restack_depth <= stack.len() { - let result = self.restack_vec.iter().map(|&restack_index| - match stack.get(restack_index) { - None => Err(RestackError::StackIndexInvalid{ restack_index: restack_index, restack_depth: self.restack_depth, }), - Some(stack_element) => Ok( stack_element.clone() ), - } - ).collect::>(); - match result { - Ok(mut result_ok) => { - result_ok.extend(stack.drain(self.restack_depth..)); - Ok(result_ok) }, - Err(e) => Err(e) - } - - } else { - Err(RestackError::InvalidDepth{ stack_len: stack.len(), restack_depth: self.restack_depth, }) - } - } - - // self.valid_depth() -> - // self.restack_depth <= xs.len() -> - // self.run(xs).is_ok() == true - pub fn valid_depth(&self) -> bool { - !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) - } - - // NOTE: unchecked (run valid_depth on arguments for safe version) - // x.append(y).run(s) == x.run(y.run(s)) - pub fn append(&self, other: Self) -> Self { - Restack { - restack_depth: cmp::max(self.restack_depth, other.restack_depth), - restack_vec: self.restack_vec.iter().map(|&restack_index| - match other.restack_vec.get(restack_index) { - None => restack_index, - Some(stack_index) => stack_index.clone(), - } - ).collect() - } - } -} - - -#[derive(Debug, PartialEq, Error)] -pub enum RestackError { - #[error("invalid Restack: restack_index = {restack_index:?} out of bounds for restack_depth = {restack_depth:?}")] - StackIndexInvalid { - restack_index: usize, - restack_depth: usize, - }, - #[error("attempt to restack {restack_depth:?} elements of a stack with only {stack_len:?} elements")] - InvalidDepth { - stack_len: usize, - restack_depth: usize, - }, -} - - pub type Instructions = Vec; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_restack_id() { - let mut example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; - let restack = Restack::id(); - assert!(restack.valid_depth(), "Restack::id() has invalid depth"); - assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) - } - - #[test] - fn test_restack_dig() { - assert!(Restack::dig(4).valid_depth(), "Restack::dig(4) has invalid depth"); - assert_eq!(Restack { restack_depth: 5, restack_vec: vec![4, 0, 1, 2, 3] }, Restack::dig(4)); - let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(true)]; - let example_stack_out = vec![Elem::Bool(true), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false)]; - assert_eq!(Ok(example_stack_out.clone()), Restack::dig(4).run(&mut example_stack_in)) - } - - #[test] - fn test_restack_dug() { - assert!(Restack::dug(4).valid_depth(), "Restack::dug(4) has invalid depth"); - assert_eq!(Restack { restack_depth: 5, restack_vec: vec![1, 2, 3, 4, 0] }, Restack::dug(4)); - let mut example_stack_in = vec![Elem::Bool(true), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false)]; - let example_stack_out = vec![Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(false), Elem::Bool(true)]; - assert_eq!(Ok(example_stack_out.clone()), Restack::dug(4).run(&mut example_stack_in)) - } - - #[test] - fn test_restack_drop_n() { - let example_stack_in = vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)]; - for example_stack_out in - [vec![Elem::Bool(false), Elem::Bool(true), Elem::Bool(false)], - vec![Elem::Bool(true), Elem::Bool(false)], - vec![Elem::Bool(false)], - vec![]] { - let restack = Restack::drop_n(3 - example_stack_out.len()); - assert!(restack.valid_depth(), "Restack::drop_n(_) has invalid depth"); - assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in.clone())); - } - } - - #[test] - fn test_restack_drop() { - let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; - let example_stack_out = vec![Elem::Bool(true)]; - let restack = Restack::drop(); - assert!(restack.valid_depth(), "Restack::drop() has invalid depth"); - assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) - } - - #[test] - fn test_restack_swap() { - let mut example_stack_in = vec![Elem::Bool(false), Elem::Bool(true)]; - let example_stack_out = vec![Elem::Bool(true), Elem::Bool(false)]; - let restack = Restack::swap(); - assert!(restack.valid_depth(), "Restack::swap() has invalid depth"); - assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) - } - - #[test] - fn test_restack_swap_twice_append() { - let mut example_stack = vec![Elem::Bool(false), Elem::Bool(true)]; - let restack = Restack::swap().append(Restack::swap()); - assert!(restack.valid_depth(), "Restack::swap().append(Restack::swap()) has invalid depth"); - assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) - } - -} +// pub type Stack = Vec; From 7fd559ba5b0b5f05e26144b94fb0cb596a2274a3 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Fri, 11 Feb 2022 14:31:11 -0500 Subject: [PATCH 15/77] tests now passing: deriving clone, eq, ord on instruction --- src/lib.rs | 2 +- src/main.rs | 8 +++----- src/restack.rs | 2 +- src/types.rs | 41 +++++++++++++++++++++++------------------ 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c0ccebd..5ab94b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ pub use restack::Restack; mod types; pub use types::{Elem, Instruction, Instructions}; mod parse; -pub use parse::parse; +pub use parse::{parse, parse_json}; mod executor; pub use executor::Executor; diff --git a/src/main.rs b/src/main.rs index 8507a5c..af98d12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use cryptoscript::{Elem, Executor, Instruction, Instructions, Restack}; +use cryptoscript::{parse_json, Elem, Executor, Instruction, Instructions, Restack}; #[cfg(test)] mod tests { @@ -6,7 +6,6 @@ mod tests { use cryptoscript::{parse}; #[test] - #[should_panic] fn test_parse_exec() { let instructions = parse( r#" @@ -154,12 +153,11 @@ fn main() { ]; - // assert_eq!(serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap().into_iter(), instructions); - // println!("{}", serde_json::to_string_pretty(&serde_json::to_value(instructions).unwrap()).unwrap()); + let json_instructions = serde_json::to_string_pretty(&serde_json::to_value(instructions.clone()).unwrap()).unwrap(); + assert_eq!(parse_json(&json_instructions).unwrap(), instructions); let mut exec = Executor::default(); exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); - // serde_json::from_value::(serde_json::from_str(json_instructions).unwrap()).unwrap() exec.consume(instructions) .expect("error processing instructions"); diff --git a/src/restack.rs b/src/restack.rs index e309760..3024ae5 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -25,7 +25,7 @@ pub type StackIx = usize; // + REQUIRED: constant compile-time choice of manipulations // + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] // + global: keep track of stack indices (always possible?) and print where it's from??? -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Restack { restack_depth: StackIx, restack_vec: Vec, diff --git a/src/types.rs b/src/types.rs index 2cf373d..d7388a7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,8 +8,30 @@ use std::convert::TryFrom; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; +// TODO: for demo: +// - step through / display steps better +// - support signatures: +// + check_sig +// + to_pub_key + // TODO: property based tests +// TODO: Bool/Number primitives + +// Bool(bool), +// - neg +// - and +// - or + +// Number(Number), -->> later +// - to_int +// - add +// - sub +// - mul +// - div + + + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { Unit, @@ -448,24 +470,7 @@ mod elem_symbol_tests { } } - - -// TODO: Bool/Number primitives - -// Bool(bool), -// - neg -// - and -// - or - -// Number(Number), -->> later -// - to_int -// - add -// - sub -// - mul -// - div - - -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub enum Instruction { Push(Elem), FnRestack(Restack), From 92e27bc60e543c5edd77c87a3893a951fc9c2426 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 14 Feb 2022 10:13:32 -0500 Subject: [PATCH 16/77] rename s/Fn// --- src/executor.rs | 30 +++++++------- src/main.rs | 107 ++++++++++++++++++++++++++---------------------- src/parse.rs | 6 +-- src/types.rs | 30 +++++++------- 4 files changed, 90 insertions(+), 83 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 822793f..f0ad8e6 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -17,21 +17,21 @@ impl Executor { println!("#: {:?}", expr); match expr { Instruction::Push(elem) => self.push(elem), - Instruction::FnRestack(restack) => self.restack(restack)?, - Instruction::FnAssertTrue => self.assert_true()?, - Instruction::FnCheckLe => self.check_le()?, - Instruction::FnCheckLt => self.check_lt()?, - Instruction::FnCheckEq => self.check_eq()?, - Instruction::FnConcat => self.concat()?, - Instruction::FnSlice => self.slice()?, - Instruction::FnIndex => self.index()?, - Instruction::FnLookup => self.lookup()?, - Instruction::FnHashSha256 => self.sha256()?, - Instruction::FnToJson => self.to_json()?, - Instruction::FnFromJson => self.from_json()?, - Instruction::FnObjectFromJson => self.object_from_json()?, - Instruction::FnArrayFromJson => self.array_from_json()?, - Instruction::FnStringFromJson => self.string_from_json()?, + Instruction::Restack(restack) => self.restack(restack)?, + Instruction::AssertTrue => self.assert_true()?, + Instruction::CheckLe => self.check_le()?, + Instruction::CheckLt => self.check_lt()?, + Instruction::CheckEq => self.check_eq()?, + Instruction::Concat => self.concat()?, + Instruction::Slice => self.slice()?, + Instruction::Index => self.index()?, + Instruction::Lookup => self.lookup()?, + Instruction::HashSha256 => self.sha256()?, + Instruction::ToJson => self.to_json()?, + Instruction::FromJson => self.from_json()?, + Instruction::ObjectFromJson => self.object_from_json()?, + Instruction::ArrayFromJson => self.array_from_json()?, + Instruction::StringFromJson => self.string_from_json()?, } self.debug()?; } diff --git a/src/main.rs b/src/main.rs index af98d12..85ab6cf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,85 +71,92 @@ fn main() { // let json_instructions = parse_json()" let instructions: Instructions = vec![ - Instruction::FnObjectFromJson, - Instruction::FnRestack(Restack::dup()), + Instruction::ObjectFromJson, + Instruction::Restack(Restack::dup()), + // x["queries"] Instruction::Push(Elem::String("queries".to_string())), - Instruction::FnLookup, - Instruction::FnArrayFromJson, + Instruction::Lookup, + Instruction::ArrayFromJson, + // x[0] Instruction::Push(Elem::Number(From::from(0u8))), - Instruction::FnIndex, - Instruction::FnObjectFromJson, + Instruction::Index, + Instruction::ObjectFromJson, - Instruction::FnRestack(Restack::dup()), + // x["action"] = "tokenbalance" + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("action".to_string())), - Instruction::FnLookup, - Instruction::FnStringFromJson, + Instruction::Lookup, + Instruction::StringFromJson, Instruction::Push(Elem::String("tokenbalance".to_string())), - Instruction::FnCheckLe, - Instruction::FnAssertTrue, + Instruction::CheckEq, + Instruction::AssertTrue, - Instruction::FnRestack(Restack::dup()), + // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("contractaddress".to_string())), - Instruction::FnLookup, - Instruction::FnStringFromJson, + Instruction::Lookup, + Instruction::StringFromJson, Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), - Instruction::FnCheckLe, - Instruction::FnAssertTrue, + Instruction::CheckEq, + Instruction::AssertTrue, - Instruction::FnRestack(Restack::dup()), + // x["response"]["result"] = "135499" + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("response".to_string())), - Instruction::FnLookup, - Instruction::FnObjectFromJson, + Instruction::Lookup, + Instruction::ObjectFromJson, Instruction::Push(Elem::String("result".to_string())), - Instruction::FnLookup, - Instruction::FnStringFromJson, + Instruction::Lookup, + Instruction::StringFromJson, Instruction::Push(Elem::String("135499".to_string())), - Instruction::FnCheckLe, - Instruction::FnAssertTrue, + Instruction::CheckEq, + Instruction::AssertTrue, - Instruction::FnRestack(Restack::drop()), + // x["prompts"] + Instruction::Restack(Restack::drop()), Instruction::Push(Elem::String("prompts".to_string())), - Instruction::FnLookup, - Instruction::FnArrayFromJson, + Instruction::Lookup, + Instruction::ArrayFromJson, + // x[0] Instruction::Push(Elem::Number(From::from(0u8))), - Instruction::FnIndex, - Instruction::FnObjectFromJson, + Instruction::Index, + Instruction::ObjectFromJson, - Instruction::FnRestack(Restack::dup()), + // x["action"] = "siwe" + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("action".to_string())), - Instruction::FnLookup, - Instruction::FnStringFromJson, + Instruction::Lookup, + Instruction::StringFromJson, Instruction::Push(Elem::String("siwe".to_string())), - Instruction::FnCheckLe, - Instruction::FnAssertTrue, + Instruction::CheckEq, + Instruction::AssertTrue, - Instruction::FnRestack(Restack::dup()), + // x["version"] = "1.1.0" + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("version".to_string())), - Instruction::FnLookup, - Instruction::FnStringFromJson, + Instruction::Lookup, + Instruction::StringFromJson, Instruction::Push(Elem::String("1.1.0".to_string())), - Instruction::FnCheckLe, - Instruction::FnAssertTrue, + Instruction::CheckEq, + Instruction::AssertTrue, - // Instruction::FnRestack(Restack::dup()), + // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" + // Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("data".to_string())), - Instruction::FnLookup, - Instruction::FnObjectFromJson, + Instruction::Lookup, + Instruction::ObjectFromJson, Instruction::Push(Elem::String("fields".to_string())), - Instruction::FnLookup, - Instruction::FnObjectFromJson, + Instruction::Lookup, + Instruction::ObjectFromJson, Instruction::Push(Elem::String("address".to_string())), - Instruction::FnLookup, - Instruction::FnStringFromJson, - - Instruction::FnRestack(Restack::drop()), + Instruction::Lookup, + Instruction::StringFromJson, Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), - Instruction::FnRestack(Restack::dup()), - Instruction::FnCheckEq, - Instruction::FnAssertTrue, + Instruction::CheckEq, + Instruction::AssertTrue, ]; diff --git a/src/parse.rs b/src/parse.rs index 4035b06..1bf75e3 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -37,9 +37,9 @@ fn parse_instruction(term: &str) -> Result { return Ok(Instruction::Push(rest.trim().parse()?)); } match term { - "assert_true" => Ok(Instruction::FnAssertTrue), - "check_equal" => Ok(Instruction::FnCheckEq), - "hash_sha256" => Ok(Instruction::FnHashSha256), + "assert_true" => Ok(Instruction::AssertTrue), + "check_equal" => Ok(Instruction::CheckEq), + "hash_sha256" => Ok(Instruction::HashSha256), _ => Err(ParseError::UnsupportedInstruction(term.to_string())), } } diff --git a/src/types.rs b/src/types.rs index d7388a7..3777df7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -473,21 +473,21 @@ mod elem_symbol_tests { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub enum Instruction { Push(Elem), - FnRestack(Restack), - FnHashSha256, - FnCheckLe, - FnCheckLt, - FnCheckEq, - FnConcat, - FnSlice, - FnIndex, // Array - FnLookup, // Map - FnAssertTrue, - FnToJson, - FnFromJson, - FnObjectFromJson, - FnArrayFromJson, - FnStringFromJson, + Restack(Restack), + HashSha256, + CheckLe, + CheckLt, + CheckEq, + Concat, + Slice, + Index, // Array + Lookup, // Map + AssertTrue, + ToJson, + FromJson, + ObjectFromJson, + ArrayFromJson, + StringFromJson, } pub type Instructions = Vec; From 32aab543d5b8cc16bb67e8c3646c044f320cb40d Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 14 Feb 2022 10:36:57 -0500 Subject: [PATCH 17/77] add string_to_bytes primitive --- src/executor.rs | 7 +++++++ src/main.rs | 15 ++++++++++++++- src/types.rs | 11 +++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/executor.rs b/src/executor.rs index f0ad8e6..5bfe6e4 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -32,6 +32,7 @@ impl Executor { Instruction::ObjectFromJson => self.object_from_json()?, Instruction::ArrayFromJson => self.array_from_json()?, Instruction::StringFromJson => self.string_from_json()?, + Instruction::StringToBytes => self.string_to_bytes()?, } self.debug()?; } @@ -156,6 +157,12 @@ impl Executor { Ok(()) } + fn string_to_bytes(&mut self) -> Result<(), ExecError> { + let string_to_bytes_input = self.pop()?; + self.push(Elem::string_to_bytes(string_to_bytes_input)?); + Ok(()) + } + // TODO: since pop can fail, require passing debug info to it // (so we know what we were expecting) fn pop(&mut self) -> Result { diff --git a/src/main.rs b/src/main.rs index 85ab6cf..4a94923 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,7 +144,7 @@ fn main() { Instruction::AssertTrue, // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" - // Instruction::Restack(Restack::dup()), + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("data".to_string())), Instruction::Lookup, Instruction::ObjectFromJson, @@ -158,6 +158,19 @@ fn main() { Instruction::CheckEq, Instruction::AssertTrue, + // sha256(x["data"]["message"]) = "[204,12,100,29,52,72,110,177,12,176,128,217,129,34,90,130,30,118,140,1,174,107,100,122,192,144,105,103,119,242,86,249]" + Instruction::Push(Elem::String("data".to_string())), + Instruction::Lookup, + Instruction::ObjectFromJson, + Instruction::Push(Elem::String("message".to_string())), + Instruction::Lookup, + Instruction::StringFromJson, + Instruction::StringToBytes, + Instruction::HashSha256, + Instruction::Push(Elem::Bytes(vec![204,12,100,29,52,72,110,177,12,176,128,217,129,34,90,130,30,118,140,1,174,107,100,122,192,144,105,103,119,242,86,249])), + Instruction::CheckEq, + Instruction::AssertTrue, + ]; let json_instructions = serde_json::to_string_pretty(&serde_json::to_value(instructions.clone()).unwrap()).unwrap(); diff --git a/src/types.rs b/src/types.rs index 3777df7..0f0260d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -282,6 +282,13 @@ impl Elem { } } + pub fn string_to_bytes(self) -> Result { + match self { + Self::String(x) => Ok(Self::Bytes(x.into_bytes())), + other => Err(ElemError::StringToBytesUnsupportedType(&other.simple_type())), + } + } + } #[derive(Debug, PartialEq, Error)] @@ -393,6 +400,9 @@ pub enum ElemError { StringFromJsonUnsupportedType(&'static str), #[error("string_from_json applied unexpected JSON: ({0})")] StringFromJsonUnexpecteJson(Value), + + #[error("string_to_bytes applied to an Elem of an unsupported type ({0})")] + StringToBytesUnsupportedType(&'static str), } impl From for ElemError { @@ -488,6 +498,7 @@ pub enum Instruction { ObjectFromJson, ArrayFromJson, StringFromJson, + StringToBytes, } pub type Instructions = Vec; From 988a056c398e0180fab9453789a642cd3be8ad7e Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Mon, 14 Feb 2022 14:29:51 -0500 Subject: [PATCH 18/77] extend example with concat/hash of two fields --- src/main.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4a94923..4b97f8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -158,7 +158,8 @@ fn main() { Instruction::CheckEq, Instruction::AssertTrue, - // sha256(x["data"]["message"]) = "[204,12,100,29,52,72,110,177,12,176,128,217,129,34,90,130,30,118,140,1,174,107,100,122,192,144,105,103,119,242,86,249]" + // sha256(x["data"]["message"]) + Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("data".to_string())), Instruction::Lookup, Instruction::ObjectFromJson, @@ -167,7 +168,26 @@ fn main() { Instruction::StringFromJson, Instruction::StringToBytes, Instruction::HashSha256, - Instruction::Push(Elem::Bytes(vec![204,12,100,29,52,72,110,177,12,176,128,217,129,34,90,130,30,118,140,1,174,107,100,122,192,144,105,103,119,242,86,249])), + + // sha256(x["data"]["fields"]["address"]) + Instruction::Restack(Restack::swap()), + Instruction::Push(Elem::String("data".to_string())), + Instruction::Lookup, + Instruction::ObjectFromJson, + Instruction::Push(Elem::String("fields".to_string())), + Instruction::Lookup, + Instruction::ObjectFromJson, + Instruction::Push(Elem::String("address".to_string())), + Instruction::Lookup, + Instruction::StringFromJson, + Instruction::StringToBytes, + Instruction::HashSha256, + + // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = + // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] + Instruction::Concat, + Instruction::HashSha256, + Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), Instruction::CheckEq, Instruction::AssertTrue, From ea9c3ea03d3ec45b65712af7b7808b57f6945f78 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 14 Feb 2022 15:55:08 -0500 Subject: [PATCH 19/77] replace *FromJson with UnpackJson, use ElemSymbol to replace simple_type --- src/executor.rs | 24 +--- src/lib.rs | 2 +- src/main.rs | 46 +++---- src/parse.rs | 15 --- src/types.rs | 326 ++++++++++++++++++++++++++---------------------- 5 files changed, 206 insertions(+), 207 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 5bfe6e4..406bcff 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,5 @@ use crate::restack::{Restack, RestackError}; -use crate::types::{Elem, ElemError, Instruction, Instructions}; +use crate::types::{Elem, ElemSymbol, ElemError, Instruction, Instructions}; use thiserror::Error; // TODO: implement n-step executor && errors that tell you what step they're on @@ -29,9 +29,7 @@ impl Executor { Instruction::HashSha256 => self.sha256()?, Instruction::ToJson => self.to_json()?, Instruction::FromJson => self.from_json()?, - Instruction::ObjectFromJson => self.object_from_json()?, - Instruction::ArrayFromJson => self.array_from_json()?, - Instruction::StringFromJson => self.string_from_json()?, + Instruction::UnpackJson(elem_symbol) => self.unpack_json(elem_symbol)?, Instruction::StringToBytes => self.string_to_bytes()?, } self.debug()?; @@ -139,21 +137,9 @@ impl Executor { Ok(()) } - fn object_from_json(&mut self) -> Result<(), ExecError> { - let object_from_json_input = self.pop()?; - self.push(Elem::object_from_json(object_from_json_input)?); - Ok(()) - } - - fn array_from_json(&mut self) -> Result<(), ExecError> { - let array_from_json_input = self.pop()?; - self.push(Elem::array_from_json(array_from_json_input)?); - Ok(()) - } - - fn string_from_json(&mut self) -> Result<(), ExecError> { - let string_from_json_input = self.pop()?; - self.push(Elem::string_from_json(string_from_json_input)?); + fn unpack_json(&mut self, elem_symbol: ElemSymbol) -> Result<(), ExecError> { + let unpack_json_input = self.pop()?; + self.push(Elem::unpack_json(unpack_json_input, elem_symbol)?); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 5ab94b4..26ba92f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ mod restack; pub use restack::Restack; mod types; -pub use types::{Elem, Instruction, Instructions}; +pub use types::{Elem, ElemSymbol, Instruction, Instructions}; mod parse; pub use parse::{parse, parse_json}; mod executor; diff --git a/src/main.rs b/src/main.rs index 4b97f8d..0f3e798 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use cryptoscript::{parse_json, Elem, Executor, Instruction, Instructions, Restack}; +use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; #[cfg(test)] mod tests { @@ -71,24 +71,24 @@ fn main() { // let json_instructions = parse_json()" let instructions: Instructions = vec![ - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Restack(Restack::dup()), // x["queries"] Instruction::Push(Elem::String("queries".to_string())), Instruction::Lookup, - Instruction::ArrayFromJson, + Instruction::UnpackJson(ElemSymbol::Array), // x[0] Instruction::Push(Elem::Number(From::from(0u8))), Instruction::Index, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), // x["action"] = "tokenbalance" Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("action".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::Push(Elem::String("tokenbalance".to_string())), Instruction::CheckEq, Instruction::AssertTrue, @@ -97,7 +97,7 @@ fn main() { Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("contractaddress".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), Instruction::CheckEq, Instruction::AssertTrue, @@ -106,10 +106,10 @@ fn main() { Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("response".to_string())), Instruction::Lookup, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Push(Elem::String("result".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::Push(Elem::String("135499".to_string())), Instruction::CheckEq, Instruction::AssertTrue, @@ -118,18 +118,18 @@ fn main() { Instruction::Restack(Restack::drop()), Instruction::Push(Elem::String("prompts".to_string())), Instruction::Lookup, - Instruction::ArrayFromJson, + Instruction::UnpackJson(ElemSymbol::Array), // x[0] Instruction::Push(Elem::Number(From::from(0u8))), Instruction::Index, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), // x["action"] = "siwe" Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("action".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::Push(Elem::String("siwe".to_string())), Instruction::CheckEq, Instruction::AssertTrue, @@ -138,7 +138,7 @@ fn main() { Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("version".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::Push(Elem::String("1.1.0".to_string())), Instruction::CheckEq, Instruction::AssertTrue, @@ -147,13 +147,13 @@ fn main() { Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("data".to_string())), Instruction::Lookup, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Push(Elem::String("fields".to_string())), Instruction::Lookup, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Push(Elem::String("address".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), Instruction::CheckEq, Instruction::AssertTrue, @@ -162,10 +162,10 @@ fn main() { Instruction::Restack(Restack::dup()), Instruction::Push(Elem::String("data".to_string())), Instruction::Lookup, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Push(Elem::String("message".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::StringToBytes, Instruction::HashSha256, @@ -173,13 +173,13 @@ fn main() { Instruction::Restack(Restack::swap()), Instruction::Push(Elem::String("data".to_string())), Instruction::Lookup, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Push(Elem::String("fields".to_string())), Instruction::Lookup, - Instruction::ObjectFromJson, + Instruction::UnpackJson(ElemSymbol::Object), Instruction::Push(Elem::String("address".to_string())), Instruction::Lookup, - Instruction::StringFromJson, + Instruction::UnpackJson(ElemSymbol::String), Instruction::StringToBytes, Instruction::HashSha256, @@ -198,9 +198,9 @@ fn main() { let mut exec = Executor::default(); exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); - exec.consume(instructions) - .expect("error processing instructions"); + /* exec.consume(instructions) */ + /* .expect("error processing instructions"); */ - println!("FINAL STACK"); + /* println!("FINAL STACK"); */ println!("{:?}", exec); } diff --git a/src/parse.rs b/src/parse.rs index 1bf75e3..f9d223e 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -44,21 +44,6 @@ fn parse_instruction(term: &str) -> Result { } } -impl Elem { - pub fn simple_type(&self) -> &'static str { - match self { - Self::Unit => "Unit", - Self::Bool(_) => "Bool", - Self::Bytes(_) => "Bytes", - Self::Number(_) => "Number", - Self::String(_) => "String", - Self::Array(_) => "Array", - Self::Object(_) => "Object", - Self::Json(_) => "JSON", - } - } -} - impl FromStr for Elem { type Err = ParseError; fn from_str(s: &str) -> Result { diff --git a/src/types.rs b/src/types.rs index 0f0260d..cf5e1ef 100644 --- a/src/types.rs +++ b/src/types.rs @@ -30,8 +30,6 @@ use serde_json::{Map, Number, Value}; // - mul // - div - - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { Unit, @@ -59,7 +57,110 @@ impl PartialOrd for Elem { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum ElemSymbol { + Unit, + Bool, + Number, + Bytes, + String, + Array, + Object, + Json, +} + +impl From for &'static str { + fn from(x: ElemSymbol) -> Self { + match x { + ElemSymbol::Unit => "Unit", + ElemSymbol::Bool => "Bool", + ElemSymbol::Bytes => "Bytes", + ElemSymbol::Number => "Number", + ElemSymbol::String => "String", + ElemSymbol::Array => "Array", + ElemSymbol::Object => "Object", + ElemSymbol::Json => "JSON", + } + } +} + +impl From<&Elem> for ElemSymbol { + fn from(x: &Elem) -> Self { + match x { + Elem::Unit => Self::Unit, + Elem::Bool(_) => Self::Bool, + Elem::Number(_) => Self::Number, + Elem::Bytes(_) => Self::Bytes, + Elem::String(_) => Self::String, + Elem::Array(_) => Self::Array, + Elem::Object(_) => Self::Object, + Elem::Json(_) => Self::Json, + } + } +} + +#[cfg(test)] +impl ElemSymbol { + pub fn default_elem(&self) -> Elem { + match self { + Self::Unit => Elem::Unit, + Self::Bool => Elem::Bool(Default::default()), + Self::Number => Elem::Number(Default::default()), + Self::Bytes => Elem::Bytes(Default::default()), + Self::String => Elem::String(Default::default()), + Self::Array => Elem::Array(Default::default()), + Self::Object => Elem::Object(Default::default()), + Self::Json => Elem::Json(Default::default()), + } + } +} + +#[cfg(test)] +mod elem_symbol_tests { + use super::*; + + #[test] + fn test_from_default_elem() { + for symbol in [ + ElemSymbol::Unit, + ElemSymbol::Bool, + ElemSymbol::Number, + ElemSymbol::Bytes, + ElemSymbol::String, + ElemSymbol::Array, + ElemSymbol::Object, + ElemSymbol::Json, + ] { + assert_eq!(symbol, From::from(symbol.default_elem())) + } + } + + #[test] + fn test_to_default_elem() { + for default_elem in [ + Elem::Unit, + Elem::Bool(Default::default()), + Elem::Number(Default::default()), + Elem::Bytes(Default::default()), + Elem::String(Default::default()), + Elem::Array(Default::default()), + Elem::Object(Default::default()), + Elem::Json(Default::default()), + ] { + assert_eq!(default_elem, From::from(default_elem).default_elem()) + } + } +} + impl Elem { + pub fn symbol(&self) -> ElemSymbol { + From::from(self) + } + + pub fn symbol_str(&self) -> &'static str { + From::from(self.symbol()) + } + pub fn assert_true(&self) -> Result<(), ElemError> { match self { Self::Bool(x) => if *x { @@ -74,8 +175,8 @@ impl Elem { pub fn check_le(&self, other: Self) -> Result { let result = match self.partial_cmp(&other) .ok_or_else(|| ElemError::CheckLeIncomparableTypes { - lhs: self.simple_type(), - rhs: other.simple_type() })? { + lhs: self.symbol_str(), + rhs: other.symbol_str() })? { cmp::Ordering::Less => true, cmp::Ordering::Equal => true, cmp::Ordering::Greater => false, @@ -86,8 +187,8 @@ impl Elem { pub fn check_lt(&self, other: Self) -> Result { let result = match self.partial_cmp(&other) .ok_or_else(|| ElemError::CheckLtIncomparableTypes { - lhs: self.simple_type(), - rhs: other.simple_type() })? { + lhs: self.symbol_str(), + rhs: other.symbol_str() })? { cmp::Ordering::Less => true, _ => false, }; @@ -97,8 +198,8 @@ impl Elem { pub fn check_eq(self, other: Self) -> Result { let result = match self.partial_cmp(&other) .ok_or_else(|| ElemError::CheckEqIncomparableTypes { - lhs: self.simple_type(), - rhs: other.simple_type() })? { + lhs: self.symbol_str(), + rhs: other.symbol_str() })? { cmp::Ordering::Equal => true, _ => false, }; @@ -120,15 +221,19 @@ impl Elem { (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), (some_x, some_y) => { Err(ElemError::ConcatUnsupportedTypes { - lhs: &some_x.simple_type(), - rhs: &some_y.simple_type() + lhs: some_x.symbol_str(), + rhs: some_y.symbol_str() }) }, } } - - fn slice_generic::Item>>(offset: Number, length: Number, iterable: T) -> Result { + fn slice_generic::Item>>(offset: Number, + length: Number, + iterable: T, + elem_symbol: ElemSymbol) -> + Result { let u_offset = offset.as_u64() .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; @@ -138,14 +243,11 @@ impl Elem { let u_offset_plus_length = u_offset.checked_add(u_length) .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; if iterable.clone().into_iter().count() < u_offset_plus_length { - panic!("slice_generic SliceTooShort unimplemented") - - // TODO: implement proper error - // Err(ElemError::SliceTooShort { - // offset: u_offset, - // length: u_length, - // iterable: &Self::Bytes(vec![]).simple_type(), - // }) + Err(ElemError::SliceTooShort { + offset: u_offset, + length: u_length, + iterable: From::from(elem_symbol), + }) } else { Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) } @@ -154,38 +256,42 @@ impl Elem { pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { match (maybe_offset, maybe_length, maybe_iterable) { (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => - Ok(Self::Bytes(Self::slice_generic(offset, length, iterator)?)), + Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { let iterator_vec = Vec::from(iterator.clone()); - Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec)?) + Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) }, (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => - Ok(Self::Array(Self::slice_generic(offset, length, iterator)?)), + Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => - Ok(Self::Object(Self::slice_generic(offset, length, iterator)?)), + Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { Err(ElemError::SliceUnsupportedTypes { - maybe_not_offset: &maybe_not_offset.simple_type(), - maybe_not_length: &maybe_not_length.simple_type(), - maybe_not_iterable: &maybe_not_iterable.simple_type(), + maybe_not_offset: maybe_not_offset.symbol_str(), + maybe_not_length: maybe_not_length.symbol_str(), + maybe_not_iterable: maybe_not_iterable.symbol_str(), }) } } } - fn index_generic::Item>>(index: Number, iterable: T) -> Result<::Item, ElemError> { + fn index_generic::Item>>(index: Number, + iterable: T, + elem_symbol: ElemSymbol) -> + Result<::Item, ElemError> { let u_index: usize = index.as_u64() .ok_or_else(|| ElemError::IndexNotU64(index.clone())) .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; if iterable.clone().into_iter().count() <= u_index { return Err(ElemError::IndexTooShort { index: u_index, - iterable: &Self::Bytes(vec![]).simple_type(), + iterable: From::from(elem_symbol), }) } else { match iterable.into_iter().skip(u_index).next() { - None => Err(ElemError::IndexTooShort { index: u_index, iterable: &Self::Bytes(vec![]).simple_type() }), + None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), Some(x) => Ok(x), } } @@ -193,21 +299,16 @@ impl Elem { pub fn index(self, maybe_iterable: Self) -> Result { match (self, maybe_iterable) { - // (Self::Number(index), Self::Bytes(iterator)) => - // Ok(Self::Bytes(Self::slice_index(index, length, iterator)?)), - // (Self::Number(index), Self::String(iterator)) => { - // let iterator_vec = Vec::from(iterator.clone()); - // Ok(Self::String(String::from_utf8(Self::slice_generic(index.clone(), length.clone(), iterator_vec)?) - // .map_err(|_| ElemError::SliceInvalidUTF8 { index: index, length: length, iterator: iterator })?)) - // }, + (Self::Number(index), Self::Bytes(iterator)) => + Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), (Self::Number(index), Self::Array(iterator)) => - Ok(Self::Json(Self::index_generic(index, iterator)?)), + Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), (Self::Number(index), Self::Object(iterator)) => - Ok(Self::Json(Self::index_generic(index, iterator)?.1)), + Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), (maybe_not_index, maybe_not_iterable) => { Err(ElemError::IndexUnsupportedTypes { - maybe_not_index: &maybe_not_index.simple_type(), - maybe_not_iterable: &maybe_not_iterable.simple_type(), + maybe_not_index: maybe_not_index.symbol_str(), + maybe_not_iterable: maybe_not_iterable.symbol_str(), }) } } @@ -225,8 +326,8 @@ impl Elem { .map(|x|x.clone())?)) }, (maybe_not_key, maybe_not_map) => Err(ElemError::LookupUnsupportedTypes { - maybe_not_key: &maybe_not_key.simple_type(), - maybe_not_map: &maybe_not_map.simple_type(), + maybe_not_key: maybe_not_key.symbol_str(), + maybe_not_map: maybe_not_map.symbol_str(), }), } } @@ -236,7 +337,7 @@ impl Elem { Self::Bytes(bytes) => { Ok(Self::Bytes(super::sha256(&bytes))) } - elem => Err(ElemError::HashUnsupportedType(elem.simple_type())), + elem => Err(ElemError::HashUnsupportedType(elem.symbol_str())), } } @@ -247,48 +348,35 @@ impl Elem { pub fn from_json(self) -> Result { match self { Self::Json(raw_json) => Ok(serde_json::from_value(raw_json)?), - non_json => Err(ElemError::FromJsonUnsupportedType(&non_json.simple_type())), + non_json => Err(ElemError::FromJsonUnsupportedType(non_json.symbol_str())), } } - // pub fn unit_from_json(self) -> Result { - // match self { - // Self::Json(serde_json::Null) => Ok(Elem::Unit), - // other => Err(ElemError::UnitFromJsonUnsupportedType(&other.simple_type())), - // } - // } - - pub fn object_from_json(self) -> Result { - match self { - Self::Json(serde_json::Value::Object(x)) => Ok(Elem::Object(x)), - Self::Json(x) => Err(ElemError::ObjectFromJsonUnexpecteJson(x)), - other => Err(ElemError::ObjectFromJsonUnsupportedType(&other.simple_type())), - } - } - - pub fn array_from_json(self) -> Result { - match self { - Self::Json(serde_json::Value::Array(x)) => Ok(Elem::Array(x)), - Self::Json(x) => Err(ElemError::ArrayFromJsonUnexpecteJson(x)), - other => Err(ElemError::ArrayFromJsonUnsupportedType(&other.simple_type())), - } - } - - pub fn string_from_json(self) -> Result { - match self { - Self::Json(serde_json::Value::String(x)) => Ok(Elem::String(x)), - Self::Json(x) => Err(ElemError::StringFromJsonUnexpecteJson(x)), - other => Err(ElemError::StringFromJsonUnsupportedType(&other.simple_type())), + pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { + match (self, elem_symbol) { + (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), + (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), + (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), + (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), + (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), + (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), + (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { + json: json, + elem_symbol: From::from(elem_symbol), + }), + (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { + non_json: non_json.symbol_str(), + elem_symbol: From::from(elem_symbol), + }), } } pub fn string_to_bytes(self) -> Result { match self { Self::String(x) => Ok(Self::Bytes(x.into_bytes())), - other => Err(ElemError::StringToBytesUnsupportedType(&other.simple_type())), + other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), } } - } #[derive(Debug, PartialEq, Error)] @@ -401,6 +489,17 @@ pub enum ElemError { #[error("string_from_json applied unexpected JSON: ({0})")] StringFromJsonUnexpecteJson(Value), + #[error("unpack_json applied to a value that's not raw JSON or it didn't match the expected type: {non_json:?}; type: {elem_symbol:?}")] + UnpackJsonUnexpectedType { + non_json: &'static str, + elem_symbol: &'static str, + }, + #[error("unpack_json applied to raw JSON and an unsupported type: {json:?}; type: {elem_symbol:?}")] + UnpackJsonUnsupportedSymbol { + json: serde_json::Value, + elem_symbol: &'static str, + }, + #[error("string_to_bytes applied to an Elem of an unsupported type ({0})")] StringToBytesUnsupportedType(&'static str), } @@ -411,75 +510,6 @@ impl From for ElemError { } } - - - -// TODO: implement simple_type on this instead of Elem -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum ElemSymbol { - UnitSymbol, - BoolSymbol, - NumberSymbol, - BytesSymbol, - StringSymbol, - ArraySymbol, - ObjectSymbol, - JsonSymbol, -} - -impl From for ElemSymbol { - fn from(x: Elem) -> Self { - match x { - Elem::Unit => Self::UnitSymbol, - Elem::Bool(_) => Self::BoolSymbol, - Elem::Number(_) => Self::NumberSymbol, - Elem::Bytes(_) => Self::BytesSymbol, - Elem::String(_) => Self::StringSymbol, - Elem::Array(_) => Self::ArraySymbol, - Elem::Object(_) => Self::ObjectSymbol, - Elem::Json(_) => Self::JsonSymbol, - } - } -} - -#[cfg(test)] -impl ElemSymbol { - // TODO: synchronize with Default trait - pub fn default_elem(self) -> Elem { - match self { - Self::UnitSymbol => Elem::Unit, - Self::BoolSymbol => Elem::Bool(false), - Self::NumberSymbol => Elem::Number(From::from(0u8)), - Self::BytesSymbol => Elem::Bytes(vec![]), - Self::StringSymbol => Elem::String("".to_string()), - Self::ArraySymbol => Elem::Array(vec![]), - Self::ObjectSymbol => Elem::Object(Map::new()), - Self::JsonSymbol => Elem::Json(serde_json::Value::Null), - } - } -} - -#[cfg(test)] -mod elem_symbol_tests { - use super::*; - - #[test] - fn test_from_default_elem() { - for symbol in [ - ElemSymbol::UnitSymbol, - ElemSymbol::BoolSymbol, - ElemSymbol::NumberSymbol, - ElemSymbol::BytesSymbol, - ElemSymbol::StringSymbol, - ElemSymbol::ArraySymbol, - ElemSymbol::ObjectSymbol, - ElemSymbol::JsonSymbol, - ] { - assert_eq!(symbol, From::from(symbol.default_elem())) - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub enum Instruction { Push(Elem), @@ -490,14 +520,12 @@ pub enum Instruction { CheckEq, Concat, Slice, - Index, // Array - Lookup, // Map + Index, + Lookup, AssertTrue, ToJson, FromJson, - ObjectFromJson, - ArrayFromJson, - StringFromJson, + UnpackJson(ElemSymbol), StringToBytes, } From f089faa9e4231c6be1a3f85014ed722ca8af110b Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 14 Feb 2022 16:15:23 -0500 Subject: [PATCH 20/77] use pop_pushN to simplify executor --- src/executor.rs | 146 +++++++++++++----------------------------------- src/parse.rs | 1 - src/types.rs | 4 +- 3 files changed, 42 insertions(+), 109 deletions(-) diff --git a/src/executor.rs b/src/executor.rs index 406bcff..ba163f8 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,9 +1,8 @@ use crate::restack::{Restack, RestackError}; -use crate::types::{Elem, ElemSymbol, ElemError, Instruction, Instructions}; +use crate::types::{Elem, ElemError, Instruction, Instructions}; use thiserror::Error; -// TODO: implement n-step executor && errors that tell you what step they're on -// to allow minimal step-by-step debugging +// TODO: implement n-step executor #[derive(Debug, Default)] pub struct Executor { stack: Vec, @@ -16,21 +15,25 @@ impl Executor { println!("------------------------------------------------------------------------------------------"); println!("#: {:?}", expr); match expr { - Instruction::Push(elem) => self.push(elem), Instruction::Restack(restack) => self.restack(restack)?, - Instruction::AssertTrue => self.assert_true()?, - Instruction::CheckLe => self.check_le()?, - Instruction::CheckLt => self.check_lt()?, - Instruction::CheckEq => self.check_eq()?, - Instruction::Concat => self.concat()?, - Instruction::Slice => self.slice()?, - Instruction::Index => self.index()?, - Instruction::Lookup => self.lookup()?, - Instruction::HashSha256 => self.sha256()?, - Instruction::ToJson => self.to_json()?, - Instruction::FromJson => self.from_json()?, - Instruction::UnpackJson(elem_symbol) => self.unpack_json(elem_symbol)?, - Instruction::StringToBytes => self.string_to_bytes()?, + Instruction::Push(elem) => self.push(elem), + + Instruction::AssertTrue => self.pop()?.assert_true()?, + + Instruction::HashSha256 => self.pop_push(Elem::sha256)?, + Instruction::ToJson => self.pop_push(Elem::to_json)?, + Instruction::FromJson => self.pop_push(Elem::from_json)?, + Instruction::UnpackJson(elem_symbol) => self.pop_push(|x| x.unpack_json(elem_symbol))?, + Instruction::StringToBytes => self.pop_push(Elem::string_to_bytes)?, + + Instruction::CheckLe => self.pop_push2(Elem::check_le)?, + Instruction::CheckLt => self.pop_push2(Elem::check_lt)?, + Instruction::CheckEq => self.pop_push2(Elem::check_eq)?, + Instruction::Concat => self.pop_push2(Elem::concat)?, + Instruction::Index => self.pop_push2(Elem::index)?, + Instruction::Lookup => self.pop_push2(Elem::lookup)?, + + Instruction::Slice => self.pop_push3(Elem::slice)?, } self.debug()?; } @@ -46,12 +49,6 @@ impl Executor { Ok(()) } - pub fn push(&mut self, elem: Elem) { - let mut memo = vec![elem]; - memo.append(&mut self.stack.clone()); - self.stack = memo; - } - fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { match restack.run(&mut self.stack) { Err(e) => Err(ExecError::RestackExecError(e)), @@ -61,103 +58,41 @@ impl Executor { } } - fn assert_true(&mut self) -> Result<(), ExecError> { - let x = self.pop()?; - x.assert_true()?; - Ok(()) - } - - fn check_le(&mut self) -> Result<(), ExecError> { - let one = self.pop()?; - let other = self.pop()?; - self.push(one.check_le(other)?); - Ok(()) + // TODO: since pop can fail, require passing debug info to it + // (so we know what we were expecting) + fn pop(&mut self) -> Result { + // self.stack.pop().ok_or_else(|| ExecError::EmptyStack) + let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; + self.stack = self.stack.drain(1..).collect(); + Ok(result.clone()) } - fn check_lt(&mut self) -> Result<(), ExecError> { - let one = self.pop()?; - let other = self.pop()?; - self.push(one.check_lt(other)?); - Ok(()) + pub fn push(&mut self, elem: Elem) { + let mut memo = vec![elem]; + memo.append(&mut self.stack.clone()); + self.stack = memo; } - fn check_eq(&mut self) -> Result<(), ExecError> { + pub fn pop_push(&mut self, f: impl Fn(Elem) -> Result) -> Result<(), ExecError> { let one = self.pop()?; - let other = self.pop()?; - self.push(one.check_eq(other)?); + self.push(f(one)?); Ok(()) } - fn concat(&mut self) -> Result<(), ExecError> { + pub fn pop_push2(&mut self, f: impl Fn(Elem, Elem) -> Result) -> Result<(), ExecError> { let one = self.pop()?; let other = self.pop()?; - self.push(one.concat(other)?); + self.push(f(one, other)?); Ok(()) } - fn slice(&mut self) -> Result<(), ExecError> { - let maybe_offset = self.pop()?; - let maybe_length = self.pop()?; - let maybe_iterable = self.pop()?; - self.push(Elem::slice(maybe_offset, maybe_length, maybe_iterable)?); + pub fn pop_push3(&mut self, f: impl Fn(Elem, Elem, Elem) -> Result) -> Result<(), ExecError> { + let first = self.pop()?; + let second = self.pop()?; + let third = self.pop()?; + self.push(f(first, second, third)?); Ok(()) } - - // you can index any iterable - fn index(&mut self) -> Result<(), ExecError> { - let maybe_index = self.pop()?; - let maybe_iterable = self.pop()?; - self.push(Elem::index(maybe_index, maybe_iterable)?); - Ok(()) - } - - // you can lookup a key in a Map (or fail, no recovery) - fn lookup(&mut self) -> Result<(), ExecError> { - let maybe_key = self.pop()?; - let maybe_map = self.pop()?; - self.push(Elem::lookup(maybe_key, maybe_map)?); - Ok(()) - } - - fn sha256(&mut self) -> Result<(), ExecError> { - let hash_input = self.pop()?; - self.push(Elem::sha256(hash_input)?); - Ok(()) - } - - fn to_json(&mut self) -> Result<(), ExecError> { - let to_json_input = self.pop()?; - self.push(Elem::to_json(to_json_input)?); - Ok(()) - } - - fn from_json(&mut self) -> Result<(), ExecError> { - let from_json_input = self.pop()?; - self.push(Elem::from_json(from_json_input)?); - Ok(()) - } - - fn unpack_json(&mut self, elem_symbol: ElemSymbol) -> Result<(), ExecError> { - let unpack_json_input = self.pop()?; - self.push(Elem::unpack_json(unpack_json_input, elem_symbol)?); - Ok(()) - } - - fn string_to_bytes(&mut self) -> Result<(), ExecError> { - let string_to_bytes_input = self.pop()?; - self.push(Elem::string_to_bytes(string_to_bytes_input)?); - Ok(()) - } - - // TODO: since pop can fail, require passing debug info to it - // (so we know what we were expecting) - fn pop(&mut self) -> Result { - // self.stack.pop().ok_or_else(|| ExecError::EmptyStack) - let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; - self.stack = self.stack.drain(1..).collect(); - Ok(result.clone()) - } - } #[derive(Debug, Error)] @@ -181,4 +116,3 @@ impl From for ExecError { ExecError::ElemError(ElemError::ToFromJsonFailed(format!("{}", error))) } } - diff --git a/src/parse.rs b/src/parse.rs index f9d223e..5cecfe6 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -15,7 +15,6 @@ use std::str::FromStr; use thiserror::Error; - pub fn parse_json(input: &str) -> Result { match serde_json::from_str(&input) { Err(serde_error) => Err(ParseError::SerdeJsonError(serde_error)), diff --git a/src/types.rs b/src/types.rs index cf5e1ef..3a8972e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -172,7 +172,7 @@ impl Elem { } } - pub fn check_le(&self, other: Self) -> Result { + pub fn check_le(self, other: Self) -> Result { let result = match self.partial_cmp(&other) .ok_or_else(|| ElemError::CheckLeIncomparableTypes { lhs: self.symbol_str(), @@ -184,7 +184,7 @@ impl Elem { Ok(Self::Bool(result)) } - pub fn check_lt(&self, other: Self) -> Result { + pub fn check_lt(self, other: Self) -> Result { let result = match self.partial_cmp(&other) .ok_or_else(|| ElemError::CheckLtIncomparableTypes { lhs: self.symbol_str(), From 83ab03d6365841a47e680c39e19bd29fe3da1b8f Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 16 Feb 2022 12:21:12 -0500 Subject: [PATCH 21/77] WIP preparing to type instructions --- Cargo.toml | 2 + src/executor.rs | 17 +-- src/restack.rs | 5 + src/types.rs | 383 +++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 358 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 463b459..64a8877 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,11 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +enumset = { version = "1.0.8", features = ["serde"] } generic-array = "0.14" hex = "0.4" hex-literal = "0.3" +k256 = { version = "0.10.2", features = ["std", "ecdsa", "serde"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha2 = "0.9" diff --git a/src/executor.rs b/src/executor.rs index ba163f8..f19e69e 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -2,7 +2,6 @@ use crate::restack::{Restack, RestackError}; use crate::types::{Elem, ElemError, Instruction, Instructions}; use thiserror::Error; -// TODO: implement n-step executor #[derive(Debug, Default)] pub struct Executor { stack: Vec, @@ -22,7 +21,6 @@ impl Executor { Instruction::HashSha256 => self.pop_push(Elem::sha256)?, Instruction::ToJson => self.pop_push(Elem::to_json)?, - Instruction::FromJson => self.pop_push(Elem::from_json)?, Instruction::UnpackJson(elem_symbol) => self.pop_push(|x| x.unpack_json(elem_symbol))?, Instruction::StringToBytes => self.pop_push(Elem::string_to_bytes)?, @@ -50,18 +48,13 @@ impl Executor { } fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { - match restack.run(&mut self.stack) { - Err(e) => Err(ExecError::RestackExecError(e)), - Ok(new_stack) => { - self.stack = new_stack; - Ok(()) }, - } + self.stack = restack.run(&mut self.stack)?; + Ok(()) } // TODO: since pop can fail, require passing debug info to it // (so we know what we were expecting) fn pop(&mut self) -> Result { - // self.stack.pop().ok_or_else(|| ExecError::EmptyStack) let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; self.stack = self.stack.drain(1..).collect(); Ok(result.clone()) @@ -116,3 +109,9 @@ impl From for ExecError { ExecError::ElemError(ElemError::ToFromJsonFailed(format!("{}", error))) } } + +impl From for ExecError { + fn from (error: RestackError) -> Self { + ExecError::RestackExecError(error) + } +} diff --git a/src/restack.rs b/src/restack.rs index 3024ae5..ac95c84 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -32,6 +32,11 @@ pub struct Restack { } impl Restack { + // (consumed_input_stack_size, produced_output_stack_size) + pub fn stack_io_counts(&self) -> (usize, usize) { + (self.restack_depth, self.restack_vec.len()) + } + // identity pub fn id() -> Self { Restack { diff --git a/src/types.rs b/src/types.rs index 3a8972e..4e1a3d3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,33 +2,38 @@ use crate::restack::Restack; use thiserror::Error; +// use core::num; use std::cmp; use std::convert::TryFrom; +// use std::iter::Chain; +// use std::iter::Repeat; +// use std::iter::Zip; +use std::ops::Range; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; -// TODO: for demo: -// - step through / display steps better -// - support signatures: -// + check_sig -// + to_pub_key +use enumset::{EnumSet, EnumSetType}; -// TODO: property based tests +// - readability: + +// + stack size typing +// + stack typing +// + value annotations +// + JSON traversal path + +// TODO - primitives + numeric: to_int, add, sub, mul, div +// TODO - primitives + bool: neg, and, or +// TODO - primitives + signatures: to_pub_key, check_sig -// TODO: Bool/Number primitives +// - debugging -// Bool(bool), -// - neg -// - and -// - or +// TODO: step through / display steps better + +// - testing + +// TODO: property based tests -// Number(Number), -->> later -// - to_int -// - add -// - sub -// - mul -// - div #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { @@ -57,7 +62,8 @@ impl PartialOrd for Elem { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// EnumSetType implies: Copy, PartialEq, Eq +#[derive(EnumSetType, Debug, PartialOrd, Ord, Serialize, Deserialize)] pub enum ElemSymbol { Unit, Bool, @@ -69,6 +75,7 @@ pub enum ElemSymbol { Json, } + impl From for &'static str { fn from(x: ElemSymbol) -> Self { match x { @@ -99,8 +106,8 @@ impl From<&Elem> for ElemSymbol { } } -#[cfg(test)] impl ElemSymbol { + #[cfg(test)] pub fn default_elem(&self) -> Elem { match self { Self::Unit => Elem::Unit, @@ -113,6 +120,12 @@ impl ElemSymbol { Self::Json => Elem::Json(Default::default()), } } + + pub fn ty(&self) -> Ty { + Ty { + ty_set: EnumSet::only(self.clone()), + } + } } #[cfg(test)] @@ -121,16 +134,7 @@ mod elem_symbol_tests { #[test] fn test_from_default_elem() { - for symbol in [ - ElemSymbol::Unit, - ElemSymbol::Bool, - ElemSymbol::Number, - ElemSymbol::Bytes, - ElemSymbol::String, - ElemSymbol::Array, - ElemSymbol::Object, - ElemSymbol::Json, - ] { + for symbol in EnumSet::all().iter() { assert_eq!(symbol, From::from(symbol.default_elem())) } } @@ -161,6 +165,10 @@ impl Elem { From::from(self.symbol()) } + // pub fn push_ty_sets(&self) -> (Vec, Vec) { + // (vec![], vec![self.symbol().ty()]) + // } + pub fn assert_true(&self) -> Result<(), ElemError> { match self { Self::Bool(x) => if *x { @@ -299,8 +307,8 @@ impl Elem { pub fn index(self, maybe_iterable: Self) -> Result { match (self, maybe_iterable) { - (Self::Number(index), Self::Bytes(iterator)) => - Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), + // (Self::Number(index), Self::Bytes(iterator)) => + // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), (Self::Number(index), Self::Array(iterator)) => Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), (Self::Number(index), Self::Object(iterator)) => @@ -345,13 +353,6 @@ impl Elem { Ok(Self::Json(serde_json::to_value(self)?)) } - pub fn from_json(self) -> Result { - match self { - Self::Json(raw_json) => Ok(serde_json::from_value(raw_json)?), - non_json => Err(ElemError::FromJsonUnsupportedType(non_json.symbol_str())), - } - } - pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { match (self, elem_symbol) { (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), @@ -524,12 +525,314 @@ pub enum Instruction { Lookup, AssertTrue, ToJson, - FromJson, UnpackJson(ElemSymbol), StringToBytes, } -pub type Instructions = Vec; +impl Instruction { + // forall (instr : Instructions) (input_stack: Executor), + // if input_stack.len() < instr.stack_io_counts().0 { + // stack_too_small error + // } else { + // output_stack.len() = input_stack.len() - instr.stack_io_counts().0 + instr.stack_io_counts().1 + // } + + // (consumed_input_stack_size, produced_output_stack_size) + pub fn stack_io_counts(&self) -> (usize, usize) { + match self { + Instruction::Push(_) => (0, 1), + Instruction::Restack(restack) => restack.stack_io_counts(), + Instruction::HashSha256 => (1, 1), + Instruction::CheckLe => (2, 1), + Instruction::CheckLt => (2, 1), + Instruction::CheckEq => (2, 1), + Instruction::Concat => (2, 1), + Instruction::Slice => (3, 1), + Instruction::Index => (2, 1), + Instruction::Lookup => (2, 1), + Instruction::AssertTrue => (1, 0), + Instruction::ToJson => (1, 1), + Instruction::UnpackJson(_) => (1, 1), + Instruction::StringToBytes => (1, 1), + } + } + // pub fn ty_sets(&self) -> (Vec, Vec) { + // match self { + // Instruction::Push(elem) => elem.push_ty_sets(), + // // Restack(restack) => restack.ty_sets(), + // Instruction::HashSha256 => (vec![ElemSymbol::Bytes.ty()], vec![ElemSymbol::Bytes.ty()]), + // // CheckLe, + // // CheckLt, + // Instruction::CheckEq => (vec![Ty::any(), Ty::any()], vec![ElemSymbol::Bool.ty()]), + // // Concat, + // // Slice, + // // Index, + // // Lookup, + // // AssertTrue, + // // ToJson, + // // UnpackJson(ElemSymbol), + // // StringToBytes, + // _ => panic!("infer_instruction: unimplemented"), + // } + // } +} + +pub type Instructions = Vec; // pub type Stack = Vec; +// impl Instructions { +// pub fn stack_io_counts(&self, input_stack_size: usize) -> (usize, usize) { +// self.iter().fold((input_stack_size, input_stack_size), |memo, x| { +// let (memo_input, memo_output) = memo; +// let (next_input, next_output) = x.stack_io_counts; + +// let mut this_input = memo_input; +// let mut this_output = memo_output; + +// while this_input < next_input { +// this_input += 1; +// this_output += 1; +// } +// (this_input, this_output + next_output) +// }) +// } +// } + +// Typing Overview: +// - calculate the number of in/out stack elements per instruction +// + most consume 0..2 and produce one input +// + exceptions are restack and assert_true +// - trace the stack type variables through the execution +// + [ instruction ] -> [ (instruction, [stack_variable]) ], num_stack_variables +// + map from type_var -> [ (instruction_location, (instruction), stack_location) ] +// * instruction may/may-not be needed here +// * stack_location differentiates between e.g. index number and iterable +// + convert to a list of constraints +// + resolve the list of constraints to a single type + + + + + + + + + + + + + + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct SrcRange { + range: Range, +} + +impl SrcRange { + pub fn singleton(src_location: usize) -> Self { + SrcRange { + range: (src_location..src_location + 1), + } + } + + pub fn append(&self, other: Self) -> Result { + if self.range.end + 1 == other.range.start { + Ok(SrcRange { range: self.range.start..other.range.end }) + } else { + Err(SrcRangeError::MismatchedRanges { + lhs: self.clone(), + rhs: other, + }) + } + } +} + +#[derive(Debug, PartialEq, Error)] +pub enum SrcRangeError { + #[error("SrcRange::append applied to non-contiguous ranges: lhs: {lhs:?}; rhs: {rhs:?}")] + MismatchedRanges { + lhs: SrcRange, + rhs: SrcRange, + }, +} + + + + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TyUnifyLocation { +// lhs: SrcRange, +// rhs: SrcRange, +// stack_position: usize, +// } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Ty { + ty_set: EnumSet, +} + +// impl Ty { +// pub fn any() -> Self { +// Ty { +// ty_set: EnumSet::all(), +// } +// } + +// pub fn unify(&self, other: Self, location: TyUnifyLocation) -> Result { +// let both = self.ty_set.intersection(other.ty_set); +// if both.is_empty() { +// Err(TypeError::TyUnifyEmpty { +// lhs: self.clone(), +// rhs: other, +// location: location, +// }) +// } else { +// Ok(Ty { +// ty_set: both +// }) +// } +// } +// } + + +// typing: +// - inference +// - checking against inferred or other type (this + inference = bidirecitonal) +// - unification +// - two categories of tests: +// + property tests for typing methods themselves +// + test that a function having a particular type -> it runs w/o type errors on such inputs + + // Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] + // Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] + // HashSha256, // : [ bytes ] -> [ bytes ] + // CheckLe, // : [ x, x ] -> [ bool ] + // CheckLt, // : [ x, x ] -> [ bool ] + // CheckEq, // : [ x, x ] -> [ bool ] + // Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] + // Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] + // Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] + // Lookup, // [ string, object ] -> [ json ] + // AssertTrue, // [ bool ] -> [] + // ToJson, // (t: type) : [ t ] -> [ json ] + // UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] + // StringToBytes, // [ string ] -> [ bytes ] + + + + +// // TODO: use in_ty/out_ty + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct StackTy { +// src_range: SrcRange, +// in_ty: Vec, +// out_ty: Vec, +// } + +// impl StackTy { +// // pub fn zip_extend_with(xs: Vec, ys: Vec, fl: Fn(T) -> T, f: Fn(T, T) + +// pub fn unify_ty_sets(xs: Vec, ys: Vec, xs_src_range: SrcRange, ys_src_range: SrcRange) -> Result, TypeError> { +// let zip_len = cmp::max(xs.len(), ys.len()); +// let xs_extended = xs.iter().map(|z|Some(z)).chain(std::iter::repeat(None).take(zip_len - xs.len())); +// let ys_extended = ys.iter().map(|z|Some(z)).chain(std::iter::repeat(None).take(zip_len - ys.len())); + +// xs_extended.zip(ys_extended).enumerate().map(|ixy| { +// match ixy.1 { +// (None, None) => Err(TypeError::StackTyUnifyNone { +// lhs: xs.clone(), +// rhs: ys.clone(), +// }), +// (Some(x), None) => Ok(*x), +// (None, Some(y)) => Ok(*y), +// (Some(x), Some(y)) => Ok(x.unify(*y, TyUnifyLocation { +// lhs: xs_src_range.clone(), +// rhs: ys_src_range.clone(), +// stack_position: ixy.0, +// })?), +// } + +// }).collect() +// } + +// pub fn diff_ty_sets(xs: Vec, ys: Vec) -> Result, TypeError> { +// xs.iter().zip(ys.iter()).map(|x, y| { +// x.difference(y) +// }.collect() +// } + +// pub fn union_ty_sets(xs: Vec, ys: Vec) -> Result, TypeError> { +// // pad lengths and zip (pad with empty) +// xs.iter().zip(ys.iter()).map(|x, y| { +// x.union(y) +// }.collect() +// } + +// pub fn unify(&self, other: Self) -> Result { +// let middle_ty = Self::unify_ty_sets(self.out_ty, other.in_ty, self.src_range, other.src_range); +// let self_remainder = Self::diff_ty_sets(middle_ty, self.out_ty); +// let other_remainder = Self::diff_ty_sets(middle_ty, other.in_ty); +// in_ty: self.in_ty + self_remainder +// out_ty: other.out_ty + other_remainder + +// StackTy { +// src_range: self.src_range.append(other.src_range)?, +// in_ty: Self::union_ty_sets(self.in_ty, self_remainder), +// out_ty: Self::union_ty_sets(other.out_ty, other_remainder), +// } +// } + +// pub fn infer_instruction(instruction: &Instruction, src_location: usize) -> Self { +// let instruction_ty_sets = instruction.ty_sets(); +// StackTy { +// src_range: SrcRange::singleton(src_location), +// in_ty: instruction_ty_sets.0, +// out_ty: instruction_ty_sets.1, +// } +// } + +// // pub fn infer(instructions: Instructions) -> Result { +// // instructions.iter().enumerate() +// // .map(|ix| Self::infer_instruction(ix.1, ix.0)) +// // .reduce(|memo, x| memo.unify(x)) + +// // } + + +// } + + + +// #[derive(Debug, PartialEq, Error)] +// pub enum TypeError { +// #[error("Ty::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] +// TyUnifyEmpty { +// lhs: Ty, +// rhs: Ty, +// location: TyUnifyLocation, +// }, + +// // should be impossible +// #[error("StackTy::unify produced an attempt to unify None and None: lhs: {lhs:?}; rhs: {rhs:?}")] +// StackTyUnifyNone { +// lhs: Vec, +// rhs: Vec, +// }, + +// #[error("attempt to unify types of non-contiguous locations: lhs: {0:?}")] +// SrcRangeError(SrcRangeError), +// } + +// impl From for TypeError { +// fn from(error: SrcRangeError) -> Self { +// Self::SrcRangeError(error) +// } +// } + + + + + + From 8e88d03fd88491d2291ea89867fefa81b919e5c7 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 16 Feb 2022 15:27:35 -0500 Subject: [PATCH 22/77] implement arbitrary for ElemSymbol, Number --- Cargo.toml | 2 ++ src/types.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 64a8877..cf85739 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha2 = "0.9" sha3 = "0.9" thiserror = "1.0" +quickcheck = "1.0.3" +quickcheck_macros = "1.0.0" diff --git a/src/types.rs b/src/types.rs index 4e1a3d3..213bd13 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,6 +8,7 @@ use std::convert::TryFrom; // use std::iter::Chain; // use std::iter::Repeat; // use std::iter::Zip; +// use std::iter::empty; use std::ops::Range; use serde::{Deserialize, Serialize}; @@ -15,6 +16,8 @@ use serde_json::{Map, Number, Value}; use enumset::{EnumSet, EnumSetType}; +use quickcheck::{empty_shrinker, Arbitrary, Gen}; + // - readability: // + stack size typing @@ -54,6 +57,8 @@ impl PartialOrd for Elem { (Self::Bool(x), Self::Bool(y)) => x.partial_cmp(y), (Self::Bytes(x), Self::Bytes(y)) => x.partial_cmp(y), (Self::Number(x), Self::Number(y)) => format!("{}", x).partial_cmp(&format!("{}", y)), + // TODO: use x.to_string().partial_cmp(&y.to_string()), + (Self::String(x), Self::String(y)) => x.partial_cmp(y), (Self::Array(x), Self::Array(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None }, (Self::Object(x), Self::Object(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None } @@ -62,6 +67,7 @@ impl PartialOrd for Elem { } } + // EnumSetType implies: Copy, PartialEq, Eq #[derive(EnumSetType, Debug, PartialOrd, Ord, Serialize, Deserialize)] pub enum ElemSymbol { @@ -75,6 +81,92 @@ pub enum ElemSymbol { Json, } +impl Arbitrary for ElemSymbol { + fn arbitrary(g: &mut Gen) -> Self { + let choices: Vec = EnumSet::all().iter().collect(); + *g.choose(&choices).unwrap_or_else(|| &Self::Unit) + } + + fn shrink(&self) -> Box> { + let self_copy = self.clone(); + Box::new(EnumSet::all().iter().filter(move |&x| x < self_copy)) + } +} + + + + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ArbitraryNumber { + number: Number, +} + +impl Arbitrary for ArbitraryNumber { + fn arbitrary(g: &mut Gen) -> Self { + if Arbitrary::arbitrary(g) { + if Arbitrary::arbitrary(g) { + let x: u64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + From::from(x) + } + } else { + let x: i64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + From::from(x) + } + } + } else { + let x: f64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + Number::from_f64(x).unwrap_or(From::from(0u8)) + } + } + } + + fn shrink(&self) -> Box> { + match self.number.as_f64() { + None => match self.number.as_u64() { + None => match self.number.as_i64() { + None => empty_shrinker(), + Some(self_i64) => Box::new( + self_i64.shrink() + .map(|x| ArbitraryNumber { + number: From::from(x), + })), + }, + Some(self_u64) => Box::new( + self_u64.shrink() + .map(|x| ArbitraryNumber { + number: From::from(x), + })), + }, + Some(self_f64) => Box::new( + self_f64.shrink() + .map(|x| ArbitraryNumber { + number: Number::from_f64(x).unwrap_or(From::from(0u8)), + })), + } + } +} + +impl ElemSymbol { + pub fn arbitrary_contents(&self, g: &mut Gen) -> Elem { + match self { + Self::Unit => Elem::Unit, + Self::Bool => Elem::Bool(Arbitrary::arbitrary(g)), + Self::Number => { + let x: ArbitraryNumber = Arbitrary::arbitrary(g); + Elem::Number(x.number) + }, + Self::Bytes => Elem::Bytes(Arbitrary::arbitrary(g)), + Self::String => Elem::String(Arbitrary::arbitrary(g)), + Self::Array => Elem::Array(Arbitrary::arbitrary(g)), + Self::Object => Elem::Object(Arbitrary::arbitrary(g)), + Self::Json => Elem::Json(Arbitrary::arbitrary(g)), + } + } +} + impl From for &'static str { fn from(x: ElemSymbol) -> Self { From 26638dde5f70c75a07c387fe6c410e7c3a66e1d1 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 17 Feb 2022 15:55:24 -0500 Subject: [PATCH 23/77] add arbitrary instances for Value, Map, Number, ElemSymbol --- src/types.rs | 180 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 121 insertions(+), 59 deletions(-) diff --git a/src/types.rs b/src/types.rs index 213bd13..6250bd9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -38,6 +38,114 @@ use quickcheck::{empty_shrinker, Arbitrary, Gen}; // TODO: property based tests +// TODO: migrate to own file +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ArbitraryNumber { + number: Number, +} + +// TODO: migrate to own file +impl Arbitrary for ArbitraryNumber { + fn arbitrary(g: &mut Gen) -> Self { + if Arbitrary::arbitrary(g) { + if Arbitrary::arbitrary(g) { + let x: u64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + From::from(x) + } + } else { + let x: i64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + From::from(x) + } + } + } else { + let x: f64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + Number::from_f64(x).unwrap_or(From::from(0u8)) + } + } + } + + fn shrink(&self) -> Box> { + match self.number.as_f64() { + None => match self.number.as_u64() { + None => match self.number.as_i64() { + None => empty_shrinker(), + Some(self_i64) => Box::new( + self_i64.shrink() + .map(|x| ArbitraryNumber { + number: From::from(x), + })), + }, + Some(self_u64) => Box::new( + self_u64.shrink() + .map(|x| ArbitraryNumber { + number: From::from(x), + })), + }, + Some(self_f64) => Box::new( + self_f64.shrink() + .map(|x| ArbitraryNumber { + number: Number::from_f64(x).unwrap_or(From::from(0u8)), + })), + } + } +} + + +// TODO: migrate to own file +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ArbitraryMap { + map: Vec<(String, Value)>, +} + +impl From for Map { + fn from(x: ArbitraryMap) -> Self { + x.map.into_iter().collect() + } +} + + +// TODO: migrate to own file +impl Arbitrary for ArbitraryMap { + fn arbitrary(g: &mut Gen) -> Self { + let map_vec: Vec<(String, ArbitraryValue)> = Arbitrary::arbitrary(g); + ArbitraryMap { + map: map_vec.into_iter().map(|x| (x.0, x.1.value)).collect(), + } + } + + fn shrink(&self) -> Box> { + empty_shrinker() + } +} + +// TODO: migrate to own file +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ArbitraryValue { + value: Value, +} + +// TODO: migrate to own file +impl Arbitrary for ArbitraryValue { + fn arbitrary(g: &mut Gen) -> Self { + ArbitraryValue { + value: Value::Null, + } + } + + fn shrink(&self) -> Box> { + empty_shrinker() + } +} + + + + + + + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { Unit, @@ -56,9 +164,7 @@ impl PartialOrd for Elem { (Self::Unit, Self::Unit) => Some(cmp::Ordering::Equal), (Self::Bool(x), Self::Bool(y)) => x.partial_cmp(y), (Self::Bytes(x), Self::Bytes(y)) => x.partial_cmp(y), - (Self::Number(x), Self::Number(y)) => format!("{}", x).partial_cmp(&format!("{}", y)), - // TODO: use x.to_string().partial_cmp(&y.to_string()), - + (Self::Number(x), Self::Number(y)) => x.to_string().partial_cmp(&y.to_string()), (Self::String(x), Self::String(y)) => x.partial_cmp(y), (Self::Array(x), Self::Array(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None }, (Self::Object(x), Self::Object(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None } @@ -96,59 +202,6 @@ impl Arbitrary for ElemSymbol { -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ArbitraryNumber { - number: Number, -} - -impl Arbitrary for ArbitraryNumber { - fn arbitrary(g: &mut Gen) -> Self { - if Arbitrary::arbitrary(g) { - if Arbitrary::arbitrary(g) { - let x: u64 = Arbitrary::arbitrary(g); - ArbitraryNumber { number: - From::from(x) - } - } else { - let x: i64 = Arbitrary::arbitrary(g); - ArbitraryNumber { number: - From::from(x) - } - } - } else { - let x: f64 = Arbitrary::arbitrary(g); - ArbitraryNumber { number: - Number::from_f64(x).unwrap_or(From::from(0u8)) - } - } - } - - fn shrink(&self) -> Box> { - match self.number.as_f64() { - None => match self.number.as_u64() { - None => match self.number.as_i64() { - None => empty_shrinker(), - Some(self_i64) => Box::new( - self_i64.shrink() - .map(|x| ArbitraryNumber { - number: From::from(x), - })), - }, - Some(self_u64) => Box::new( - self_u64.shrink() - .map(|x| ArbitraryNumber { - number: From::from(x), - })), - }, - Some(self_f64) => Box::new( - self_f64.shrink() - .map(|x| ArbitraryNumber { - number: Number::from_f64(x).unwrap_or(From::from(0u8)), - })), - } - } -} - impl ElemSymbol { pub fn arbitrary_contents(&self, g: &mut Gen) -> Elem { match self { @@ -160,9 +213,18 @@ impl ElemSymbol { }, Self::Bytes => Elem::Bytes(Arbitrary::arbitrary(g)), Self::String => Elem::String(Arbitrary::arbitrary(g)), - Self::Array => Elem::Array(Arbitrary::arbitrary(g)), - Self::Object => Elem::Object(Arbitrary::arbitrary(g)), - Self::Json => Elem::Json(Arbitrary::arbitrary(g)), + Self::Array => { + let xs: Vec = Arbitrary::arbitrary(g); + Elem::Array(xs.into_iter().map(|x| x.value).collect()) + }, + Self::Object => { + let xs: ArbitraryMap = Arbitrary::arbitrary(g); + Elem::Object(From::from(xs)) + }, + Self::Json => { + let xs: ArbitraryValue = Arbitrary::arbitrary(g); + Elem::Json(xs.value) + }, } } } From c7543ed7966d96610494691b36b4568902058420 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 17 Feb 2022 16:25:15 -0500 Subject: [PATCH 24/77] migrate Arbitrary instances and Elem to own files, cleanup --- src/arbitrary.rs | 99 ++++++ src/elem.rs | 539 ++++++++++++++++++++++++++++++++ src/executor.rs | 3 +- src/lib.rs | 6 +- src/parse.rs | 3 +- src/types.rs | 783 +++-------------------------------------------- 6 files changed, 692 insertions(+), 741 deletions(-) create mode 100644 src/arbitrary.rs create mode 100644 src/elem.rs diff --git a/src/arbitrary.rs b/src/arbitrary.rs new file mode 100644 index 0000000..4d301ff --- /dev/null +++ b/src/arbitrary.rs @@ -0,0 +1,99 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; +use quickcheck::{empty_shrinker, Arbitrary, Gen}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ArbitraryNumber { + pub number: Number, +} + +impl Arbitrary for ArbitraryNumber { + fn arbitrary(g: &mut Gen) -> Self { + if Arbitrary::arbitrary(g) { + if Arbitrary::arbitrary(g) { + let x: u64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + From::from(x) + } + } else { + let x: i64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + From::from(x) + } + } + } else { + let x: f64 = Arbitrary::arbitrary(g); + ArbitraryNumber { number: + Number::from_f64(x).unwrap_or(From::from(0u8)) + } + } + } + + fn shrink(&self) -> Box> { + match self.number.as_f64() { + None => match self.number.as_u64() { + None => match self.number.as_i64() { + None => empty_shrinker(), + Some(self_i64) => Box::new( + self_i64.shrink() + .map(|x| ArbitraryNumber { + number: From::from(x), + })), + }, + Some(self_u64) => Box::new( + self_u64.shrink() + .map(|x| ArbitraryNumber { + number: From::from(x), + })), + }, + Some(self_f64) => Box::new( + self_f64.shrink() + .map(|x| ArbitraryNumber { + number: Number::from_f64(x).unwrap_or(From::from(0u8)), + })), + } + } +} + + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ArbitraryMap { + pub map: Vec<(String, Value)>, +} + +impl From for Map { + fn from(x: ArbitraryMap) -> Self { + x.map.into_iter().collect() + } +} + +impl Arbitrary for ArbitraryMap { + fn arbitrary(g: &mut Gen) -> Self { + let map_vec: Vec<(String, ArbitraryValue)> = Arbitrary::arbitrary(g); + ArbitraryMap { + map: map_vec.into_iter().map(|x| (x.0, x.1.value)).collect(), + } + } + + fn shrink(&self) -> Box> { + empty_shrinker() + } +} + + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ArbitraryValue { + pub value: Value, +} + +impl Arbitrary for ArbitraryValue { + fn arbitrary(_g: &mut Gen) -> Self { + ArbitraryValue { + value: Value::Null, + } + } + + fn shrink(&self) -> Box> { + empty_shrinker() + } +} diff --git a/src/elem.rs b/src/elem.rs new file mode 100644 index 0000000..7cea31c --- /dev/null +++ b/src/elem.rs @@ -0,0 +1,539 @@ +use crate::arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; + +use thiserror::Error; + +use std::cmp; +use std::convert::TryFrom; + +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; + +use enumset::{EnumSet, EnumSetType}; +use quickcheck::{empty_shrinker, Arbitrary, Gen}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum Elem { + Unit, + Bool(bool), + Number(Number), + Bytes(Vec), + String(String), + Array(Vec), + Object(Map), + Json(Value), +} + +impl PartialOrd for Elem { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::Unit, Self::Unit) => Some(cmp::Ordering::Equal), + (Self::Bool(x), Self::Bool(y)) => x.partial_cmp(y), + (Self::Bytes(x), Self::Bytes(y)) => x.partial_cmp(y), + (Self::Number(x), Self::Number(y)) => x.to_string().partial_cmp(&y.to_string()), + (Self::String(x), Self::String(y)) => x.partial_cmp(y), + (Self::Array(x), Self::Array(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None }, + (Self::Object(x), Self::Object(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None } + (_, _) => None, + } + } +} + + +// EnumSetType implies: Copy, PartialEq, Eq +#[derive(EnumSetType, Debug, PartialOrd, Ord, Serialize, Deserialize)] +pub enum ElemSymbol { + Unit, + Bool, + Number, + Bytes, + String, + Array, + Object, + Json, +} + +impl Arbitrary for ElemSymbol { + fn arbitrary(g: &mut Gen) -> Self { + let choices: Vec = EnumSet::all().iter().collect(); + *g.choose(&choices).unwrap_or_else(|| &Self::Unit) + } + + fn shrink(&self) -> Box> { + let self_copy = self.clone(); + Box::new(EnumSet::all().iter().filter(move |&x| x < self_copy)) + } +} + +impl ElemSymbol { + pub fn arbitrary_contents(&self, g: &mut Gen) -> Elem { + match self { + Self::Unit => Elem::Unit, + Self::Bool => Elem::Bool(Arbitrary::arbitrary(g)), + Self::Number => { + let x: ArbitraryNumber = Arbitrary::arbitrary(g); + Elem::Number(x.number) + }, + Self::Bytes => Elem::Bytes(Arbitrary::arbitrary(g)), + Self::String => Elem::String(Arbitrary::arbitrary(g)), + Self::Array => { + let xs: Vec = Arbitrary::arbitrary(g); + Elem::Array(xs.into_iter().map(|x| x.value).collect()) + }, + Self::Object => { + let xs: ArbitraryMap = Arbitrary::arbitrary(g); + Elem::Object(From::from(xs)) + }, + Self::Json => { + let xs: ArbitraryValue = Arbitrary::arbitrary(g); + Elem::Json(xs.value) + }, + } + } +} + +impl Arbitrary for Elem { + fn arbitrary(g: &mut Gen) -> Self { + let symbol: ElemSymbol = Arbitrary::arbitrary(g); + symbol.arbitrary_contents(g) + } + + fn shrink(&self) -> Box> { + empty_shrinker() + // let self_copy = self.clone(); + // Box::new(EnumSet::all().iter().filter(move |&x| x < self_copy)) + } +} + + + +impl From for &'static str { + fn from(x: ElemSymbol) -> Self { + match x { + ElemSymbol::Unit => "Unit", + ElemSymbol::Bool => "Bool", + ElemSymbol::Bytes => "Bytes", + ElemSymbol::Number => "Number", + ElemSymbol::String => "String", + ElemSymbol::Array => "Array", + ElemSymbol::Object => "Object", + ElemSymbol::Json => "JSON", + } + } +} + +impl From<&Elem> for ElemSymbol { + fn from(x: &Elem) -> Self { + match x { + Elem::Unit => Self::Unit, + Elem::Bool(_) => Self::Bool, + Elem::Number(_) => Self::Number, + Elem::Bytes(_) => Self::Bytes, + Elem::String(_) => Self::String, + Elem::Array(_) => Self::Array, + Elem::Object(_) => Self::Object, + Elem::Json(_) => Self::Json, + } + } +} + +impl ElemSymbol { + #[cfg(test)] + pub fn default_elem(&self) -> Elem { + match self { + Self::Unit => Elem::Unit, + Self::Bool => Elem::Bool(Default::default()), + Self::Number => Elem::Number(Default::default()), + Self::Bytes => Elem::Bytes(Default::default()), + Self::String => Elem::String(Default::default()), + Self::Array => Elem::Array(Default::default()), + Self::Object => Elem::Object(Default::default()), + Self::Json => Elem::Json(Default::default()), + } + } + + // pub fn ty(&self) -> Ty { + // Ty { + // ty_set: EnumSet::only(self.clone()), + // } + // } +} + +#[cfg(test)] +mod elem_symbol_tests { + use super::*; + + #[test] + fn test_from_default_elem() { + for symbol in EnumSet::all().iter() { + assert_eq!(symbol, From::from(symbol.default_elem())) + } + } + + #[test] + fn test_to_default_elem() { + for default_elem in [ + Elem::Unit, + Elem::Bool(Default::default()), + Elem::Number(Default::default()), + Elem::Bytes(Default::default()), + Elem::String(Default::default()), + Elem::Array(Default::default()), + Elem::Object(Default::default()), + Elem::Json(Default::default()), + ] { + assert_eq!(default_elem, From::from(default_elem).default_elem()) + } + } +} + +impl Elem { + pub fn symbol(&self) -> ElemSymbol { + From::from(self) + } + + pub fn symbol_str(&self) -> &'static str { + From::from(self.symbol()) + } + + pub fn assert_true(&self) -> Result<(), ElemError> { + match self { + Self::Bool(x) => if *x { + Ok(()) + } else { + Err(ElemError::AssertTrueFailed()) + }, + found => Err(ElemError::AssertTrueUnsupportedType(found.clone())), + } + } + + pub fn check_le(self, other: Self) -> Result { + let result = match self.partial_cmp(&other) + .ok_or_else(|| ElemError::CheckLeIncomparableTypes { + lhs: self.symbol_str(), + rhs: other.symbol_str() })? { + cmp::Ordering::Less => true, + cmp::Ordering::Equal => true, + cmp::Ordering::Greater => false, + }; + Ok(Self::Bool(result)) + } + + pub fn check_lt(self, other: Self) -> Result { + let result = match self.partial_cmp(&other) + .ok_or_else(|| ElemError::CheckLtIncomparableTypes { + lhs: self.symbol_str(), + rhs: other.symbol_str() })? { + cmp::Ordering::Less => true, + _ => false, + }; + Ok(Self::Bool(result)) + } + + pub fn check_eq(self, other: Self) -> Result { + let result = match self.partial_cmp(&other) + .ok_or_else(|| ElemError::CheckEqIncomparableTypes { + lhs: self.symbol_str(), + rhs: other.symbol_str() })? { + cmp::Ordering::Equal => true, + _ => false, + }; + Ok(Self::Bool(result)) + } + + fn concat_generic::Item>>(x: T, y: T) -> T { + x.into_iter().chain(y.into_iter()).collect() + } + + pub fn concat(self, other: Self) -> Result { + match (self, other) { + (Self::Bytes(x), Self::Bytes(y)) => Ok(Self::Bytes(Self::concat_generic(x, y))), + (Self::String(x), Self::String(y)) => { + Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) + .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) + }, + (Self::Array(x), Self::Array(y)) => Ok(Self::Array(Self::concat_generic(x, y))), + (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), + (some_x, some_y) => { + Err(ElemError::ConcatUnsupportedTypes { + lhs: some_x.symbol_str(), + rhs: some_y.symbol_str() + }) + }, + } + } + + fn slice_generic::Item>>(offset: Number, + length: Number, + iterable: T, + elem_symbol: ElemSymbol) -> + Result { + let u_offset = offset.as_u64() + .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + let u_length = length.as_u64() + .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + let u_offset_plus_length = u_offset.checked_add(u_length) + .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(ElemError::SliceTooShort { + offset: u_offset, + length: u_length, + iterable: From::from(elem_symbol), + }) + } else { + Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) + } + } + + pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { + match (maybe_offset, maybe_length, maybe_iterable) { + (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => + Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), + (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { + let iterator_vec = Vec::from(iterator.clone()); + Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) + .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) + }, + (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => + Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), + (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => + Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), + (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { + Err(ElemError::SliceUnsupportedTypes { + maybe_not_offset: maybe_not_offset.symbol_str(), + maybe_not_length: maybe_not_length.symbol_str(), + maybe_not_iterable: maybe_not_iterable.symbol_str(), + }) + } + } + } + + fn index_generic::Item>>(index: Number, + iterable: T, + elem_symbol: ElemSymbol) -> + Result<::Item, ElemError> { + let u_index: usize = index.as_u64() + .ok_or_else(|| ElemError::IndexNotU64(index.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; + if iterable.clone().into_iter().count() <= u_index { + return Err(ElemError::IndexTooShort { + index: u_index, + iterable: From::from(elem_symbol), + }) + } else { + match iterable.into_iter().skip(u_index).next() { + None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), + Some(x) => Ok(x), + } + } + } + + pub fn index(self, maybe_iterable: Self) -> Result { + match (self, maybe_iterable) { + // (Self::Number(index), Self::Bytes(iterator)) => + // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), + (Self::Number(index), Self::Array(iterator)) => + Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), + (Self::Number(index), Self::Object(iterator)) => + Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), + (maybe_not_index, maybe_not_iterable) => { + Err(ElemError::IndexUnsupportedTypes { + maybe_not_index: maybe_not_index.symbol_str(), + maybe_not_iterable: maybe_not_iterable.symbol_str(), + }) + } + } + } + + // you can lookup a key in a Map (or fail, no recovery) + pub fn lookup(self, maybe_map: Self) -> Result { + match (self, maybe_map) { + (Self::String(key), Self::Object(map)) => { + Ok(Self::Json(map.get(&key) + .ok_or_else(|| ElemError::LookupKeyMissing { + key: key, + map: map.clone(), + }) + .map(|x|x.clone())?)) + }, + (maybe_not_key, maybe_not_map) => Err(ElemError::LookupUnsupportedTypes { + maybe_not_key: maybe_not_key.symbol_str(), + maybe_not_map: maybe_not_map.symbol_str(), + }), + } + } + + pub fn sha256(self) -> Result { + match self { + Self::Bytes(bytes) => { + Ok(Self::Bytes(super::sha256(&bytes))) + } + elem => Err(ElemError::HashUnsupportedType(elem.symbol_str())), + } + } + + pub fn to_json(self) -> Result { + Ok(Self::Json(serde_json::to_value(self)?)) + } + + pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { + match (self, elem_symbol) { + (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), + (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), + (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), + (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), + (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), + (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), + (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { + json: json, + elem_symbol: From::from(elem_symbol), + }), + (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { + non_json: non_json.symbol_str(), + elem_symbol: From::from(elem_symbol), + }), + } + } + + pub fn string_to_bytes(self) -> Result { + match self { + Self::String(x) => Ok(Self::Bytes(x.into_bytes())), + other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), + } + } +} + +#[derive(Debug, PartialEq, Error)] +pub enum ElemError { + #[error("expected Elem::Bool(true), found {0:?}")] + AssertTrueUnsupportedType(Elem), + #[error("expected true, but found false")] + AssertTrueFailed(), + #[error("check_le: incomparable types: {lhs:?}; {rhs:?}")] + CheckLeIncomparableTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("check_lt: incomparable types: {lhs:?}; {rhs:?}")] + CheckLtIncomparableTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("check_eq: incomparable types: {lhs:?}; {rhs:?}")] + CheckEqIncomparableTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("concat applied to unsupported types: lhs: {lhs:?}; rhs: {rhs:?}")] + ConcatUnsupportedTypes { + lhs: &'static str, + rhs: &'static str, + }, + #[error("concat applied to strings that concatentate to invalid UTF8: lhs: {lhs:?}; rhs: {rhs:?}")] + ConcatInvalidUTF8 { + lhs: String, + rhs: String, + }, + + #[error("slice applied to unsupported types: maybe_not_offset: {maybe_not_offset:?}; maybe_not_length: {maybe_not_length:?}; maybe_not_iterable: {maybe_not_iterable:?}")] + SliceUnsupportedTypes { + maybe_not_offset: &'static str, + maybe_not_length: &'static str, + maybe_not_iterable: &'static str, + }, + #[error("slice applied to an 'offset' that can't be unpacked to u64: offset: {0:?}")] + SliceOffsetNotU64(Number), + #[error("slice applied to a 'length' that can't be unpacked to u64: length: {0:?}")] + SliceLengthNotU64(Number), + #[error("slice applied to an iterable that's too short for the given offset: offset: {offset:?} and length: {length:?}: iterable: {iterable:?}")] + SliceTooShort { + offset: usize, + length: usize, + iterable: &'static str, + }, + #[error("slice applied to offset and length whose sum overflows usize: offset: {offset:?} and length: {length:?}")] + SliceOverflow { + offset: Number, + length: Number, + }, + #[error("slice applied to arguments that produce invalid UTF8: offset: {offset:?}; length: {length:?}, iterator: {iterator:?}")] + SliceInvalidUTF8 { + offset: Number, + length: Number, + iterator: String, + }, + + #[error("index applied to unsupported types: maybe_not_index: {maybe_not_index:?}; maybe_not_iterable: {maybe_not_iterable:?}")] + IndexUnsupportedTypes { + maybe_not_index: &'static str, + maybe_not_iterable: &'static str, + }, + #[error("index applied to an 'index' that can't be unpacked to u64: {0:?}")] + IndexNotU64(Number), + #[error("index applied to an iterable that's too short for the given index: {index:?}; iterable: {iterable:?}")] + IndexTooShort { + index: usize, + iterable: &'static str, + }, + #[error("slice applied to offset and length whose sum overflows usize: {0:?}")] + IndexOverflow(Number), + + #[error("lookup applied to unsupported types: maybe_not_key: {maybe_not_key:?}; maybe_not_map: {maybe_not_map:?}")] + LookupUnsupportedTypes { + maybe_not_key: &'static str, + maybe_not_map: &'static str, + }, + #[error("lookup applied to a map that doesn't contain the given key: {key:?}; map: {map:?}")] + LookupKeyMissing { + key: String, + map: Map, + }, + + #[error("sha256 applied an Elem of an unsupported type ({0})")] + HashUnsupportedType(&'static str), + + #[error("to_json/from_json serialization failed: ({0})")] + ToFromJsonFailed(String), + + #[error("from_json applied an Elem of an unsupported type ({0})")] + FromJsonUnsupportedType(&'static str), + + #[error("object_from_json applied an Elem of an unsupported type ({0})")] + ObjectFromJsonUnsupportedType(&'static str), + #[error("object_from_json applied unexpected JSON: ({0})")] + ObjectFromJsonUnexpecteJson(Value), + + #[error("array_from_json applied an Elem of an unsupported type ({0})")] + ArrayFromJsonUnsupportedType(&'static str), + #[error("array_from_json applied unexpected JSON: ({0})")] + ArrayFromJsonUnexpecteJson(Value), + + #[error("string_from_json applied an Elem of an unsupported type ({0})")] + StringFromJsonUnsupportedType(&'static str), + #[error("string_from_json applied unexpected JSON: ({0})")] + StringFromJsonUnexpecteJson(Value), + + #[error("unpack_json applied to a value that's not raw JSON or it didn't match the expected type: {non_json:?}; type: {elem_symbol:?}")] + UnpackJsonUnexpectedType { + non_json: &'static str, + elem_symbol: &'static str, + }, + #[error("unpack_json applied to raw JSON and an unsupported type: {json:?}; type: {elem_symbol:?}")] + UnpackJsonUnsupportedSymbol { + json: serde_json::Value, + elem_symbol: &'static str, + }, + + #[error("string_to_bytes applied to an Elem of an unsupported type ({0})")] + StringToBytesUnsupportedType(&'static str), +} + +impl From for ElemError { + fn from(error: serde_json::Error) -> Self { + ElemError::ToFromJsonFailed(format!("{}", error)) + } +} + diff --git a/src/executor.rs b/src/executor.rs index f19e69e..732175a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,6 @@ use crate::restack::{Restack, RestackError}; -use crate::types::{Elem, ElemError, Instruction, Instructions}; +use crate::elem::{Elem, ElemError}; +use crate::types::{Instruction, Instructions}; use thiserror::Error; #[derive(Debug, Default)] diff --git a/src/lib.rs b/src/lib.rs index 26ba92f..fd97009 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ mod restack; pub use restack::Restack; +mod arbitrary; +pub use arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; +mod elem; +pub use elem::{Elem, ElemSymbol}; mod types; -pub use types::{Elem, ElemSymbol, Instruction, Instructions}; +pub use types::{Instruction, Instructions}; mod parse; pub use parse::{parse, parse_json}; mod executor; diff --git a/src/parse.rs b/src/parse.rs index 5cecfe6..0bacfdc 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -9,7 +9,8 @@ /// Where CHARS is any number of characters which aren't escaped double-quotes (\") and HEX is a 64 /// digit hexadecimal number. -use crate::types::{Elem, Instruction, Instructions}; +use crate::elem::{Elem}; +use crate::types::{Instruction, Instructions}; use std::str::FromStr; diff --git a/src/types.rs b/src/types.rs index 6250bd9..3d01a76 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,669 +1,53 @@ use crate::restack::Restack; +use crate::elem::{Elem, ElemSymbol}; -use thiserror::Error; - -// use core::num; -use std::cmp; -use std::convert::TryFrom; -// use std::iter::Chain; -// use std::iter::Repeat; -// use std::iter::Zip; -// use std::iter::empty; use std::ops::Range; -use serde::{Deserialize, Serialize}; -use serde_json::{Map, Number, Value}; - use enumset::{EnumSet, EnumSetType}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; -use quickcheck::{empty_shrinker, Arbitrary, Gen}; - -// - readability: - -// + stack size typing -// + stack typing -// + value annotations -// + JSON traversal path - -// TODO - primitives + numeric: to_int, add, sub, mul, div -// TODO - primitives + bool: neg, and, or -// TODO - primitives + signatures: to_pub_key, check_sig - -// - debugging - -// TODO: step through / display steps better - -// - testing - -// TODO: property based tests - - -// TODO: migrate to own file -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ArbitraryNumber { - number: Number, -} - -// TODO: migrate to own file -impl Arbitrary for ArbitraryNumber { - fn arbitrary(g: &mut Gen) -> Self { - if Arbitrary::arbitrary(g) { - if Arbitrary::arbitrary(g) { - let x: u64 = Arbitrary::arbitrary(g); - ArbitraryNumber { number: - From::from(x) - } - } else { - let x: i64 = Arbitrary::arbitrary(g); - ArbitraryNumber { number: - From::from(x) - } - } - } else { - let x: f64 = Arbitrary::arbitrary(g); - ArbitraryNumber { number: - Number::from_f64(x).unwrap_or(From::from(0u8)) - } - } - } - - fn shrink(&self) -> Box> { - match self.number.as_f64() { - None => match self.number.as_u64() { - None => match self.number.as_i64() { - None => empty_shrinker(), - Some(self_i64) => Box::new( - self_i64.shrink() - .map(|x| ArbitraryNumber { - number: From::from(x), - })), - }, - Some(self_u64) => Box::new( - self_u64.shrink() - .map(|x| ArbitraryNumber { - number: From::from(x), - })), - }, - Some(self_f64) => Box::new( - self_f64.shrink() - .map(|x| ArbitraryNumber { - number: Number::from_f64(x).unwrap_or(From::from(0u8)), - })), - } - } -} - - -// TODO: migrate to own file -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ArbitraryMap { - map: Vec<(String, Value)>, -} - -impl From for Map { - fn from(x: ArbitraryMap) -> Self { - x.map.into_iter().collect() - } -} - - -// TODO: migrate to own file -impl Arbitrary for ArbitraryMap { - fn arbitrary(g: &mut Gen) -> Self { - let map_vec: Vec<(String, ArbitraryValue)> = Arbitrary::arbitrary(g); - ArbitraryMap { - map: map_vec.into_iter().map(|x| (x.0, x.1.value)).collect(), - } - } - - fn shrink(&self) -> Box> { - empty_shrinker() - } -} - -// TODO: migrate to own file -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ArbitraryValue { - value: Value, -} - -// TODO: migrate to own file -impl Arbitrary for ArbitraryValue { - fn arbitrary(g: &mut Gen) -> Self { - ArbitraryValue { - value: Value::Null, - } - } - - fn shrink(&self) -> Box> { - empty_shrinker() - } -} - - - - - - - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum Elem { - Unit, - Bool(bool), - Number(Number), - Bytes(Vec), - String(String), - Array(Vec), - Object(Map), - Json(Value), -} - -impl PartialOrd for Elem { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (Self::Unit, Self::Unit) => Some(cmp::Ordering::Equal), - (Self::Bool(x), Self::Bool(y)) => x.partial_cmp(y), - (Self::Bytes(x), Self::Bytes(y)) => x.partial_cmp(y), - (Self::Number(x), Self::Number(y)) => x.to_string().partial_cmp(&y.to_string()), - (Self::String(x), Self::String(y)) => x.partial_cmp(y), - (Self::Array(x), Self::Array(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None }, - (Self::Object(x), Self::Object(y)) => if x == y { Some(cmp::Ordering::Equal) } else { None } - (_, _) => None, - } - } -} - - -// EnumSetType implies: Copy, PartialEq, Eq -#[derive(EnumSetType, Debug, PartialOrd, Ord, Serialize, Deserialize)] -pub enum ElemSymbol { - Unit, - Bool, - Number, - Bytes, - String, - Array, - Object, - Json, -} - -impl Arbitrary for ElemSymbol { - fn arbitrary(g: &mut Gen) -> Self { - let choices: Vec = EnumSet::all().iter().collect(); - *g.choose(&choices).unwrap_or_else(|| &Self::Unit) - } - - fn shrink(&self) -> Box> { - let self_copy = self.clone(); - Box::new(EnumSet::all().iter().filter(move |&x| x < self_copy)) - } -} - - - - -impl ElemSymbol { - pub fn arbitrary_contents(&self, g: &mut Gen) -> Elem { - match self { - Self::Unit => Elem::Unit, - Self::Bool => Elem::Bool(Arbitrary::arbitrary(g)), - Self::Number => { - let x: ArbitraryNumber = Arbitrary::arbitrary(g); - Elem::Number(x.number) - }, - Self::Bytes => Elem::Bytes(Arbitrary::arbitrary(g)), - Self::String => Elem::String(Arbitrary::arbitrary(g)), - Self::Array => { - let xs: Vec = Arbitrary::arbitrary(g); - Elem::Array(xs.into_iter().map(|x| x.value).collect()) - }, - Self::Object => { - let xs: ArbitraryMap = Arbitrary::arbitrary(g); - Elem::Object(From::from(xs)) - }, - Self::Json => { - let xs: ArbitraryValue = Arbitrary::arbitrary(g); - Elem::Json(xs.value) - }, - } - } -} - - -impl From for &'static str { - fn from(x: ElemSymbol) -> Self { - match x { - ElemSymbol::Unit => "Unit", - ElemSymbol::Bool => "Bool", - ElemSymbol::Bytes => "Bytes", - ElemSymbol::Number => "Number", - ElemSymbol::String => "String", - ElemSymbol::Array => "Array", - ElemSymbol::Object => "Object", - ElemSymbol::Json => "JSON", - } - } -} - -impl From<&Elem> for ElemSymbol { - fn from(x: &Elem) -> Self { - match x { - Elem::Unit => Self::Unit, - Elem::Bool(_) => Self::Bool, - Elem::Number(_) => Self::Number, - Elem::Bytes(_) => Self::Bytes, - Elem::String(_) => Self::String, - Elem::Array(_) => Self::Array, - Elem::Object(_) => Self::Object, - Elem::Json(_) => Self::Json, - } - } -} - -impl ElemSymbol { - #[cfg(test)] - pub fn default_elem(&self) -> Elem { - match self { - Self::Unit => Elem::Unit, - Self::Bool => Elem::Bool(Default::default()), - Self::Number => Elem::Number(Default::default()), - Self::Bytes => Elem::Bytes(Default::default()), - Self::String => Elem::String(Default::default()), - Self::Array => Elem::Array(Default::default()), - Self::Object => Elem::Object(Default::default()), - Self::Json => Elem::Json(Default::default()), - } - } - - pub fn ty(&self) -> Ty { - Ty { - ty_set: EnumSet::only(self.clone()), - } - } -} - -#[cfg(test)] -mod elem_symbol_tests { - use super::*; - - #[test] - fn test_from_default_elem() { - for symbol in EnumSet::all().iter() { - assert_eq!(symbol, From::from(symbol.default_elem())) - } - } - - #[test] - fn test_to_default_elem() { - for default_elem in [ - Elem::Unit, - Elem::Bool(Default::default()), - Elem::Number(Default::default()), - Elem::Bytes(Default::default()), - Elem::String(Default::default()), - Elem::Array(Default::default()), - Elem::Object(Default::default()), - Elem::Json(Default::default()), - ] { - assert_eq!(default_elem, From::from(default_elem).default_elem()) - } - } -} - -impl Elem { - pub fn symbol(&self) -> ElemSymbol { - From::from(self) - } - - pub fn symbol_str(&self) -> &'static str { - From::from(self.symbol()) - } - - // pub fn push_ty_sets(&self) -> (Vec, Vec) { - // (vec![], vec![self.symbol().ty()]) - // } - - pub fn assert_true(&self) -> Result<(), ElemError> { - match self { - Self::Bool(x) => if *x { - Ok(()) - } else { - Err(ElemError::AssertTrueFailed()) - }, - found => Err(ElemError::AssertTrueUnsupportedType(found.clone())), - } - } - - pub fn check_le(self, other: Self) -> Result { - let result = match self.partial_cmp(&other) - .ok_or_else(|| ElemError::CheckLeIncomparableTypes { - lhs: self.symbol_str(), - rhs: other.symbol_str() })? { - cmp::Ordering::Less => true, - cmp::Ordering::Equal => true, - cmp::Ordering::Greater => false, - }; - Ok(Self::Bool(result)) - } - - pub fn check_lt(self, other: Self) -> Result { - let result = match self.partial_cmp(&other) - .ok_or_else(|| ElemError::CheckLtIncomparableTypes { - lhs: self.symbol_str(), - rhs: other.symbol_str() })? { - cmp::Ordering::Less => true, - _ => false, - }; - Ok(Self::Bool(result)) - } - - pub fn check_eq(self, other: Self) -> Result { - let result = match self.partial_cmp(&other) - .ok_or_else(|| ElemError::CheckEqIncomparableTypes { - lhs: self.symbol_str(), - rhs: other.symbol_str() })? { - cmp::Ordering::Equal => true, - _ => false, - }; - Ok(Self::Bool(result)) - } - - fn concat_generic::Item>>(x: T, y: T) -> T { - x.into_iter().chain(y.into_iter()).collect() - } - - pub fn concat(self, other: Self) -> Result { - match (self, other) { - (Self::Bytes(x), Self::Bytes(y)) => Ok(Self::Bytes(Self::concat_generic(x, y))), - (Self::String(x), Self::String(y)) => { - Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) - .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) - }, - (Self::Array(x), Self::Array(y)) => Ok(Self::Array(Self::concat_generic(x, y))), - (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), - (some_x, some_y) => { - Err(ElemError::ConcatUnsupportedTypes { - lhs: some_x.symbol_str(), - rhs: some_y.symbol_str() - }) - }, - } - } - - fn slice_generic::Item>>(offset: Number, - length: Number, - iterable: T, - elem_symbol: ElemSymbol) -> - Result { - let u_offset = offset.as_u64() - .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - let u_length = length.as_u64() - .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - let u_offset_plus_length = u_offset.checked_add(u_length) - .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; - if iterable.clone().into_iter().count() < u_offset_plus_length { - Err(ElemError::SliceTooShort { - offset: u_offset, - length: u_length, - iterable: From::from(elem_symbol), - }) - } else { - Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) - } - } - - pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { - match (maybe_offset, maybe_length, maybe_iterable) { - (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => - Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), - (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { - let iterator_vec = Vec::from(iterator.clone()); - Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) - .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) - }, - (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => - Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), - (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => - Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), - (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { - Err(ElemError::SliceUnsupportedTypes { - maybe_not_offset: maybe_not_offset.symbol_str(), - maybe_not_length: maybe_not_length.symbol_str(), - maybe_not_iterable: maybe_not_iterable.symbol_str(), - }) - } - } - } - - fn index_generic::Item>>(index: Number, - iterable: T, - elem_symbol: ElemSymbol) -> - Result<::Item, ElemError> { - let u_index: usize = index.as_u64() - .ok_or_else(|| ElemError::IndexNotU64(index.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; - if iterable.clone().into_iter().count() <= u_index { - return Err(ElemError::IndexTooShort { - index: u_index, - iterable: From::from(elem_symbol), - }) - } else { - match iterable.into_iter().skip(u_index).next() { - None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), - Some(x) => Ok(x), - } - } - } - - pub fn index(self, maybe_iterable: Self) -> Result { - match (self, maybe_iterable) { - // (Self::Number(index), Self::Bytes(iterator)) => - // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), - (Self::Number(index), Self::Array(iterator)) => - Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), - (Self::Number(index), Self::Object(iterator)) => - Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), - (maybe_not_index, maybe_not_iterable) => { - Err(ElemError::IndexUnsupportedTypes { - maybe_not_index: maybe_not_index.symbol_str(), - maybe_not_iterable: maybe_not_iterable.symbol_str(), - }) - } - } - } - - // you can lookup a key in a Map (or fail, no recovery) - pub fn lookup(self, maybe_map: Self) -> Result { - match (self, maybe_map) { - (Self::String(key), Self::Object(map)) => { - Ok(Self::Json(map.get(&key) - .ok_or_else(|| ElemError::LookupKeyMissing { - key: key, - map: map.clone(), - }) - .map(|x|x.clone())?)) - }, - (maybe_not_key, maybe_not_map) => Err(ElemError::LookupUnsupportedTypes { - maybe_not_key: maybe_not_key.symbol_str(), - maybe_not_map: maybe_not_map.symbol_str(), - }), - } - } - - pub fn sha256(self) -> Result { - match self { - Self::Bytes(bytes) => { - Ok(Self::Bytes(super::sha256(&bytes))) - } - elem => Err(ElemError::HashUnsupportedType(elem.symbol_str())), - } - } - - pub fn to_json(self) -> Result { - Ok(Self::Json(serde_json::to_value(self)?)) - } - - pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { - match (self, elem_symbol) { - (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), - (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), - (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), - (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), - (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), - (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), - (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { - json: json, - elem_symbol: From::from(elem_symbol), - }), - (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { - non_json: non_json.symbol_str(), - elem_symbol: From::from(elem_symbol), - }), - } - } - - pub fn string_to_bytes(self) -> Result { - match self { - Self::String(x) => Ok(Self::Bytes(x.into_bytes())), - other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), - } - } -} - -#[derive(Debug, PartialEq, Error)] -pub enum ElemError { - #[error("expected Elem::Bool(true), found {0:?}")] - AssertTrueUnsupportedType(Elem), - #[error("expected true, but found false")] - AssertTrueFailed(), - #[error("check_le: incomparable types: {lhs:?}; {rhs:?}")] - CheckLeIncomparableTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("check_lt: incomparable types: {lhs:?}; {rhs:?}")] - CheckLtIncomparableTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("check_eq: incomparable types: {lhs:?}; {rhs:?}")] - CheckEqIncomparableTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("concat applied to unsupported types: lhs: {lhs:?}; rhs: {rhs:?}")] - ConcatUnsupportedTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("concat applied to strings that concatentate to invalid UTF8: lhs: {lhs:?}; rhs: {rhs:?}")] - ConcatInvalidUTF8 { - lhs: String, - rhs: String, - }, - - #[error("slice applied to unsupported types: maybe_not_offset: {maybe_not_offset:?}; maybe_not_length: {maybe_not_length:?}; maybe_not_iterable: {maybe_not_iterable:?}")] - SliceUnsupportedTypes { - maybe_not_offset: &'static str, - maybe_not_length: &'static str, - maybe_not_iterable: &'static str, - }, - #[error("slice applied to an 'offset' that can't be unpacked to u64: offset: {0:?}")] - SliceOffsetNotU64(Number), - #[error("slice applied to a 'length' that can't be unpacked to u64: length: {0:?}")] - SliceLengthNotU64(Number), - #[error("slice applied to an iterable that's too short for the given offset: offset: {offset:?} and length: {length:?}: iterable: {iterable:?}")] - SliceTooShort { - offset: usize, - length: usize, - iterable: &'static str, - }, - #[error("slice applied to offset and length whose sum overflows usize: offset: {offset:?} and length: {length:?}")] - SliceOverflow { - offset: Number, - length: Number, - }, - #[error("slice applied to arguments that produce invalid UTF8: offset: {offset:?}; length: {length:?}, iterator: {iterator:?}")] - SliceInvalidUTF8 { - offset: Number, - length: Number, - iterator: String, - }, - - #[error("index applied to unsupported types: maybe_not_index: {maybe_not_index:?}; maybe_not_iterable: {maybe_not_iterable:?}")] - IndexUnsupportedTypes { - maybe_not_index: &'static str, - maybe_not_iterable: &'static str, - }, - #[error("index applied to an 'index' that can't be unpacked to u64: {0:?}")] - IndexNotU64(Number), - #[error("index applied to an iterable that's too short for the given index: {index:?}; iterable: {iterable:?}")] - IndexTooShort { - index: usize, - iterable: &'static str, - }, - #[error("slice applied to offset and length whose sum overflows usize: {0:?}")] - IndexOverflow(Number), - - #[error("lookup applied to unsupported types: maybe_not_key: {maybe_not_key:?}; maybe_not_map: {maybe_not_map:?}")] - LookupUnsupportedTypes { - maybe_not_key: &'static str, - maybe_not_map: &'static str, - }, - #[error("lookup applied to a map that doesn't contain the given key: {key:?}; map: {map:?}")] - LookupKeyMissing { - key: String, - map: Map, - }, - - #[error("sha256 applied an Elem of an unsupported type ({0})")] - HashUnsupportedType(&'static str), - - #[error("to_json/from_json serialization failed: ({0})")] - ToFromJsonFailed(String), - - #[error("from_json applied an Elem of an unsupported type ({0})")] - FromJsonUnsupportedType(&'static str), - - #[error("object_from_json applied an Elem of an unsupported type ({0})")] - ObjectFromJsonUnsupportedType(&'static str), - #[error("object_from_json applied unexpected JSON: ({0})")] - ObjectFromJsonUnexpecteJson(Value), - #[error("array_from_json applied an Elem of an unsupported type ({0})")] - ArrayFromJsonUnsupportedType(&'static str), - #[error("array_from_json applied unexpected JSON: ({0})")] - ArrayFromJsonUnexpecteJson(Value), +// NEXT: +// - define a context of type variables (Vec> === Map>) +// - define input/output stacks of type variables (Vec) +// - define unification/inference/typing rules/patterns - #[error("string_from_json applied an Elem of an unsupported type ({0})")] - StringFromJsonUnsupportedType(&'static str), - #[error("string_from_json applied unexpected JSON: ({0})")] - StringFromJsonUnexpecteJson(Value), +// Typing Overview: +// - calculate the number of in/out stack elements per instruction +// + most consume 0..2 and produce one input +// + exceptions are restack and assert_true +// - trace the stack type variables through the execution +// + [ instruction ] -> [ (instruction, [stack_variable]) ], num_stack_variables +// + map from type_var -> [ (instruction_location, (instruction), stack_location) ] +// * instruction may/may-not be needed here +// * stack_location differentiates between e.g. index number and iterable +// + convert to a list of constraints +// + resolve the list of constraints to a single type - #[error("unpack_json applied to a value that's not raw JSON or it didn't match the expected type: {non_json:?}; type: {elem_symbol:?}")] - UnpackJsonUnexpectedType { - non_json: &'static str, - elem_symbol: &'static str, - }, - #[error("unpack_json applied to raw JSON and an unsupported type: {json:?}; type: {elem_symbol:?}")] - UnpackJsonUnsupportedSymbol { - json: serde_json::Value, - elem_symbol: &'static str, - }, +// typing: +// - inference +// - checking against inferred or other type (this + inference = bidirecitonal) +// - unification +// - two categories of tests: +// + property tests for typing methods themselves +// + test that a function having a particular type -> it runs w/o type errors on such inputs - #[error("string_to_bytes applied to an Elem of an unsupported type ({0})")] - StringToBytesUnsupportedType(&'static str), -} +// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] +// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] +// HashSha256, // : [ bytes ] -> [ bytes ] +// CheckLe, // : [ x, x ] -> [ bool ] +// CheckLt, // : [ x, x ] -> [ bool ] +// CheckEq, // : [ x, x ] -> [ bool ] +// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] +// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] +// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] +// Lookup, // [ string, object ] -> [ json ] +// AssertTrue, // [ bool ] -> [] +// ToJson, // (t: type) : [ t ] -> [ json ] +// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] +// StringToBytes, // [ string ] -> [ bytes ] -impl From for ElemError { - fn from(error: serde_json::Error) -> Self { - ElemError::ToFromJsonFailed(format!("{}", error)) - } -} #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub enum Instruction { @@ -684,13 +68,6 @@ pub enum Instruction { } impl Instruction { - // forall (instr : Instructions) (input_stack: Executor), - // if input_stack.len() < instr.stack_io_counts().0 { - // stack_too_small error - // } else { - // output_stack.len() = input_stack.len() - instr.stack_io_counts().0 + instr.stack_io_counts().1 - // } - // (consumed_input_stack_size, produced_output_stack_size) pub fn stack_io_counts(&self) -> (usize, usize) { match self { @@ -733,48 +110,8 @@ impl Instruction { } pub type Instructions = Vec; -// pub type Stack = Vec; - -// impl Instructions { -// pub fn stack_io_counts(&self, input_stack_size: usize) -> (usize, usize) { -// self.iter().fold((input_stack_size, input_stack_size), |memo, x| { -// let (memo_input, memo_output) = memo; -// let (next_input, next_output) = x.stack_io_counts; - -// let mut this_input = memo_input; -// let mut this_output = memo_output; - -// while this_input < next_input { -// this_input += 1; -// this_output += 1; -// } -// (this_input, this_output + next_output) -// }) -// } -// } - -// Typing Overview: -// - calculate the number of in/out stack elements per instruction -// + most consume 0..2 and produce one input -// + exceptions are restack and assert_true -// - trace the stack type variables through the execution -// + [ instruction ] -> [ (instruction, [stack_variable]) ], num_stack_variables -// + map from type_var -> [ (instruction_location, (instruction), stack_location) ] -// * instruction may/may-not be needed here -// * stack_location differentiates between e.g. index number and iterable -// + convert to a list of constraints -// + resolve the list of constraints to a single type - - - - - - - - - - +// pub type Stack = Vec; @@ -814,6 +151,11 @@ pub enum SrcRangeError { +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Ty { + ty_set: EnumSet, +} + // #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] // pub struct TyUnifyLocation { // lhs: SrcRange, @@ -821,11 +163,6 @@ pub enum SrcRangeError { // stack_position: usize, // } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Ty { - ty_set: EnumSet, -} - // impl Ty { // pub fn any() -> Self { // Ty { @@ -850,30 +187,6 @@ pub struct Ty { // } -// typing: -// - inference -// - checking against inferred or other type (this + inference = bidirecitonal) -// - unification -// - two categories of tests: -// + property tests for typing methods themselves -// + test that a function having a particular type -> it runs w/o type errors on such inputs - - // Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] - // Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] - // HashSha256, // : [ bytes ] -> [ bytes ] - // CheckLe, // : [ x, x ] -> [ bool ] - // CheckLt, // : [ x, x ] -> [ bool ] - // CheckEq, // : [ x, x ] -> [ bool ] - // Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] - // Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] - // Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] - // Lookup, // [ string, object ] -> [ json ] - // AssertTrue, // [ bool ] -> [] - // ToJson, // (t: type) : [ t ] -> [ json ] - // UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] - // StringToBytes, // [ string ] -> [ bytes ] - - // // TODO: use in_ty/out_ty @@ -954,7 +267,6 @@ pub struct Ty { // // } - // } @@ -985,8 +297,3 @@ pub struct Ty { // } // } - - - - - From f5fc69b371496092b2d9dd5fcfe37ec9327fda33 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 17 Feb 2022 18:10:36 -0500 Subject: [PATCH 25/77] add typing for each instruction besides Restack --- src/restack.rs | 20 ++-- src/types.rs | 317 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 268 insertions(+), 69 deletions(-) diff --git a/src/restack.rs b/src/restack.rs index ac95c84..8ffcbd2 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -118,14 +118,14 @@ impl Restack { } } - // self.valid_depth() -> + // self.is_valid_depth() -> // self.restack_depth <= xs.len() -> // self.run(xs).is_ok() == true - pub fn valid_depth(&self) -> bool { + pub fn is_valid_depth(&self) -> bool { !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) } - // NOTE: unchecked (run valid_depth on arguments for safe version) + // NOTE: unchecked (run is_valid_depth on arguments for safe version) // x.append(y).run(s) == x.run(y.run(s)) pub fn append(&self, other: Self) -> Self { Restack { @@ -163,13 +163,13 @@ mod tests { fn test_restack_id() { let mut example_stack = vec![false, true]; let restack = Restack::id(); - assert!(restack.valid_depth(), "Restack::id() has invalid depth"); + assert!(restack.is_valid_depth(), "Restack::id() has invalid depth"); assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) } #[test] fn test_restack_dig() { - assert!(Restack::dig(4).valid_depth(), "Restack::dig(4) has invalid depth"); + assert!(Restack::dig(4).is_valid_depth(), "Restack::dig(4) has invalid depth"); assert_eq!(Restack { restack_depth: 5, restack_vec: vec![4, 0, 1, 2, 3] }, Restack::dig(4)); let mut example_stack_in = vec![false, false, false, false, true]; let example_stack_out = vec![true, false, false, false, false]; @@ -178,7 +178,7 @@ mod tests { #[test] fn test_restack_dug() { - assert!(Restack::dug(4).valid_depth(), "Restack::dug(4) has invalid depth"); + assert!(Restack::dug(4).is_valid_depth(), "Restack::dug(4) has invalid depth"); assert_eq!(Restack { restack_depth: 5, restack_vec: vec![1, 2, 3, 4, 0] }, Restack::dug(4)); let mut example_stack_in = vec![true, false, false, false, false]; let example_stack_out = vec![false, false, false, false, true]; @@ -194,7 +194,7 @@ mod tests { vec![false], vec![]] { let restack = Restack::drop_n(3 - example_stack_out.len()); - assert!(restack.valid_depth(), "Restack::drop_n(_) has invalid depth"); + assert!(restack.is_valid_depth(), "Restack::drop_n(_) has invalid depth"); assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in.clone())); } } @@ -204,7 +204,7 @@ mod tests { let mut example_stack_in = vec![false, true]; let example_stack_out = vec![true]; let restack = Restack::drop(); - assert!(restack.valid_depth(), "Restack::drop() has invalid depth"); + assert!(restack.is_valid_depth(), "Restack::drop() has invalid depth"); assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) } @@ -213,7 +213,7 @@ mod tests { let mut example_stack_in = vec![false, true]; let example_stack_out = vec![true, false]; let restack = Restack::swap(); - assert!(restack.valid_depth(), "Restack::swap() has invalid depth"); + assert!(restack.is_valid_depth(), "Restack::swap() has invalid depth"); assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) } @@ -221,7 +221,7 @@ mod tests { fn test_restack_swap_twice_append() { let mut example_stack = vec![false, true]; let restack = Restack::swap().append(Restack::swap()); - assert!(restack.valid_depth(), "Restack::swap().append(Restack::swap()) has invalid depth"); + assert!(restack.is_valid_depth(), "Restack::swap().append(Restack::swap()) has invalid depth"); assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) } } diff --git a/src/types.rs b/src/types.rs index 3d01a76..c1f34f2 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,7 @@ use crate::restack::Restack; use crate::elem::{Elem, ElemSymbol}; -use std::ops::Range; - -use enumset::{EnumSet, EnumSetType}; +use enumset::{EnumSet}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -33,21 +31,6 @@ use thiserror::Error; // + property tests for typing methods themselves // + test that a function having a particular type -> it runs w/o type errors on such inputs -// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] -// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] -// HashSha256, // : [ bytes ] -> [ bytes ] -// CheckLe, // : [ x, x ] -> [ bool ] -// CheckLt, // : [ x, x ] -> [ bool ] -// CheckEq, // : [ x, x ] -> [ bool ] -// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] -// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] -// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] -// Lookup, // [ string, object ] -> [ json ] -// AssertTrue, // [ bool ] -> [] -// ToJson, // (t: type) : [ t ] -> [ json ] -// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] -// StringToBytes, // [ string ] -> [ bytes ] - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub enum Instruction { @@ -115,47 +98,268 @@ pub type Instructions = Vec; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct SrcRange { - range: Range, + + + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemType { + type_set: EnumSet, } -impl SrcRange { - pub fn singleton(src_location: usize) -> Self { - SrcRange { - range: (src_location..src_location + 1), +impl ElemSymbol { + pub fn elem_type(&self) -> ElemType { + ElemType { + type_set: EnumSet::only(*self), } } +} - pub fn append(&self, other: Self) -> Result { - if self.range.end + 1 == other.range.start { - Ok(SrcRange { range: self.range.start..other.range.end }) - } else { - Err(SrcRangeError::MismatchedRanges { - lhs: self.clone(), - rhs: other, - }) +impl Elem { + pub fn elem_type(&self) -> ElemType { + self.symbol().elem_type() + } +} + +impl ElemType { + pub fn any() -> Self { + ElemType { + type_set: EnumSet::all(), } } } -#[derive(Debug, PartialEq, Error)] -pub enum SrcRangeError { - #[error("SrcRange::append applied to non-contiguous ranges: lhs: {lhs:?}; rhs: {rhs:?}")] - MismatchedRanges { - lhs: SrcRange, - rhs: SrcRange, - }, +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Context { + context: Vec } +impl Context { +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct TypeId { + type_id: usize, +} +impl Context { + pub fn new() -> Self { + Context { + context: vec![], + } + } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Ty { - ty_set: EnumSet, + pub fn size(&self) -> usize { + self.context.len() + } + + pub fn push(&mut self, elem_type: ElemType) -> TypeId { + let push_id = TypeId { + type_id: self.size(), + }; + self.context.push(elem_type); + push_id + } +} + + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Type { + context: Context, + i_type: Vec, + o_type: Vec, +} + +impl Type { + pub fn is_valid(&self) -> bool { + let context_size = self.context.size(); + !(self.i_type.iter().any(|x| x.type_id >= context_size) || + self.o_type.iter().any(|x| x.type_id >= context_size)) + } +} + +// TODO: implement +impl Restack { + pub fn type_of(&self) -> Type { + panic!("Restack.type_of unimplemented"); + + let mut context = Context::new(); + // let bool_var = context.push(ElemSymbol::Bool.elem_type()); + Type { + context: context, + i_type: vec![], + o_type: vec![], + } + } } +impl Instruction { + pub fn type_of(&self) -> Type { + match self { + Instruction::Restack(restack) => restack.type_of(), + + Instruction::AssertTrue => { + let mut context = Context::new(); + let bool_var = context.push(ElemSymbol::Bool.elem_type()); + Type { + context: context, + i_type: vec![bool_var], + o_type: vec![], + } + }, + + Instruction::Push(elem) => { + let mut context = Context::new(); + let elem_var = context.push(elem.elem_type()); + Type { + context: context, + i_type: vec![], + o_type: vec![elem_var], + } + }, + + Instruction::HashSha256 => { + let mut context = Context::new(); + let bytes_var = context.push(ElemSymbol::Bytes.elem_type()); + Type { + context: context, + i_type: vec![bytes_var], + o_type: vec![bytes_var], + } + }, + + Instruction::ToJson => { + let mut context = Context::new(); + let any_var = context.push(ElemType::any()); + let json_var = context.push(ElemSymbol::Json.elem_type()); + Type { + context: context, + i_type: vec![any_var], + o_type: vec![json_var], + } + }, + + Instruction::StringToBytes => { + let mut context = Context::new(); + let string_var = context.push(ElemSymbol::String.elem_type()); + let bytes_var = context.push(ElemSymbol::Bytes.elem_type()); + Type { + context: context, + i_type: vec![string_var], + o_type: vec![bytes_var], + } + }, + + Instruction::UnpackJson(elem_symbol) => { + let mut context = Context::new(); + let json_var = context.push(ElemSymbol::Json.elem_type()); + let elem_symbol_var = context.push(elem_symbol.elem_type()); + Type { + context: context, + i_type: vec![json_var], + o_type: vec![elem_symbol_var], + } + }, + + Instruction::CheckLe => { + let mut context = Context::new(); + let any_lhs_var = context.push(ElemType::any()); + let any_rhs_var = context.push(ElemType::any()); + let bool_var = context.push(ElemSymbol::Bool.elem_type()); + Type { + context: context, + i_type: vec![any_lhs_var, any_rhs_var], + o_type: vec![bool_var], + } + }, + + Instruction::CheckLt => { + let mut context = Context::new(); + let any_lhs_var = context.push(ElemType::any()); + let any_rhs_var = context.push(ElemType::any()); + let bool_var = context.push(ElemSymbol::Bool.elem_type()); + Type { + context: context, + i_type: vec![any_lhs_var, any_rhs_var], + o_type: vec![bool_var], + } + }, + + Instruction::CheckEq => { + let mut context = Context::new(); + let any_lhs_var = context.push(ElemType::any()); + let any_rhs_var = context.push(ElemType::any()); + let bool_var = context.push(ElemSymbol::Bool.elem_type()); + Type { + context: context, + i_type: vec![any_lhs_var, any_rhs_var], + o_type: vec![bool_var], + } + }, + + Instruction::Concat => { + let mut context = Context::new(); + let concat_var = context.push(ElemType::concat_type()); + Type { + context: context, + i_type: vec![concat_var, concat_var], + o_type: vec![concat_var], + } + }, + + Instruction::Index => { + let mut context = Context::new(); + let number_var = context.push(ElemSymbol::Number.elem_type()); + let index_var = context.push(ElemType::index_type()); + Type { + context: context, + i_type: vec![number_var, index_var], + o_type: vec![index_var], + } + }, + + Instruction::Lookup => { + let mut context = Context::new(); + let string_var = context.push(ElemSymbol::String.elem_type()); + let object_var = context.push(ElemSymbol::Object.elem_type()); + Type { + context: context, + i_type: vec![string_var, object_var], + o_type: vec![object_var], + } + }, + + Instruction::Slice => { + let mut context = Context::new(); + let offset_number_var = context.push(ElemSymbol::Number.elem_type()); + let length_number_var = context.push(ElemSymbol::Number.elem_type()); + let slice_var = context.push(ElemType::slice_type()); + Type { + context: context, + i_type: vec![offset_number_var, length_number_var, slice_var], + o_type: vec![slice_var], + } + }, + } + } +} + +// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] +// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] +// HashSha256, // : [ bytes ] -> [ bytes ] +// CheckLe, // : [ x, x ] -> [ bool ] +// CheckLt, // : [ x, x ] -> [ bool ] +// CheckEq, // : [ x, x ] -> [ bool ] +// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] +// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] +// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] +// Lookup, // [ string, object ] -> [ json ] +// AssertTrue, // [ bool ] -> [] +// ToJson, // (t: type) : [ t ] -> [ json ] +// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] +// StringToBytes, // [ string ] -> [ bytes ] + + + // #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] // pub struct TyUnifyLocation { // lhs: SrcRange, @@ -164,11 +368,6 @@ pub struct Ty { // } // impl Ty { -// pub fn any() -> Self { -// Ty { -// ty_set: EnumSet::all(), -// } -// } // pub fn unify(&self, other: Self, location: TyUnifyLocation) -> Result { // let both = self.ty_set.intersection(other.ty_set); @@ -194,8 +393,8 @@ pub struct Ty { // #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] // pub struct StackTy { // src_range: SrcRange, -// in_ty: Vec, -// out_ty: Vec, +// in_type: Vec, +// out_type: Vec, // } // impl StackTy { @@ -238,16 +437,16 @@ pub struct Ty { // } // pub fn unify(&self, other: Self) -> Result { -// let middle_ty = Self::unify_ty_sets(self.out_ty, other.in_ty, self.src_range, other.src_range); -// let self_remainder = Self::diff_ty_sets(middle_ty, self.out_ty); -// let other_remainder = Self::diff_ty_sets(middle_ty, other.in_ty); -// in_ty: self.in_ty + self_remainder -// out_ty: other.out_ty + other_remainder +// let middle_ty = Self::unify_ty_sets(self.out_type, other.in_type, self.src_range, other.src_range); +// let self_remainder = Self::diff_ty_sets(middle_ty, self.out_type); +// let other_remainder = Self::diff_ty_sets(middle_ty, other.in_type); +// in_type: self.in_type + self_remainder +// out_type: other.out_type + other_remainder // StackTy { // src_range: self.src_range.append(other.src_range)?, -// in_ty: Self::union_ty_sets(self.in_ty, self_remainder), -// out_ty: Self::union_ty_sets(other.out_ty, other_remainder), +// in_type Self::union_ty_sets(self.in_type, self_remainder), +// out_type Self::union_ty_sets(other.out_type, other_remainder), // } // } @@ -255,8 +454,8 @@ pub struct Ty { // let instruction_ty_sets = instruction.ty_sets(); // StackTy { // src_range: SrcRange::singleton(src_location), -// in_ty: instruction_ty_sets.0, -// out_ty: instruction_ty_sets.1, +// in_type instruction_ty_sets.0, +// out_type instruction_ty_sets.1, // } // } From 3af5eab88be33770b0a21daa7a35dec67acf2031 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 18 Feb 2022 14:30:08 -0500 Subject: [PATCH 26/77] added zip_then to compare typed stacks and got tests passing --- src/elem.rs | 16 +-- src/restack.rs | 4 +- src/types.rs | 312 +++++++++++++++++++++++++++++++------------------ 3 files changed, 203 insertions(+), 129 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 7cea31c..463a4fd 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -142,7 +142,7 @@ impl ElemSymbol { match self { Self::Unit => Elem::Unit, Self::Bool => Elem::Bool(Default::default()), - Self::Number => Elem::Number(Default::default()), + Self::Number => Elem::Number(From::::from(Default::default())), Self::Bytes => Elem::Bytes(Default::default()), Self::String => Elem::String(Default::default()), Self::Array => Elem::Array(Default::default()), @@ -150,12 +150,6 @@ impl ElemSymbol { Self::Json => Elem::Json(Default::default()), } } - - // pub fn ty(&self) -> Ty { - // Ty { - // ty_set: EnumSet::only(self.clone()), - // } - // } } #[cfg(test)] @@ -164,8 +158,8 @@ mod elem_symbol_tests { #[test] fn test_from_default_elem() { - for symbol in EnumSet::all().iter() { - assert_eq!(symbol, From::from(symbol.default_elem())) + for symbol in EnumSet::::all().iter() { + assert_eq!(symbol, symbol.default_elem().symbol()) } } @@ -174,14 +168,14 @@ mod elem_symbol_tests { for default_elem in [ Elem::Unit, Elem::Bool(Default::default()), - Elem::Number(Default::default()), + Elem::Number(From::::from(Default::default())), Elem::Bytes(Default::default()), Elem::String(Default::default()), Elem::Array(Default::default()), Elem::Object(Default::default()), Elem::Json(Default::default()), ] { - assert_eq!(default_elem, From::from(default_elem).default_elem()) + assert_eq!(default_elem, default_elem.symbol().default_elem()) } } } diff --git a/src/restack.rs b/src/restack.rs index 8ffcbd2..8fa3046 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -27,8 +27,8 @@ pub type StackIx = usize; // + global: keep track of stack indices (always possible?) and print where it's from??? #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Restack { - restack_depth: StackIx, - restack_vec: Vec, + pub restack_depth: StackIx, + pub restack_vec: Vec, } impl Restack { diff --git a/src/types.rs b/src/types.rs index c1f34f2..f9598a1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,9 +1,70 @@ -use crate::restack::Restack; +use crate::restack::{Restack, RestackError}; use crate::elem::{Elem, ElemSymbol}; -use enumset::{EnumSet}; +use std::collections::BTreeMap; + +use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; -use thiserror::Error; +// use thiserror::Error; + + +// TODO: relocate +pub fn zip_then(a: A, b: B, mut fab: FAB, mut fa: FA, mut fb: FB) -> Result<(), R> +where + A: IntoIterator, + B: IntoIterator, + FAB: FnMut(::Item, ::Item) -> Result<(), R>, + FA: FnMut(::Item) -> Result<(), R>, + FB: FnMut(::Item) -> Result<(), R>, +{ + let mut b_iter = b.into_iter(); + for x in a.into_iter() { + match b_iter.next() { + Some(y) => fab(x, y)?, + None => fa(x)?, + } + } + for y in b_iter { + fb(y)? + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_zip_then_longer_rhs() { + let xs = vec![false, false, false, false, true]; + let ys = vec![true, true, true, true, false, true, true, false]; + let mut xs_out = vec![]; + let mut ys_out = vec![]; + let mut xs_out_remainder = vec![]; + let mut ys_out_remainder = vec![]; + assert_eq!(Ok::<(), ()>(()), + zip_then(&xs, + &ys, + |x, y| { + xs_out.push(x); + ys_out.push(y); + Ok(()) + }, + |x| { + xs_out_remainder.push(x); + Ok(()) + }, + |y| { + ys_out_remainder.push(y); + Ok(()) + })); + xs_out.append(&mut xs_out_remainder); + ys_out.append(&mut ys_out_remainder); + assert_eq!(xs.iter().map(|x| *x).collect::>(), xs_out.iter().map(|&x| *x).collect::>()); + assert_eq!(ys.iter().map(|x| *x).collect::>(), ys_out.iter().map(|&x| *x).collect::>()); + } +} + // NEXT: @@ -50,48 +111,6 @@ pub enum Instruction { StringToBytes, } -impl Instruction { - // (consumed_input_stack_size, produced_output_stack_size) - pub fn stack_io_counts(&self) -> (usize, usize) { - match self { - Instruction::Push(_) => (0, 1), - Instruction::Restack(restack) => restack.stack_io_counts(), - Instruction::HashSha256 => (1, 1), - Instruction::CheckLe => (2, 1), - Instruction::CheckLt => (2, 1), - Instruction::CheckEq => (2, 1), - Instruction::Concat => (2, 1), - Instruction::Slice => (3, 1), - Instruction::Index => (2, 1), - Instruction::Lookup => (2, 1), - Instruction::AssertTrue => (1, 0), - Instruction::ToJson => (1, 1), - Instruction::UnpackJson(_) => (1, 1), - Instruction::StringToBytes => (1, 1), - } - } - - // pub fn ty_sets(&self) -> (Vec, Vec) { - // match self { - // Instruction::Push(elem) => elem.push_ty_sets(), - // // Restack(restack) => restack.ty_sets(), - // Instruction::HashSha256 => (vec![ElemSymbol::Bytes.ty()], vec![ElemSymbol::Bytes.ty()]), - // // CheckLe, - // // CheckLt, - // Instruction::CheckEq => (vec![Ty::any(), Ty::any()], vec![ElemSymbol::Bool.ty()]), - // // Concat, - // // Slice, - // // Index, - // // Lookup, - // // AssertTrue, - // // ToJson, - // // UnpackJson(ElemSymbol), - // // StringToBytes, - // _ => panic!("infer_instruction: unimplemented"), - // } - // } -} - pub type Instructions = Vec; // pub type Stack = Vec; @@ -126,43 +145,76 @@ impl ElemType { type_set: EnumSet::all(), } } -} -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Context { - context: Vec -} + pub fn concat_type() -> Self { + ElemType { + type_set: + enum_set!(ElemSymbol::Bytes | + ElemSymbol::String | + ElemSymbol::Array | + ElemSymbol::Object), + } + } -impl Context { + pub fn index_type() -> Self { + ElemType { + type_set: + enum_set!(ElemSymbol::Array | + ElemSymbol::Object), + } + } + + pub fn slice_type() -> Self { + Self::concat_type() + } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct TypeId { type_id: usize, } +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Context { + context: BTreeMap, + next_type_id: TypeId, +} + impl Context { pub fn new() -> Self { Context { - context: vec![], + context: BTreeMap::new(), + next_type_id: TypeId { + type_id: 0, + }, } } + pub fn is_valid(&self) -> bool { + !self.context.keys().any(|x| *x >= self.next_type_id) + } + pub fn size(&self) -> usize { self.context.len() } pub fn push(&mut self, elem_type: ElemType) -> TypeId { - let push_id = TypeId { - type_id: self.size(), + let push_id = self.next_type_id; + self.context.insert(push_id, elem_type); + self.next_type_id = TypeId { + type_id: push_id.type_id + 1, }; - self.context.push(elem_type); push_id } + + // pub fn push_unified(&mut self, xs: Self, ys: Self, xi: TypeId, yi: TypeId) -> Self { + // _ + // } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Type { context: Context, i_type: Vec, @@ -170,94 +222,141 @@ pub struct Type { } impl Type { + // check whether all the TypeId's are valid pub fn is_valid(&self) -> bool { - let context_size = self.context.size(); - !(self.i_type.iter().any(|x| x.type_id >= context_size) || - self.o_type.iter().any(|x| x.type_id >= context_size)) + let next_type_id = self.context.next_type_id; + self.context.is_valid() && + !(self.i_type.iter().any(|x| *x >= next_type_id) || + self.o_type.iter().any(|x| *x >= next_type_id)) } + + // TODO: + // - use remapping of variables (always larger than max of both) + // - make method to simplify context + // - pretty-print Type + + // TODO: this is next, figure out how to represent/implement replacements of + // variables, e.g. starting larger than both or collecting maps (old_lhs -> new_lhs) + + // pub fn compose(&self, other: Self) -> Result { + // let mut context = Context::new(); + + // let unified_overlap = self.o_type.iter().zip(other.i_type.iter()).map(|x, y| { + // (x, y, context.push_unified(self, other, x, y)); + // }); + + // let remainder_overlap_iter = if self.o_type.len() <= other.i_type.len()) { + // other.i_type.iter().skip(self.o_type.len()) + // } else { + // self.o_type.iter().skip(other.i_type.len()) + // }; + + // let remainder_overlap = remainder_overlap_iter.map(|x| { + // (x, context.push(x)); + // }); + + // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context + // 2. collect the remainder and add them to the context + // 3. add the remainder to (self.i_type, other.o_type), with replaced variables + + // _ + + // } + } -// TODO: implement impl Restack { - pub fn type_of(&self) -> Type { - panic!("Restack.type_of unimplemented"); - + pub fn type_of(&self) -> Result { let mut context = Context::new(); - // let bool_var = context.push(ElemSymbol::Bool.elem_type()); - Type { + let mut restack_type: Vec = (0..self.restack_depth).map(|x| TypeId { type_id: x }).collect(); + Ok(Type { context: context, - i_type: vec![], - o_type: vec![], - } + i_type: restack_type.clone(), + o_type: self.run(&mut restack_type)?, + }) } } +/// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] +/// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] +/// HashSha256, // : [ bytes ] -> [ bytes ] +/// CheckLe, // : [ x, x ] -> [ bool ] +/// CheckLt, // : [ x, x ] -> [ bool ] +/// CheckEq, // : [ x, x ] -> [ bool ] +/// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] +/// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] +/// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] +/// Lookup, // [ string, object ] -> [ json ] +/// AssertTrue, // [ bool ] -> [] +/// ToJson, // (t: type) : [ t ] -> [ json ] +/// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] +/// StringToBytes, // [ string ] -> [ bytes ] impl Instruction { - pub fn type_of(&self) -> Type { + pub fn type_of(&self) -> Result { match self { - Instruction::Restack(restack) => restack.type_of(), + Instruction::Restack(restack) => Ok(restack.type_of()?), Instruction::AssertTrue => { let mut context = Context::new(); let bool_var = context.push(ElemSymbol::Bool.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![bool_var], o_type: vec![], - } + }) }, Instruction::Push(elem) => { let mut context = Context::new(); let elem_var = context.push(elem.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![], o_type: vec![elem_var], - } + }) }, Instruction::HashSha256 => { let mut context = Context::new(); let bytes_var = context.push(ElemSymbol::Bytes.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![bytes_var], o_type: vec![bytes_var], - } + }) }, Instruction::ToJson => { let mut context = Context::new(); let any_var = context.push(ElemType::any()); let json_var = context.push(ElemSymbol::Json.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![any_var], o_type: vec![json_var], - } + }) }, Instruction::StringToBytes => { let mut context = Context::new(); let string_var = context.push(ElemSymbol::String.elem_type()); let bytes_var = context.push(ElemSymbol::Bytes.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![string_var], o_type: vec![bytes_var], - } + }) }, Instruction::UnpackJson(elem_symbol) => { let mut context = Context::new(); let json_var = context.push(ElemSymbol::Json.elem_type()); let elem_symbol_var = context.push(elem_symbol.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![json_var], o_type: vec![elem_symbol_var], - } + }) }, Instruction::CheckLe => { @@ -265,11 +364,11 @@ impl Instruction { let any_lhs_var = context.push(ElemType::any()); let any_rhs_var = context.push(ElemType::any()); let bool_var = context.push(ElemSymbol::Bool.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![any_lhs_var, any_rhs_var], o_type: vec![bool_var], - } + }) }, Instruction::CheckLt => { @@ -277,11 +376,11 @@ impl Instruction { let any_lhs_var = context.push(ElemType::any()); let any_rhs_var = context.push(ElemType::any()); let bool_var = context.push(ElemSymbol::Bool.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![any_lhs_var, any_rhs_var], o_type: vec![bool_var], - } + }) }, Instruction::CheckEq => { @@ -289,43 +388,43 @@ impl Instruction { let any_lhs_var = context.push(ElemType::any()); let any_rhs_var = context.push(ElemType::any()); let bool_var = context.push(ElemSymbol::Bool.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![any_lhs_var, any_rhs_var], o_type: vec![bool_var], - } + }) }, Instruction::Concat => { let mut context = Context::new(); let concat_var = context.push(ElemType::concat_type()); - Type { + Ok(Type { context: context, i_type: vec![concat_var, concat_var], o_type: vec![concat_var], - } + }) }, Instruction::Index => { let mut context = Context::new(); let number_var = context.push(ElemSymbol::Number.elem_type()); let index_var = context.push(ElemType::index_type()); - Type { + Ok(Type { context: context, i_type: vec![number_var, index_var], o_type: vec![index_var], - } + }) }, Instruction::Lookup => { let mut context = Context::new(); let string_var = context.push(ElemSymbol::String.elem_type()); let object_var = context.push(ElemSymbol::Object.elem_type()); - Type { + Ok(Type { context: context, i_type: vec![string_var, object_var], o_type: vec![object_var], - } + }) }, Instruction::Slice => { @@ -333,30 +432,16 @@ impl Instruction { let offset_number_var = context.push(ElemSymbol::Number.elem_type()); let length_number_var = context.push(ElemSymbol::Number.elem_type()); let slice_var = context.push(ElemType::slice_type()); - Type { + Ok(Type { context: context, i_type: vec![offset_number_var, length_number_var, slice_var], o_type: vec![slice_var], - } + }) }, } } } -// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] -// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] -// HashSha256, // : [ bytes ] -> [ bytes ] -// CheckLe, // : [ x, x ] -> [ bool ] -// CheckLt, // : [ x, x ] -> [ bool ] -// CheckEq, // : [ x, x ] -> [ bool ] -// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] -// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] -// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] -// Lookup, // [ string, object ] -> [ json ] -// AssertTrue, // [ bool ] -> [] -// ToJson, // (t: type) : [ t ] -> [ json ] -// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] -// StringToBytes, // [ string ] -> [ bytes ] @@ -385,11 +470,6 @@ impl Instruction { // } // } - - - -// // TODO: use in_ty/out_ty - // #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] // pub struct StackTy { // src_range: SrcRange, From 3ee0763b14862f79adc4ffa0280a5d695723f016 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 18 Feb 2022 17:04:57 -0500 Subject: [PATCH 27/77] unification up and running for stack types --- src/types.rs | 361 +++++++++++++++++++++------------------------------ 1 file changed, 151 insertions(+), 210 deletions(-) diff --git a/src/types.rs b/src/types.rs index f9598a1..239b230 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,76 +2,32 @@ use crate::restack::{Restack, RestackError}; use crate::elem::{Elem, ElemSymbol}; use std::collections::BTreeMap; +use std::cmp; +use std::iter::Skip; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; -// use thiserror::Error; +use thiserror::Error; // TODO: relocate -pub fn zip_then(a: A, b: B, mut fab: FAB, mut fa: FA, mut fb: FB) -> Result<(), R> +pub fn after_zip(a: A, b: B) -> Result::IntoIter>, Skip<::IntoIter>> where A: IntoIterator, B: IntoIterator, - FAB: FnMut(::Item, ::Item) -> Result<(), R>, - FA: FnMut(::Item) -> Result<(), R>, - FB: FnMut(::Item) -> Result<(), R>, + ::IntoIter: ExactSizeIterator, + ::IntoIter: ExactSizeIterator, { - let mut b_iter = b.into_iter(); - for x in a.into_iter() { - match b_iter.next() { - Some(y) => fab(x, y)?, - None => fa(x)?, - } - } - for y in b_iter { - fb(y)? - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_zip_then_longer_rhs() { - let xs = vec![false, false, false, false, true]; - let ys = vec![true, true, true, true, false, true, true, false]; - let mut xs_out = vec![]; - let mut ys_out = vec![]; - let mut xs_out_remainder = vec![]; - let mut ys_out_remainder = vec![]; - assert_eq!(Ok::<(), ()>(()), - zip_then(&xs, - &ys, - |x, y| { - xs_out.push(x); - ys_out.push(y); - Ok(()) - }, - |x| { - xs_out_remainder.push(x); - Ok(()) - }, - |y| { - ys_out_remainder.push(y); - Ok(()) - })); - xs_out.append(&mut xs_out_remainder); - ys_out.append(&mut ys_out_remainder); - assert_eq!(xs.iter().map(|x| *x).collect::>(), xs_out.iter().map(|&x| *x).collect::>()); - assert_eq!(ys.iter().map(|x| *x).collect::>(), ys_out.iter().map(|&x| *x).collect::>()); + let a_iter = a.into_iter(); + let b_iter = b.into_iter(); + let max_len = cmp::max(a_iter.len(), b_iter.len()); + if max_len == a_iter.len() { + Ok(a_iter.skip(b_iter.len())) + } else { + Err(b_iter.skip(a_iter.len())) } } - - -// NEXT: -// - define a context of type variables (Vec> === Map>) -// - define input/output stacks of type variables (Vec) -// - define unification/inference/typing rules/patterns - // Typing Overview: // - calculate the number of in/out stack elements per instruction // + most consume 0..2 and produce one input @@ -167,6 +123,20 @@ impl ElemType { pub fn slice_type() -> Self { Self::concat_type() } + + pub fn unify(&self, other: Self) -> Result { + let both = self.type_set.intersection(other.type_set); + if both.is_empty() { + Err(TypeError::ElemTypeUnifyEmpty { + lhs: self.clone(), + rhs: other.clone(), + }) + } else { + Ok(ElemType { + type_set: both, + }) + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -190,6 +160,17 @@ impl Context { } } + pub fn new_max(&self, other: Self) -> Self { + Context { + context: BTreeMap::new(), + next_type_id: TypeId { + type_id: + cmp::max(self.next_type_id.type_id, + other.next_type_id.type_id), + }, + } + } + pub fn is_valid(&self) -> bool { !self.context.keys().any(|x| *x >= self.next_type_id) } @@ -207,9 +188,23 @@ impl Context { push_id } + pub fn get(&mut self, index: &TypeId) -> Result { + Ok(self.context.get(index).ok_or_else(|| TypeError::ContextGetUnknownTypeId { + context: self.clone(), + index: *index, + })?.clone()) + } + // pub fn push_unified(&mut self, xs: Self, ys: Self, xi: TypeId, yi: TypeId) -> Self { // _ // } + + // TODO: remove mut xs/ys + pub fn unify(&mut self, mut xs: Self, mut ys: Self, xi: &TypeId, yi: &TypeId) -> Result { + let x_type = xs.get(xi)?; + let y_type = ys.get(yi)?; + Ok(self.push(x_type.unify(y_type)?)) + } } @@ -231,44 +226,89 @@ impl Type { } // TODO: - // - use remapping of variables (always larger than max of both) // - make method to simplify context // - pretty-print Type // TODO: this is next, figure out how to represent/implement replacements of // variables, e.g. starting larger than both or collecting maps (old_lhs -> new_lhs) - // pub fn compose(&self, other: Self) -> Result { - // let mut context = Context::new(); - - // let unified_overlap = self.o_type.iter().zip(other.i_type.iter()).map(|x, y| { - // (x, y, context.push_unified(self, other, x, y)); - // }); - - // let remainder_overlap_iter = if self.o_type.len() <= other.i_type.len()) { - // other.i_type.iter().skip(self.o_type.len()) - // } else { - // self.o_type.iter().skip(other.i_type.len()) - // }; - - // let remainder_overlap = remainder_overlap_iter.map(|x| { - // (x, context.push(x)); - // }); - - // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context - // 2. collect the remainder and add them to the context - // 3. add the remainder to (self.i_type, other.o_type), with replaced variables - - // _ - - // } + // f : self + // g : other + // self.compose(other) : (f ++ g).type_of() + // + // input -> + // other.i_type + // other.o_type + // self.i_type + // self.o_type + // -> output + // + // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context + // 2. collect the remainder and add them to the context + // 3. add the remainder to (self.i_type, other.o_type), with replaced variables + pub fn compose(&self, other: Self) -> Result { + let self_context = &self.context; + let other_context = &other.context; + + let mut context = self_context.clone().new_max(other_context.clone()); + let mut self_type_map: BTreeMap = BTreeMap::new(); + let mut other_type_map: BTreeMap = BTreeMap::new(); + + let mut i_type = vec![]; + let mut o_type = vec![]; + + other.o_type.iter().zip(self.i_type.clone()).try_for_each(|(o_type, i_type)| { + let new_type_id = context + .unify(self_context.clone(), + other_context.clone(), + &i_type, + &o_type)?; + self_type_map.insert(i_type, new_type_id); + other_type_map.insert(*o_type, new_type_id); + Ok(()) + })?; + + match after_zip(other.o_type.clone(), self.i_type.clone()) { + Ok(other_o_type_remainder) => + for o_type in other_o_type_remainder { + let new_o_type = context.push(other.context.clone().get(&o_type)?); + other_type_map.insert(o_type.clone(), new_o_type); + i_type.push(new_o_type.clone()); + }, + Err(self_i_type_remainder) => + for i_type in self_i_type_remainder { + let new_i_type = context.push(self.context.clone().get(&i_type)?); + self_type_map.insert(i_type.clone(), new_i_type); + o_type.push(new_i_type.clone()); + }, + } + Ok(Type { + context: context, + i_type: other.i_type.clone().iter() + .map(move |x| Ok(other_type_map + .get(x) + .ok_or_else(|| TypeError::ContextGetUnknownTypeId { // TODO: new error + context: other.context.clone(), + index: *x, + })?.clone())).chain(i_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, + o_type: self.o_type.clone().iter() + .map(|x| Ok(self_type_map + .get(x) + .ok_or_else(|| TypeError::ContextGetUnknownTypeId { + context: self.context.clone(), + index: *x, + })?.clone())).chain(o_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, + }) + } } impl Restack { pub fn type_of(&self) -> Result { let mut context = Context::new(); - let mut restack_type: Vec = (0..self.restack_depth).map(|x| TypeId { type_id: x }).collect(); + let mut restack_type: Vec = (0..self.restack_depth) + .map(|_x| context.push(ElemType::any())) + .collect(); Ok(Type { context: context, i_type: restack_type.clone(), @@ -442,133 +482,31 @@ impl Instruction { } } - - - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TyUnifyLocation { -// lhs: SrcRange, -// rhs: SrcRange, -// stack_position: usize, -// } - -// impl Ty { - -// pub fn unify(&self, other: Self, location: TyUnifyLocation) -> Result { -// let both = self.ty_set.intersection(other.ty_set); -// if both.is_empty() { -// Err(TypeError::TyUnifyEmpty { -// lhs: self.clone(), -// rhs: other, -// location: location, -// }) -// } else { -// Ok(Ty { -// ty_set: both -// }) -// } -// } -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct StackTy { -// src_range: SrcRange, -// in_type: Vec, -// out_type: Vec, -// } - -// impl StackTy { -// // pub fn zip_extend_with(xs: Vec, ys: Vec, fl: Fn(T) -> T, f: Fn(T, T) - -// pub fn unify_ty_sets(xs: Vec, ys: Vec, xs_src_range: SrcRange, ys_src_range: SrcRange) -> Result, TypeError> { -// let zip_len = cmp::max(xs.len(), ys.len()); -// let xs_extended = xs.iter().map(|z|Some(z)).chain(std::iter::repeat(None).take(zip_len - xs.len())); -// let ys_extended = ys.iter().map(|z|Some(z)).chain(std::iter::repeat(None).take(zip_len - ys.len())); - -// xs_extended.zip(ys_extended).enumerate().map(|ixy| { -// match ixy.1 { -// (None, None) => Err(TypeError::StackTyUnifyNone { -// lhs: xs.clone(), -// rhs: ys.clone(), -// }), -// (Some(x), None) => Ok(*x), -// (None, Some(y)) => Ok(*y), -// (Some(x), Some(y)) => Ok(x.unify(*y, TyUnifyLocation { -// lhs: xs_src_range.clone(), -// rhs: ys_src_range.clone(), -// stack_position: ixy.0, -// })?), -// } - -// }).collect() -// } - -// pub fn diff_ty_sets(xs: Vec, ys: Vec) -> Result, TypeError> { -// xs.iter().zip(ys.iter()).map(|x, y| { -// x.difference(y) -// }.collect() -// } - -// pub fn union_ty_sets(xs: Vec, ys: Vec) -> Result, TypeError> { -// // pad lengths and zip (pad with empty) -// xs.iter().zip(ys.iter()).map(|x, y| { -// x.union(y) -// }.collect() -// } - -// pub fn unify(&self, other: Self) -> Result { -// let middle_ty = Self::unify_ty_sets(self.out_type, other.in_type, self.src_range, other.src_range); -// let self_remainder = Self::diff_ty_sets(middle_ty, self.out_type); -// let other_remainder = Self::diff_ty_sets(middle_ty, other.in_type); -// in_type: self.in_type + self_remainder -// out_type: other.out_type + other_remainder - -// StackTy { -// src_range: self.src_range.append(other.src_range)?, -// in_type Self::union_ty_sets(self.in_type, self_remainder), -// out_type Self::union_ty_sets(other.out_type, other_remainder), -// } -// } - -// pub fn infer_instruction(instruction: &Instruction, src_location: usize) -> Self { -// let instruction_ty_sets = instruction.ty_sets(); -// StackTy { -// src_range: SrcRange::singleton(src_location), -// in_type instruction_ty_sets.0, -// out_type instruction_ty_sets.1, -// } -// } - -// // pub fn infer(instructions: Instructions) -> Result { -// // instructions.iter().enumerate() -// // .map(|ix| Self::infer_instruction(ix.1, ix.0)) -// // .reduce(|memo, x| memo.unify(x)) - -// // } - -// } - - - -// #[derive(Debug, PartialEq, Error)] -// pub enum TypeError { -// #[error("Ty::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] -// TyUnifyEmpty { -// lhs: Ty, -// rhs: Ty, -// location: TyUnifyLocation, -// }, - -// // should be impossible -// #[error("StackTy::unify produced an attempt to unify None and None: lhs: {lhs:?}; rhs: {rhs:?}")] -// StackTyUnifyNone { -// lhs: Vec, -// rhs: Vec, -// }, - -// #[error("attempt to unify types of non-contiguous locations: lhs: {0:?}")] -// SrcRangeError(SrcRangeError), -// } +#[derive(Debug, PartialEq, Error)] +pub enum TypeError { + #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}")] + ContextGetUnknownTypeId { + context: Context, + index: TypeId, + }, + + #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] + ElemTypeUnifyEmpty { + lhs: ElemType, + rhs: ElemType, + // location: TyUnifyLocation, + }, + + // // should be impossible + // #[error("StackTy::unify produced an attempt to unify None and None: lhs: {lhs:?}; rhs: {rhs:?}")] + // StackTyUnifyNone { + // lhs: Vec, + // rhs: Vec, + // }, + + // #[error("attempt to unify types of non-contiguous locations: lhs: {0:?}")] + // SrcRangeError(SrcRangeError), +} // impl From for TypeError { // fn from(error: SrcRangeError) -> Self { @@ -576,3 +514,6 @@ impl Instruction { // } // } + + + From 186edc3a9bb95fc80afe4122d5de380fbf0a1a0a Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 21 Feb 2022 14:48:35 -0500 Subject: [PATCH 28/77] typing Instructions, made Instructions a struct, make type errors more verbose, pretty-printing types --- src/parse.rs | 15 +- src/types.rs | 442 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 409 insertions(+), 48 deletions(-) diff --git a/src/parse.rs b/src/parse.rs index 0bacfdc..14e16be 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -24,12 +24,15 @@ pub fn parse_json(input: &str) -> Result { } pub fn parse(input: &str) -> Result { - input - .split(';') - .map(|term| term.trim()) - .filter(|&term| !term.is_empty()) - .map(|term| parse_instruction(term)) - .collect() + Ok(Instructions { + instructions: + input + .split(';') + .map(|term| term.trim()) + .filter(|&term| !term.is_empty()) + .map(|term| parse_instruction(term)) + .collect::, ParseError>>()?, + }) } fn parse_instruction(term: &str) -> Result { diff --git a/src/types.rs b/src/types.rs index 239b230..7b5cfdd 100644 --- a/src/types.rs +++ b/src/types.rs @@ -4,6 +4,8 @@ use crate::elem::{Elem, ElemSymbol}; use std::collections::BTreeMap; use std::cmp; use std::iter::Skip; +use std::fmt; +use std::fmt::{Display, Formatter}; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; @@ -67,61 +69,194 @@ pub enum Instruction { StringToBytes, } -pub type Instructions = Vec; -// pub type Stack = Vec; +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct LineNo { + line_no: usize, +} +impl From for LineNo { + fn from(line_no: usize) -> Self { + LineNo { + line_no: line_no, + } + } +} +pub type ArgumentIndex = usize; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Location { + line_no: LineNo, + argument_index: ArgumentIndex, + is_input: bool, +} + +impl LineNo { + pub fn in_at(&self, argument_index: usize) -> Location { + Location { + line_no: *self, + argument_index: argument_index, + is_input: true, + } + } + pub fn out_at(&self, argument_index: usize) -> Location { + Location { + line_no: *self, + argument_index: argument_index, + is_input: false, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum BaseElemType { + Any, + Concat, + Index, + Slice, + ElemSymbol(ElemSymbol), +} #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemTypeInfo { + base_elem_type: BaseElemType, + location: Location, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ElemType { type_set: EnumSet, + info: Vec, +} + +// Formatting: +// ``` +// ElemType { +// type_set: {A, B, C}, +// info: _, +// } +// ``` +// +// Results in: +// ``` +// {A, B, C} +// ``` +impl Display for ElemType { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{{{}}}", + self.type_set.iter() + .fold(String::new(), + |memo, x| { + let x_str: &'static str = From::from(x); + if memo == "" { + x_str.to_string() + } else { + memo + ", " + &x_str.to_string() + } + } + )) + } +} + +#[cfg(test)] +mod elem_type_display_tests { + use super::*; + + #[test] + fn test_empty() { + let elem_type = ElemType { + type_set: EnumSet::empty(), + info: vec![], + }; + assert_eq!("{}", format!("{}", elem_type)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), format!("{}", elem_type)); + } + } + + #[test] + fn test_all() { + assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", format!("{}", ElemType::any(vec![]))); + } } impl ElemSymbol { - pub fn elem_type(&self) -> ElemType { + pub fn elem_type(&self, locations: Vec) -> ElemType { ElemType { type_set: EnumSet::only(*self), + info: locations.iter() + .map(|location| + ElemTypeInfo { + base_elem_type: BaseElemType::ElemSymbol(*self), + location: *location, + }).collect(), } } } impl Elem { - pub fn elem_type(&self) -> ElemType { - self.symbol().elem_type() + pub fn elem_type(&self, locations: Vec) -> ElemType { + self.symbol().elem_type(locations) } } impl ElemType { - pub fn any() -> Self { + pub fn any(locations: Vec) -> Self { ElemType { type_set: EnumSet::all(), + info: locations.iter() + .map(|location| + ElemTypeInfo { + base_elem_type: BaseElemType::Any, + location: *location, + }).collect(), } } - pub fn concat_type() -> Self { + pub fn concat_type(locations: Vec) -> Self { ElemType { type_set: enum_set!(ElemSymbol::Bytes | ElemSymbol::String | ElemSymbol::Array | ElemSymbol::Object), + info: locations.iter() + .map(|location| + ElemTypeInfo { + base_elem_type: BaseElemType::Concat, + location: *location, + }).collect(), } } - pub fn index_type() -> Self { + pub fn index_type(locations: Vec) -> Self { ElemType { type_set: enum_set!(ElemSymbol::Array | ElemSymbol::Object), + info: locations.iter() + .map(|location| + ElemTypeInfo { + base_elem_type: BaseElemType::Index, + location: *location, + }).collect(), } } - pub fn slice_type() -> Self { - Self::concat_type() + pub fn slice_type(locations: Vec) -> Self { + Self::concat_type(locations) } pub fn unify(&self, other: Self) -> Result { @@ -132,8 +267,11 @@ impl ElemType { rhs: other.clone(), }) } else { + let mut both_info = self.info.clone(); + both_info.append(&mut other.info.clone()); Ok(ElemType { type_set: both, + info: both_info, }) } } @@ -150,6 +288,74 @@ pub struct Context { next_type_id: TypeId, } +// Formatting: +// ``` +// Context { +// context: [ +// (t0, {A, B, C}), +// (t1, {B, C}), +// .. +// (tN, {D, E, F})], +// next_type_id: N+1, +// } +// ``` +// +// Results in: +// ``` +// ∀ (t0 ∊ {A, B, C}), +// ∀ (t1 ∊ {B, C}), +// .. +// ∀ (tN ∊ {D, E, F}), +// ``` +impl Display for Context { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{}", + self.context.iter() + .fold(String::new(), |memo, (i, xs)| { + memo + + "\n" + + &format!("∀ (t{i} ∊ {xs}),", i = i.type_id, xs = xs).to_string() + })) + } +} + +#[cfg(test)] +mod context_display_tests { + use super::*; + + #[test] + fn test_empty() { + let big_type_id = TypeId { + type_id: 2^32 + }; + let context = Context { + context: BTreeMap::new(), + next_type_id: big_type_id, + }; + assert_eq!("", format!("{}", context)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + let mut context_map = BTreeMap::new(); + context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); + let context = Context { + context: context_map, + next_type_id: TypeId { + type_id: 1, + }, + }; + assert_eq!(format!("\n∀ (t0 ∊ {}),", elem_type), format!("{}", context)); + } + } +} + impl Context { pub fn new() -> Self { Context { @@ -216,6 +422,101 @@ pub struct Type { o_type: Vec, } + +// Formatting: +// ``` +// Type { +// context: Context { +// context: [ +// (t0, {A, B, C}), +// (t1, {B, C}), +// .. +// (tN, {D, E, F})], +// next_type_id: N+1, +// }, +// i_type: [0, 1, .., N], +// 0_type: [i, j, .., k], +// } +// ``` +// +// Results in: +// ``` +// ∀ (t0 ∊ {A, B, C}), +// ∀ (t1 ∊ {B, C}), +// .. +// ∀ (tN ∊ {D, E, F}), +// [t0, t1, .., tN] -> +// [ti, tj, .., tk] +// ``` + +impl Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{context}\n[{i_type}] ->\n[{o_type}]", + context = self.context, + i_type = self.i_type.iter().fold(String::new(), |memo, x| { + let x_str = format!("t{}", x.type_id); + if memo == "" { + x_str + } else { + memo + ", " + &x_str.to_string() + }}), + o_type = self.o_type.iter().fold(String::new(), |memo, x| { + let x_str = format!("t{}", x.type_id); + if memo == "" { + x_str + } else { + memo + ", " + &x_str.to_string() + }})) + } +} + +#[cfg(test)] +mod type_display_tests { + use super::*; + + #[test] + fn test_empty() { + let big_type_id = TypeId { + type_id: 2^32 + }; + let context = Context { + context: BTreeMap::new(), + next_type_id: big_type_id, + }; + let example_type = Type { + context: context, + i_type: vec![], + o_type: vec![], + }; + assert_eq!("\n[] ->\n[]", format!("{}", example_type)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + let mut context_map = BTreeMap::new(); + context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); + let context = Context { + context: context_map, + next_type_id: TypeId { + type_id: 1, + }, + }; + let example_type = Type { + context: context, + i_type: vec![TypeId { type_id: 0 }, TypeId { type_id: 0 }], + o_type: vec![TypeId { type_id: 0 }], + }; + assert_eq!(format!("\n∀ (t0 ∊ {}),\n[t0, t0] ->\n[t0]", elem_type), format!("{}", example_type)); + } + } +} + impl Type { // check whether all the TypeId's are valid pub fn is_valid(&self) -> bool { @@ -227,7 +528,6 @@ impl Type { // TODO: // - make method to simplify context - // - pretty-print Type // TODO: this is next, figure out how to represent/implement replacements of // variables, e.g. starting larger than both or collecting maps (old_lhs -> new_lhs) @@ -304,10 +604,11 @@ impl Type { } impl Restack { - pub fn type_of(&self) -> Result { + // TODO: fix locations + pub fn type_of(&self, line_no: LineNo) -> Result { let mut context = Context::new(); let mut restack_type: Vec = (0..self.restack_depth) - .map(|_x| context.push(ElemType::any())) + .map(|x| context.push(ElemType::any(vec![line_no.in_at(x)]))) .collect(); Ok(Type { context: context, @@ -332,13 +633,18 @@ impl Restack { /// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] /// StringToBytes, // [ string ] -> [ bytes ] impl Instruction { - pub fn type_of(&self) -> Result { + pub fn type_of(&self, line_no: LineNo) -> Result { match self { - Instruction::Restack(restack) => Ok(restack.type_of()?), + Instruction::Restack(restack) => + Ok(restack + .type_of(line_no) + .or_else(|e| Err(TypeError::InstructionTypeOfRestack(e)))?), Instruction::AssertTrue => { let mut context = Context::new(); - let bool_var = context.push(ElemSymbol::Bool.elem_type()); + let bool_var = context + .push(ElemSymbol::Bool + .elem_type(vec![line_no.in_at(0)])); Ok(Type { context: context, i_type: vec![bool_var], @@ -348,7 +654,8 @@ impl Instruction { Instruction::Push(elem) => { let mut context = Context::new(); - let elem_var = context.push(elem.elem_type()); + let elem_var = context + .push(elem.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![], @@ -358,7 +665,7 @@ impl Instruction { Instruction::HashSha256 => { let mut context = Context::new(); - let bytes_var = context.push(ElemSymbol::Bytes.elem_type()); + let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.in_at(0), line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![bytes_var], @@ -368,8 +675,8 @@ impl Instruction { Instruction::ToJson => { let mut context = Context::new(); - let any_var = context.push(ElemType::any()); - let json_var = context.push(ElemSymbol::Json.elem_type()); + let any_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![any_var], @@ -379,8 +686,8 @@ impl Instruction { Instruction::StringToBytes => { let mut context = Context::new(); - let string_var = context.push(ElemSymbol::String.elem_type()); - let bytes_var = context.push(ElemSymbol::Bytes.elem_type()); + let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); + let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![string_var], @@ -390,8 +697,8 @@ impl Instruction { Instruction::UnpackJson(elem_symbol) => { let mut context = Context::new(); - let json_var = context.push(ElemSymbol::Json.elem_type()); - let elem_symbol_var = context.push(elem_symbol.elem_type()); + let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.in_at(0)])); + let elem_symbol_var = context.push(elem_symbol.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![json_var], @@ -401,9 +708,9 @@ impl Instruction { Instruction::CheckLe => { let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any()); - let any_rhs_var = context.push(ElemType::any()); - let bool_var = context.push(ElemSymbol::Bool.elem_type()); + let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); + let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![any_lhs_var, any_rhs_var], @@ -413,9 +720,9 @@ impl Instruction { Instruction::CheckLt => { let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any()); - let any_rhs_var = context.push(ElemType::any()); - let bool_var = context.push(ElemSymbol::Bool.elem_type()); + let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); + let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![any_lhs_var, any_rhs_var], @@ -425,9 +732,9 @@ impl Instruction { Instruction::CheckEq => { let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any()); - let any_rhs_var = context.push(ElemType::any()); - let bool_var = context.push(ElemSymbol::Bool.elem_type()); + let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); + let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![any_lhs_var, any_rhs_var], @@ -437,7 +744,7 @@ impl Instruction { Instruction::Concat => { let mut context = Context::new(); - let concat_var = context.push(ElemType::concat_type()); + let concat_var = context.push(ElemType::concat_type(vec![line_no.in_at(0), line_no.in_at(1), line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![concat_var, concat_var], @@ -447,8 +754,8 @@ impl Instruction { Instruction::Index => { let mut context = Context::new(); - let number_var = context.push(ElemSymbol::Number.elem_type()); - let index_var = context.push(ElemType::index_type()); + let number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); + let index_var = context.push(ElemType::index_type(vec![line_no.in_at(1), line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![number_var, index_var], @@ -458,8 +765,8 @@ impl Instruction { Instruction::Lookup => { let mut context = Context::new(); - let string_var = context.push(ElemSymbol::String.elem_type()); - let object_var = context.push(ElemSymbol::Object.elem_type()); + let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); + let object_var = context.push(ElemSymbol::Object.elem_type(vec![line_no.in_at(1), line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![string_var, object_var], @@ -469,16 +776,19 @@ impl Instruction { Instruction::Slice => { let mut context = Context::new(); - let offset_number_var = context.push(ElemSymbol::Number.elem_type()); - let length_number_var = context.push(ElemSymbol::Number.elem_type()); - let slice_var = context.push(ElemType::slice_type()); + let offset_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); + let length_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(1)])); + let slice_var = context.push(ElemType::slice_type(vec![line_no.in_at(2), line_no.out_at(0)])); Ok(Type { context: context, i_type: vec![offset_number_var, length_number_var, slice_var], o_type: vec![slice_var], }) }, - } + }.or_else(|e| Err(TypeError::InstructionTypeOfDetail { + instruction: self.clone(), + error: Box::new(e), + })) } } @@ -490,6 +800,15 @@ pub enum TypeError { index: TypeId, }, + #[error("Instruction::type_of resulted in restack error: {0:?}")] + InstructionTypeOfRestack(RestackError), + + #[error("Instruction::type_of resulted in an error involving: {instruction:?};\n {error:?}")] + InstructionTypeOfDetail { + instruction: Instruction, + error: Box, + }, + #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] ElemTypeUnifyEmpty { lhs: ElemType, @@ -497,6 +816,16 @@ pub enum TypeError { // location: TyUnifyLocation, }, + #[error("Instructions::type_of called on an empty Vec of Instruction's")] + InstructionsTypeOfEmpty, + + #[error("Instructions::type_of resulted in an error on line: {line_no:?};\n {error:?}")] + InstructionsTypeOfLineNo { + line_no: usize, + error: Box, + }, + + // // should be impossible // #[error("StackTy::unify produced an attempt to unify None and None: lhs: {lhs:?}; rhs: {rhs:?}")] // StackTyUnifyNone { @@ -515,5 +844,34 @@ pub enum TypeError { // } +// pub type Stack = Vec; +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub struct Instructions { + pub instructions: Vec, +} + +impl IntoIterator for Instructions { + type Item = Instruction; + type IntoIter = as std::iter::IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.instructions.into_iter() + } +} +impl Instructions { + pub fn type_of(&self) -> Result { + let mut instructions = self.instructions.clone(); + let first_instruction = instructions.pop().ok_or_else(|| TypeError::InstructionsTypeOfEmpty)?; + let mut current_type = first_instruction.type_of(From::from(0))?; + for (i, instruction) in instructions.iter().enumerate() { + current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) + .or_else(|e| Err(TypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location + line_no: i, + error: Box::new(e), + }))?; + } + Ok(current_type) + } +} From a2dbfbb72fff1ad72807da454bc9bf7af0465624 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 22 Feb 2022 16:52:54 -0500 Subject: [PATCH 29/77] types work for three instruction example with new unification algorithm --- src/main.rs | 258 ++++++++++---------- src/types.rs | 651 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 646 insertions(+), 263 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0f3e798..fa219d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ mod tests { fn main() { - let input_json = r#" + let _input_json = r#" { "queries": [ { @@ -69,138 +69,150 @@ fn main() { "#; // let json_instructions = parse_json()" - let instructions: Instructions = vec![ - - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Restack(Restack::dup()), - - // x["queries"] - Instruction::Push(Elem::String("queries".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Array), - - // x[0] - Instruction::Push(Elem::Number(From::from(0u8))), - Instruction::Index, - Instruction::UnpackJson(ElemSymbol::Object), - - // x["action"] = "tokenbalance" - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("action".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::Push(Elem::String("tokenbalance".to_string())), - Instruction::CheckEq, + let instructions_vec: Vec = vec![ + Instruction::Push(Elem::Bool(true)), + Instruction::Restack(Restack::id()), Instruction::AssertTrue, - // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("contractaddress".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), - Instruction::CheckEq, - Instruction::AssertTrue, - - // x["response"]["result"] = "135499" - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("response".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Push(Elem::String("result".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::Push(Elem::String("135499".to_string())), - Instruction::CheckEq, - Instruction::AssertTrue, - - // x["prompts"] - Instruction::Restack(Restack::drop()), - Instruction::Push(Elem::String("prompts".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Array), - - // x[0] - Instruction::Push(Elem::Number(From::from(0u8))), - Instruction::Index, - Instruction::UnpackJson(ElemSymbol::Object), - - // x["action"] = "siwe" - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("action".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::Push(Elem::String("siwe".to_string())), - Instruction::CheckEq, - Instruction::AssertTrue, - - // x["version"] = "1.1.0" - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("version".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::Push(Elem::String("1.1.0".to_string())), - Instruction::CheckEq, - Instruction::AssertTrue, - - // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("data".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Push(Elem::String("fields".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Push(Elem::String("address".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), - Instruction::CheckEq, - Instruction::AssertTrue, - - // sha256(x["data"]["message"]) - Instruction::Restack(Restack::dup()), - Instruction::Push(Elem::String("data".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Push(Elem::String("message".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::StringToBytes, - Instruction::HashSha256, - - // sha256(x["data"]["fields"]["address"]) - Instruction::Restack(Restack::swap()), - Instruction::Push(Elem::String("data".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Push(Elem::String("fields".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::Object), - Instruction::Push(Elem::String("address".to_string())), - Instruction::Lookup, - Instruction::UnpackJson(ElemSymbol::String), - Instruction::StringToBytes, - Instruction::HashSha256, - - // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = - // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] - Instruction::Concat, - Instruction::HashSha256, - Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), - Instruction::CheckEq, - Instruction::AssertTrue, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Restack(Restack::dup()), + + // // x["queries"] + // Instruction::Push(Elem::String("queries".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Array), + + // // x[0] + // Instruction::Push(Elem::Number(From::from(0u8))), + // Instruction::Index, + // Instruction::UnpackJson(ElemSymbol::Object), + + // // x["action"] = "tokenbalance" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("action".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("tokenbalance".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("contractaddress".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["response"]["result"] = "135499" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("response".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("result".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("135499".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["prompts"] + // Instruction::Restack(Restack::drop()), + // Instruction::Push(Elem::String("prompts".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Array), + + // // x[0] + // Instruction::Push(Elem::Number(From::from(0u8))), + // Instruction::Index, + // Instruction::UnpackJson(ElemSymbol::Object), + + // // x["action"] = "siwe" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("action".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("siwe".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["version"] = "1.1.0" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("version".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("1.1.0".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("data".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("fields".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("address".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // sha256(x["data"]["message"]) + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("data".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("message".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::StringToBytes, + // Instruction::HashSha256, + + // // sha256(x["data"]["fields"]["address"]) + // Instruction::Restack(Restack::swap()), + // Instruction::Push(Elem::String("data".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("fields".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("address".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::StringToBytes, + // Instruction::HashSha256, + + // // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = + // // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] + // Instruction::Concat, + // Instruction::HashSha256, + // Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), + // Instruction::CheckEq, + // Instruction::AssertTrue, ]; + let instructions = Instructions { + instructions: instructions_vec, + }; let json_instructions = serde_json::to_string_pretty(&serde_json::to_value(instructions.clone()).unwrap()).unwrap(); assert_eq!(parse_json(&json_instructions).unwrap(), instructions); - let mut exec = Executor::default(); - exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); + // let mut exec = Executor::default(); + // exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); /* exec.consume(instructions) */ /* .expect("error processing instructions"); */ /* println!("FINAL STACK"); */ - println!("{:?}", exec); + // println!("{:?}", exec); + println!(""); + + match instructions.type_of() { + Ok(r) => println!("\nfinal type:\n{}", r), + Err(e) => println!("{}", e), + } } diff --git a/src/types.rs b/src/types.rs index 7b5cfdd..32d4d59 100644 --- a/src/types.rs +++ b/src/types.rs @@ -197,10 +197,10 @@ impl ElemSymbol { ElemType { type_set: EnumSet::only(*self), info: locations.iter() - .map(|location| + .map(|&location| ElemTypeInfo { base_elem_type: BaseElemType::ElemSymbol(*self), - location: *location, + location: location, }).collect(), } } @@ -217,10 +217,10 @@ impl ElemType { ElemType { type_set: EnumSet::all(), info: locations.iter() - .map(|location| + .map(|&location| ElemTypeInfo { base_elem_type: BaseElemType::Any, - location: *location, + location: location, }).collect(), } } @@ -233,10 +233,10 @@ impl ElemType { ElemSymbol::Array | ElemSymbol::Object), info: locations.iter() - .map(|location| + .map(|&location| ElemTypeInfo { base_elem_type: BaseElemType::Concat, - location: *location, + location: location, }).collect(), } } @@ -247,10 +247,10 @@ impl ElemType { enum_set!(ElemSymbol::Array | ElemSymbol::Object), info: locations.iter() - .map(|location| + .map(|&location| ElemTypeInfo { base_elem_type: BaseElemType::Index, - location: *location, + location: location, }).collect(), } } @@ -259,10 +259,10 @@ impl ElemType { Self::concat_type(locations) } - pub fn unify(&self, other: Self) -> Result { + pub fn unify(&self, other: Self) -> Result { let both = self.type_set.intersection(other.type_set); if both.is_empty() { - Err(TypeError::ElemTypeUnifyEmpty { + Err(ElemTypeError::UnifyEmpty { lhs: self.clone(), rhs: other.clone(), }) @@ -282,6 +282,24 @@ pub struct TypeId { type_id: usize, } +impl TypeId { + // TODO: test by checking: + // xs.map(TypeId).fold(x, offset) = TypeId(xs.fold(x, +)) + pub fn offset(&self, offset: TypeId) -> Self { + TypeId { + type_id: self.type_id + offset.type_id, + } + } + + pub fn update_type_id(&self, from: Self, to: Self) -> Self { + if *self == from { + to + } else { + *self + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Context { context: BTreeMap, @@ -357,6 +375,50 @@ mod context_display_tests { } impl Context { + // TODO: simplify/normalize context + // Produce a version where (basis = [..]) are the first (0..) + // TypeId's and the rest are sorted from the original Context + // + // Also output a variable remapper + // + // pub fn normalize(&self, basis: I) -> Result + // where + // I: IntoIter + // { + // let mut source = self.clone(); + // let mut result = Self::new(); + // for type_id in basis { + // let elem_type = source.get(type_id)? + // result.push(elem_type); + // _ + // } + + // // TODO: deprecated + // pub fn new_max(&self, other: Self) -> Self { + // Context { + // context: BTreeMap::new(), + // next_type_id: TypeId { + // type_id: + // cmp::max(self.next_type_id.type_id, + // other.next_type_id.type_id), + // }, + // } + // } + + // map is from other to result + // pub fn disjoint_union(&mut self, other: Self) -> Result { + // let mut type_map = &TypeIdMap::new(); + // for (type_id, elem_type) in other.context.iter() { + // type_map.push(*type_id, self.push(elem_type.clone())) + // .or_else(|e| Err(ContextError::DisjointUnion { + // lhs: self.clone(), + // rhs: other, + // error: e, + // }))? + // } + // Ok(*type_map) + // } + pub fn new() -> Self { Context { context: BTreeMap::new(), @@ -366,17 +428,6 @@ impl Context { } } - pub fn new_max(&self, other: Self) -> Self { - Context { - context: BTreeMap::new(), - next_type_id: TypeId { - type_id: - cmp::max(self.next_type_id.type_id, - other.next_type_id.type_id), - }, - } - } - pub fn is_valid(&self) -> bool { !self.context.keys().any(|x| *x >= self.next_type_id) } @@ -394,22 +445,95 @@ impl Context { push_id } - pub fn get(&mut self, index: &TypeId) -> Result { - Ok(self.context.get(index).ok_or_else(|| TypeError::ContextGetUnknownTypeId { + pub fn offset(&self, offset: TypeId) -> Self { + Context { + context: self.context.iter().map(|(k, x)| (k.offset(offset), x.clone())).collect(), + next_type_id: self.next_type_id.offset(offset), + } + } + + pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), ContextError> { + if self.context.contains_key(&from) { + Ok(()) + } else { + Err(ContextError::UpdateTypeIdFromMissing { + from: from, + to: to, + context: self.clone(), + }) + }?; + if self.context.contains_key(&to) { + Err(ContextError::UpdateTypeIdToPresent { + from: from, + to: to, + context: self.clone(), + }) + } else { + Ok(()) + }?; + self.context = self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(); + self.next_type_id = cmp::max(self.next_type_id, to); + // Ok(Context { + // context: self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(), + // next_type_id: cmp::max(self.next_type_id, to), + // }) + Ok(()) + } + + // fail iff not disjoint iff intersection non-empty + pub fn disjoint_union(&mut self, other: Self) -> Result<(), ContextError> { + for (&type_id, elem_type) in other.context.iter() { + match self.context.insert(type_id, elem_type.clone()) { + None => { + Ok(()) + }, + Some(conflicting_elem_type) => Err(ContextError::DisjointUnion { + type_id: type_id, + elem_type: elem_type.clone(), + conflicting_elem_type: conflicting_elem_type, + lhs: self.clone(), + rhs: other.clone(), + }), + }? + } + self.next_type_id = cmp::max(self.next_type_id, other.next_type_id); + Ok(()) + } + + pub fn get(&mut self, index: &TypeId, error: &dyn Fn() -> ContextError) -> Result { + Ok(self.context.get(index).ok_or_else(|| ContextError::GetUnknownTypeId { context: self.clone(), index: *index, + error: Box::new(error()), })?.clone()) } - // pub fn push_unified(&mut self, xs: Self, ys: Self, xi: TypeId, yi: TypeId) -> Self { - // _ - // } + // unify the types of two TypeId's into the rhs + // removing the lhs + pub fn unify(&mut self, xi: TypeId, yi: TypeId) -> Result<(), ContextError> { + let x_type = self.context.remove(&xi).ok_or_else(|| ContextError::Unify { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + is_lhs: true, + })?; + + let y_type = self.context.remove(&yi).ok_or_else(|| ContextError::Unify { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + is_lhs: false, + })?; + + let xy_type = x_type.unify(y_type).or_else(|e| Err(ContextError::UnifyElemType { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + error: e, + }))?; - // TODO: remove mut xs/ys - pub fn unify(&mut self, mut xs: Self, mut ys: Self, xi: &TypeId, yi: &TypeId) -> Result { - let x_type = xs.get(xi)?; - let y_type = ys.get(yi)?; - Ok(self.push(x_type.unify(y_type)?)) + self.context.insert(yi, xy_type); + Ok(()) } } @@ -423,6 +547,212 @@ pub struct Type { } + +impl Type { + pub fn id() -> Self { + Type { + context: Context::new(), + i_type: vec![], + o_type: vec![], + } + } + + // check whether all the TypeId's are valid + pub fn is_valid(&self) -> bool { + let next_type_id = self.context.next_type_id; + self.context.is_valid() && + !(self.i_type.iter().any(|x| *x >= next_type_id) || + self.o_type.iter().any(|x| *x >= next_type_id)) + } + + pub fn offset(&self, offset: TypeId) -> Self { + Type { + context: self.context.offset(offset), + i_type: self.i_type.iter().map(|x| x.offset(offset)).collect(), + o_type: self.o_type.iter().map(|x| x.offset(offset)).collect(), + } + } + + pub fn next_type_id(&self) -> TypeId { + self.context.next_type_id + } + + pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeError> { + self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?; + self.i_type = self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(); + self.o_type = self.o_type.iter().map(|x| x.update_type_id(from, to)).collect(); + // Ok(Type { + // context: self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?, + // i_type: self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(), + // o_type: self.o_type.iter().map(|x| x.update_type_id(from, to)).collect(), + // }) + Ok(()) + } + + // f : self + // g : other + // self.compose(other) : (f ++ g).type_of() + // + // input -> + // other.i_type + // other.o_type + // self.i_type + // self.o_type + // -> output + // + // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context + // 2. collect the remainder and add them to the context + // 3. add the remainder to (self.i_type, other.o_type), with replaced variables + pub fn compose(&self, other: Self) -> Result { + println!(""); + println!("composing:\n{0}\n\nAND\n{1}\n", self, other); + + let mut context = self.context.clone(); + println!("context: {}", context); + println!("context.next_type_id: {:?}", context.next_type_id.type_id); + + let offset_other = other.offset(self.next_type_id()); + println!("offset_other: {}", offset_other); + + context.disjoint_union(offset_other.context.clone()) + .map_err(|e| TypeError::ContextError(e))?; + println!("context union: {}", context); + + let mut mut_offset_other = offset_other.clone(); + let mut zip_len = 0; + let other_o_type = offset_other.o_type.iter().clone(); + let self_i_type = self.i_type.iter().clone(); + other_o_type.zip(self_i_type).try_for_each(|(&o_type, &i_type)| { + zip_len += 1; + context + .unify(o_type, i_type) + .map_err(|e| TypeError::ContextError(e))?; + mut_offset_other + .update_type_id(o_type, i_type)?; + Ok(()) + })?; + + Ok(Type { + context: context, + i_type: mut_offset_other.i_type.iter().chain(self.i_type.iter().skip(zip_len)).copied().collect(), + o_type: self.o_type.iter().chain(mut_offset_other.o_type.iter().skip(zip_len)).copied().collect(), + }) + + + + // let mut offset_other = other.offset(self.next_type_id()).clone(); + // context.disjoint_union(offset_other.context.clone()) + // .map_err(|e| TypeError::ContextError(e))?; + + // let mut other_o_type = offset_other.o_type.iter().clone(); + // let mut self_i_type = self.i_type.iter().clone(); + // other_o_type.by_ref().zip(self_i_type.by_ref()).try_for_each(|(&o_type, &i_type)| { + // context + // .unify(o_type, i_type) + // .map_err(|e| TypeError::ContextError(e))?; + // offset_other + // .update_type_id(o_type, i_type)?; + // Ok(()) + // })?; + + // Ok(Type { + // context: context, + // i_type: offset_other.i_type.iter().chain(self_i_type).copied().collect(), + // o_type: self.o_type.iter().chain(other_o_type).copied().collect(), + // }) + } + + + // other_o_type.zip(self_i_type).enumerate().try_for_each(|(i, (&o_type, &i_type))| { + + // self_to_context.push(*i_type, *o_type).or_else(|_| Ok(()))?; + // context.unify(other_to_context.get(o_type, i)?, *i_type) + + // let mut i_type_result = offset_other.i_type.clone(); + // let mut o_type_result = self.o_type.clone(); + + // for (i, &o_type) in other_o_type.enumerate() { + // o_type_result.push(o_type) + // } + + // let other_to_context = context.disjoint_union(other.context) + // .map_err(|e| TypeError::ComposeDisjointUnion(e))?; + // let self_to_context = TypeIdMap::new(); + + + // let self_context = &self.context; + // let other_context = &other.context; + + // let mut context = self_context.clone().new_max(other_context.clone()); + // let mut self_type_map = TypeIdMap::new(); + // let mut other_type_map = TypeIdMap::new(); + + // let mut i_type = vec![]; + // let mut o_type = vec![]; + + // other.o_type.iter().zip(self.i_type.clone()).try_for_each(|(o_type, i_type)| { + // let new_type_id = context + // .unify(self_context.clone(), + // other_context.clone(), + // &i_type, + // &o_type)?; + // self_type_map.push(i_type, new_type_id)?; + // other_type_map.push(*o_type, new_type_id)?; + // Ok(()) + // })?; + + // TODO: replace with context merging + // match after_zip(other.o_type.clone(), self.i_type.clone()) { + // Ok(other_o_type_remainder) => + // for o_type in other_o_type_remainder { + // let new_o_type = context.push(other.context.clone().get(&o_type)?); + // other_type_map.push(o_type.clone(), new_o_type)?; + // i_type.push(new_o_type.clone()); + // }, + // Err(self_i_type_remainder) => + // for i_type in self_i_type_remainder { + // let new_i_type = context.push(self.context.clone().get(&i_type)?); + // self_type_map.push(i_type.clone(), new_i_type)?; + // o_type.push(new_i_type.clone()); + // }, + // } + + // let mut i_type_prefix = other_type_map.run(other.i_type.clone())?; + // let mut o_type_prefix = self_type_map.run(self.o_type.clone())?; + // i_type_prefix.append(&mut i_type); + // o_type_prefix.append(&mut o_type); + // Ok(Type { + // context: context.clone(), + // i_type: i_type_prefix, + // // .iter().chain(i_type.iter()).collect(), + // o_type: o_type_prefix, + // // o_type: .iter().chain(o_type.iter()).collect(), + + // // i_type: other_type_map.run(other.i_type.clone())?.iter().chain(i_type.iter()).collect(), + // // o_type: self_type_map.run(self.o_type.clone())?.iter().chain(o_type.iter()).collect(), + + // // other.i_type.clone().iter() + // // .map(|x| Ok(other_type_map + // // .get(x) + // // .ok_or_else(|| TypeError::ContextRemapUnknownTypeId { + // // context: context.clone(), + // // type_map: other_type_map.clone(), + // // index: *x, + // // })?.clone())).chain(i_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, + + // // o_type: self.o_type.clone().iter() + // // .map(|x| Ok(self_type_map + // // .get(x) + // // .ok_or_else(|| TypeError::ContextRemapUnknownTypeId { + // // context: context.clone(), + // // type_map: self_type_map.clone(), + // // index: *x, + // // })?.clone())).chain(o_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, + // }) + +} + + // Formatting: // ``` // Type { @@ -448,7 +778,6 @@ pub struct Type { // [t0, t1, .., tN] -> // [ti, tj, .., tk] // ``` - impl Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, @@ -517,94 +846,14 @@ mod type_display_tests { } } -impl Type { - // check whether all the TypeId's are valid - pub fn is_valid(&self) -> bool { - let next_type_id = self.context.next_type_id; - self.context.is_valid() && - !(self.i_type.iter().any(|x| *x >= next_type_id) || - self.o_type.iter().any(|x| *x >= next_type_id)) - } - // TODO: - // - make method to simplify context - // TODO: this is next, figure out how to represent/implement replacements of - // variables, e.g. starting larger than both or collecting maps (old_lhs -> new_lhs) - // f : self - // g : other - // self.compose(other) : (f ++ g).type_of() - // - // input -> - // other.i_type - // other.o_type - // self.i_type - // self.o_type - // -> output - // - // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context - // 2. collect the remainder and add them to the context - // 3. add the remainder to (self.i_type, other.o_type), with replaced variables - pub fn compose(&self, other: Self) -> Result { - let self_context = &self.context; - let other_context = &other.context; - - let mut context = self_context.clone().new_max(other_context.clone()); - let mut self_type_map: BTreeMap = BTreeMap::new(); - let mut other_type_map: BTreeMap = BTreeMap::new(); - - let mut i_type = vec![]; - let mut o_type = vec![]; - - other.o_type.iter().zip(self.i_type.clone()).try_for_each(|(o_type, i_type)| { - let new_type_id = context - .unify(self_context.clone(), - other_context.clone(), - &i_type, - &o_type)?; - self_type_map.insert(i_type, new_type_id); - other_type_map.insert(*o_type, new_type_id); - Ok(()) - })?; - match after_zip(other.o_type.clone(), self.i_type.clone()) { - Ok(other_o_type_remainder) => - for o_type in other_o_type_remainder { - let new_o_type = context.push(other.context.clone().get(&o_type)?); - other_type_map.insert(o_type.clone(), new_o_type); - i_type.push(new_o_type.clone()); - }, - Err(self_i_type_remainder) => - for i_type in self_i_type_remainder { - let new_i_type = context.push(self.context.clone().get(&i_type)?); - self_type_map.insert(i_type.clone(), new_i_type); - o_type.push(new_i_type.clone()); - }, - } - Ok(Type { - context: context, - i_type: other.i_type.clone().iter() - .map(move |x| Ok(other_type_map - .get(x) - .ok_or_else(|| TypeError::ContextGetUnknownTypeId { // TODO: new error - context: other.context.clone(), - index: *x, - })?.clone())).chain(i_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, - o_type: self.o_type.clone().iter() - .map(|x| Ok(self_type_map - .get(x) - .ok_or_else(|| TypeError::ContextGetUnknownTypeId { - context: self.context.clone(), - index: *x, - })?.clone())).chain(o_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, - }) - } -} impl Restack { - // TODO: fix locations + // TODO: fix locations: out locations are mislabeled as in locations pub fn type_of(&self, line_no: LineNo) -> Result { let mut context = Context::new(); let mut restack_type: Vec = (0..self.restack_depth) @@ -792,14 +1041,108 @@ impl Instruction { } } +// TODO: split up TypeError +// TODO: add layers of detail to TypeIdMapGetUnknownTypeId + + #[derive(Debug, PartialEq, Error)] -pub enum TypeError { - #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}")] - ContextGetUnknownTypeId { +pub enum ElemTypeError { + #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] + UnifyEmpty { + lhs: ElemType, + rhs: ElemType, + // location: TyUnifyLocation, + }, +} + +// #[derive(Debug, PartialEq, Error)] +// pub enum TypeIdMapError { +// #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] +// GetUnknownTypeId { +// index: TypeId, +// location: usize, +// type_map: TypeIdMap, +// }, + +// #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] +// PushExists { +// from: TypeId, +// to: TypeId, +// map: TypeIdMap, +// }, +// } + +#[derive(Debug, PartialEq, Error)] +pub enum ContextError { + #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}, error: {error:?}")] + GetUnknownTypeId { context: Context, index: TypeId, + error: Box, + }, + + #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, / + with type_id: {type_id:?}, and elem_type: {elem_type:?}, conflicted / + with lhs entry conflicting_elem_type: {conflicting_elem_type:?}")] + DisjointUnion { + type_id: TypeId, + elem_type: ElemType, + conflicting_elem_type: ElemType, + lhs: Context, + rhs: Context, }, + // #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, resulted in impossible TypeIdMapError: {error:?}")] + // DisjointUnion { + // lhs: Context, + // rhs: Context, + // error: TypeIdMapError, + // }, + + #[error("Context::update_type_id called on missing 'from: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] + UpdateTypeIdFromMissing { + from: TypeId, + to: TypeId, + context: Context, + }, + + #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] + UpdateTypeIdToPresent { + from: TypeId, + to: TypeId, + context: Context, + }, + + #[error("Context::unify failed:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n is_lhs: {is_lhs:?}\n")] + Unify { + xs: Context, + xi: TypeId, + yi: TypeId, + is_lhs: bool, + }, + + #[error("Context::unify failed to unify ElemType's:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n elem_error: {error:?}\n")] + UnifyElemType { + xs: Context, + xi: TypeId, + yi: TypeId, + error: ElemTypeError, + }, +} + + +#[derive(Debug, PartialEq, Error)] +pub enum TypeError { + #[error("ContextError {0}")] + ContextError(ContextError), + + #[error("TypeError::update_type_id failed when updating the Context: {0}")] + UpdateTypeId(ContextError), + + #[error("TypeError::compose disjoint_union {0}")] + ComposeDisjointUnion(ContextError), + + #[error("Instruction::type_of resulted in restack error: {0:?}")] InstructionTypeOfRestack(RestackError), @@ -809,13 +1152,6 @@ pub enum TypeError { error: Box, }, - #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] - ElemTypeUnifyEmpty { - lhs: ElemType, - rhs: ElemType, - // location: TyUnifyLocation, - }, - #[error("Instructions::type_of called on an empty Vec of Instruction's")] InstructionsTypeOfEmpty, @@ -825,21 +1161,13 @@ pub enum TypeError { error: Box, }, - - // // should be impossible - // #[error("StackTy::unify produced an attempt to unify None and None: lhs: {lhs:?}; rhs: {rhs:?}")] - // StackTyUnifyNone { - // lhs: Vec, - // rhs: Vec, - // }, - - // #[error("attempt to unify types of non-contiguous locations: lhs: {0:?}")] - // SrcRangeError(SrcRangeError), + // #[error("applying TypeIdMap failed: {0:?}")] + // TypeIdMapError(TypeIdMapError), } -// impl From for TypeError { -// fn from(error: SrcRangeError) -> Self { -// Self::SrcRangeError(error) +// impl From for TypeError { +// fn from(error: TypeIdMapError) -> Self { +// Self::TypeIdMapError(error) // } // } @@ -861,17 +1189,60 @@ impl IntoIterator for Instructions { impl Instructions { pub fn type_of(&self) -> Result { - let mut instructions = self.instructions.clone(); - let first_instruction = instructions.pop().ok_or_else(|| TypeError::InstructionsTypeOfEmpty)?; - let mut current_type = first_instruction.type_of(From::from(0))?; - for (i, instruction) in instructions.iter().enumerate() { + let mut current_type = Type::id(); + for (i, instruction) in self.instructions.iter().enumerate() { current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) .or_else(|e| Err(TypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location line_no: i, error: Box::new(e), }))?; + + println!("line {i}: {current_type}", i = i, current_type = current_type); } Ok(current_type) } } + +// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +// pub struct TypeIdMap { +// map: BTreeMap, +// } + + +// impl TypeIdMap { +// pub fn new() -> Self { +// TypeIdMap { +// map: BTreeMap::new(), +// } +// } + +// pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { +// if self.map.contains_key(&from) { +// Err(TypeIdMapError::PushExists { +// from: from, +// to: to, +// map: self.clone(), +// }) +// } else { +// self.map.insert(from, to); +// Ok(()) +// } +// } + +// pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { +// self.map.get(index) +// .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { +// index: index.clone(), +// location: location, +// type_map: self.clone(), +// }) +// } + +// pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { +// type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() +// } +// } + + + From a2293f89cf26b5ff99ecfda377ed873f7cd388cc Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 22 Feb 2022 17:58:23 -0500 Subject: [PATCH 30/77] now typing [push(json), unpack_to(object), dup] with silent eq error --- src/main.rs | 16 +- src/types.rs | 488 ++++++++++++++++++--------------------------------- 2 files changed, 178 insertions(+), 326 deletions(-) diff --git a/src/main.rs b/src/main.rs index fa219d8..9ff6580 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,14 +70,18 @@ fn main() { // let json_instructions = parse_json()" let instructions_vec: Vec = vec![ - Instruction::Push(Elem::Bool(true)), - Instruction::Restack(Restack::id()), - Instruction::AssertTrue, + // TEST #1 + // Instruction::Push(Elem::Bool(true)), + // Instruction::Restack(Restack::id()), + // Instruction::AssertTrue, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Restack(Restack::dup()), + // FOR DEBUGGING TYPER + Instruction::Push(Elem::Json(Default::default())), + + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Restack(Restack::dup()), - // // x["queries"] + // x["queries"] // Instruction::Push(Elem::String("queries".to_string())), // Instruction::Lookup, // Instruction::UnpackJson(ElemSymbol::Array), diff --git a/src/types.rs b/src/types.rs index 32d4d59..8ef57a3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,7 +3,6 @@ use crate::elem::{Elem, ElemSymbol}; use std::collections::BTreeMap; use std::cmp; -use std::iter::Skip; use std::fmt; use std::fmt::{Display, Formatter}; @@ -11,46 +10,14 @@ use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; use thiserror::Error; - -// TODO: relocate -pub fn after_zip(a: A, b: B) -> Result::IntoIter>, Skip<::IntoIter>> -where - A: IntoIterator, - B: IntoIterator, - ::IntoIter: ExactSizeIterator, - ::IntoIter: ExactSizeIterator, -{ - let a_iter = a.into_iter(); - let b_iter = b.into_iter(); - let max_len = cmp::max(a_iter.len(), b_iter.len()); - if max_len == a_iter.len() { - Ok(a_iter.skip(b_iter.len())) - } else { - Err(b_iter.skip(a_iter.len())) - } -} - -// Typing Overview: -// - calculate the number of in/out stack elements per instruction -// + most consume 0..2 and produce one input -// + exceptions are restack and assert_true -// - trace the stack type variables through the execution -// + [ instruction ] -> [ (instruction, [stack_variable]) ], num_stack_variables -// + map from type_var -> [ (instruction_location, (instruction), stack_location) ] -// * instruction may/may-not be needed here -// * stack_location differentiates between e.g. index number and iterable -// + convert to a list of constraints -// + resolve the list of constraints to a single type - // typing: -// - inference -// - checking against inferred or other type (this + inference = bidirecitonal) // - unification +// + inference +// + checking against inferred or other type (this + inference = bidirecitonal) // - two categories of tests: // + property tests for typing methods themselves // + test that a function having a particular type -> it runs w/o type errors on such inputs - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub enum Instruction { Push(Elem), @@ -69,8 +36,6 @@ pub enum Instruction { StringToBytes, } - - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct LineNo { line_no: usize, @@ -182,13 +147,15 @@ mod elem_type_display_tests { type_set: EnumSet::only(elem_symbol), info: vec![], }; - assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), format!("{}", elem_type)); + assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), + format!("{}", elem_type)); } } #[test] fn test_all() { - assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", format!("{}", ElemType::any(vec![]))); + assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", + format!("{}", ElemType::any(vec![]))); } } @@ -213,46 +180,43 @@ impl Elem { } impl ElemType { - pub fn any(locations: Vec) -> Self { + fn from_locations(type_set: EnumSet, + base_elem_type: BaseElemType, + locations: Vec) -> Self { ElemType { - type_set: EnumSet::all(), + type_set: type_set, info: locations.iter() .map(|&location| ElemTypeInfo { - base_elem_type: BaseElemType::Any, + base_elem_type: base_elem_type, location: location, }).collect(), } } + pub fn any(locations: Vec) -> Self { + Self::from_locations( + EnumSet::all(), + BaseElemType::Any, + locations) + } + pub fn concat_type(locations: Vec) -> Self { - ElemType { - type_set: - enum_set!(ElemSymbol::Bytes | - ElemSymbol::String | - ElemSymbol::Array | - ElemSymbol::Object), - info: locations.iter() - .map(|&location| - ElemTypeInfo { - base_elem_type: BaseElemType::Concat, - location: location, - }).collect(), - } + Self::from_locations( + enum_set!(ElemSymbol::Bytes | + ElemSymbol::String | + ElemSymbol::Array | + ElemSymbol::Object), + BaseElemType::Concat, + locations) } pub fn index_type(locations: Vec) -> Self { - ElemType { - type_set: - enum_set!(ElemSymbol::Array | - ElemSymbol::Object), - info: locations.iter() - .map(|&location| - ElemTypeInfo { - base_elem_type: BaseElemType::Index, - location: location, - }).collect(), - } + Self::from_locations( + enum_set!(ElemSymbol::Array | + ElemSymbol::Object), + BaseElemType::Index, + locations) } pub fn slice_type(locations: Vec) -> Self { @@ -375,50 +339,6 @@ mod context_display_tests { } impl Context { - // TODO: simplify/normalize context - // Produce a version where (basis = [..]) are the first (0..) - // TypeId's and the rest are sorted from the original Context - // - // Also output a variable remapper - // - // pub fn normalize(&self, basis: I) -> Result - // where - // I: IntoIter - // { - // let mut source = self.clone(); - // let mut result = Self::new(); - // for type_id in basis { - // let elem_type = source.get(type_id)? - // result.push(elem_type); - // _ - // } - - // // TODO: deprecated - // pub fn new_max(&self, other: Self) -> Self { - // Context { - // context: BTreeMap::new(), - // next_type_id: TypeId { - // type_id: - // cmp::max(self.next_type_id.type_id, - // other.next_type_id.type_id), - // }, - // } - // } - - // map is from other to result - // pub fn disjoint_union(&mut self, other: Self) -> Result { - // let mut type_map = &TypeIdMap::new(); - // for (type_id, elem_type) in other.context.iter() { - // type_map.push(*type_id, self.push(elem_type.clone())) - // .or_else(|e| Err(ContextError::DisjointUnion { - // lhs: self.clone(), - // rhs: other, - // error: e, - // }))? - // } - // Ok(*type_map) - // } - pub fn new() -> Self { Context { context: BTreeMap::new(), @@ -445,6 +365,30 @@ impl Context { push_id } + // NormalizeOnInvalidBasis is possible iff a `TypeId` in (basis) is repeated + // or missing from (self) + pub fn normalize_on(&self, basis: Vec) -> Result<(Self, TypeIdMap), ContextError> { + let mut source = self.clone(); + let mut result = Self::new(); + let mut type_map = TypeIdMap::new(); + for &type_id in &basis { + match source.context.remove(&type_id) { + None => Err(ContextError::NormalizeOnInvalidBasis { + type_id: type_id, + context: self.clone(), + basis: basis.clone().into_iter().collect(), + }), + Some(elem_type) => { + let new_type_id = result.next_type_id; + result.push(elem_type); + type_map.push(type_id, new_type_id)?; + Ok(()) + }, + }? + } + Ok((result, type_map)) + } + pub fn offset(&self, offset: TypeId) -> Self { Context { context: self.context.iter().map(|(k, x)| (k.offset(offset), x.clone())).collect(), @@ -473,14 +417,9 @@ impl Context { }?; self.context = self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(); self.next_type_id = cmp::max(self.next_type_id, to); - // Ok(Context { - // context: self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(), - // next_type_id: cmp::max(self.next_type_id, to), - // }) Ok(()) } - // fail iff not disjoint iff intersection non-empty pub fn disjoint_union(&mut self, other: Self) -> Result<(), ContextError> { for (&type_id, elem_type) in other.context.iter() { match self.context.insert(type_id, elem_type.clone()) { @@ -517,28 +456,24 @@ impl Context { yi: yi.clone(), is_lhs: true, })?; - let y_type = self.context.remove(&yi).ok_or_else(|| ContextError::Unify { xs: self.clone(), xi: xi.clone(), yi: yi.clone(), is_lhs: false, })?; - let xy_type = x_type.unify(y_type).or_else(|e| Err(ContextError::UnifyElemType { xs: self.clone(), xi: xi.clone(), yi: yi.clone(), error: e, }))?; - self.context.insert(yi, xy_type); Ok(()) } } - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Type { context: Context, @@ -546,8 +481,6 @@ pub struct Type { o_type: Vec, } - - impl Type { pub fn id() -> Self { Type { @@ -557,14 +490,20 @@ impl Type { } } + pub fn next_type_id(&self) -> TypeId { + self.context.next_type_id + } + // check whether all the TypeId's are valid pub fn is_valid(&self) -> bool { - let next_type_id = self.context.next_type_id; + let next_type_id = self.next_type_id(); self.context.is_valid() && !(self.i_type.iter().any(|x| *x >= next_type_id) || self.o_type.iter().any(|x| *x >= next_type_id)) } + // equivalent to running update_type_id w/ offset from largest to smallest + // existing TypeId pub fn offset(&self, offset: TypeId) -> Self { Type { context: self.context.offset(offset), @@ -573,22 +512,25 @@ impl Type { } } - pub fn next_type_id(&self) -> TypeId { - self.context.next_type_id - } - pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeError> { self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?; self.i_type = self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(); self.o_type = self.o_type.iter().map(|x| x.update_type_id(from, to)).collect(); - // Ok(Type { - // context: self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?, - // i_type: self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(), - // o_type: self.o_type.iter().map(|x| x.update_type_id(from, to)).collect(), - // }) Ok(()) } + pub fn normalize(&self) -> Result { + let mut basis = self.i_type.clone(); + basis.append(&mut self.o_type.clone()); + basis.dedup(); + let (new_context, type_map) = self.context.normalize_on(basis).map_err(|e| TypeError::ContextError(e))?; + Ok(Type { + context: new_context, + i_type: type_map.run(self.i_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, + o_type: type_map.run(self.o_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, + }) + } + // f : self // g : other // self.compose(other) : (f ++ g).type_of() @@ -608,15 +550,15 @@ impl Type { println!("composing:\n{0}\n\nAND\n{1}\n", self, other); let mut context = self.context.clone(); - println!("context: {}", context); - println!("context.next_type_id: {:?}", context.next_type_id.type_id); + // println!("context: {}", context); + // println!("context.next_type_id: {:?}", context.next_type_id.type_id); let offset_other = other.offset(self.next_type_id()); - println!("offset_other: {}", offset_other); + // println!("offset_other: {}", offset_other); context.disjoint_union(offset_other.context.clone()) .map_err(|e| TypeError::ContextError(e))?; - println!("context union: {}", context); + // println!("context union: {}", context); let mut mut_offset_other = offset_other.clone(); let mut zip_len = 0; @@ -637,122 +579,9 @@ impl Type { i_type: mut_offset_other.i_type.iter().chain(self.i_type.iter().skip(zip_len)).copied().collect(), o_type: self.o_type.iter().chain(mut_offset_other.o_type.iter().skip(zip_len)).copied().collect(), }) - - - - // let mut offset_other = other.offset(self.next_type_id()).clone(); - // context.disjoint_union(offset_other.context.clone()) - // .map_err(|e| TypeError::ContextError(e))?; - - // let mut other_o_type = offset_other.o_type.iter().clone(); - // let mut self_i_type = self.i_type.iter().clone(); - // other_o_type.by_ref().zip(self_i_type.by_ref()).try_for_each(|(&o_type, &i_type)| { - // context - // .unify(o_type, i_type) - // .map_err(|e| TypeError::ContextError(e))?; - // offset_other - // .update_type_id(o_type, i_type)?; - // Ok(()) - // })?; - - // Ok(Type { - // context: context, - // i_type: offset_other.i_type.iter().chain(self_i_type).copied().collect(), - // o_type: self.o_type.iter().chain(other_o_type).copied().collect(), - // }) } - - - // other_o_type.zip(self_i_type).enumerate().try_for_each(|(i, (&o_type, &i_type))| { - - // self_to_context.push(*i_type, *o_type).or_else(|_| Ok(()))?; - // context.unify(other_to_context.get(o_type, i)?, *i_type) - - // let mut i_type_result = offset_other.i_type.clone(); - // let mut o_type_result = self.o_type.clone(); - - // for (i, &o_type) in other_o_type.enumerate() { - // o_type_result.push(o_type) - // } - - // let other_to_context = context.disjoint_union(other.context) - // .map_err(|e| TypeError::ComposeDisjointUnion(e))?; - // let self_to_context = TypeIdMap::new(); - - - // let self_context = &self.context; - // let other_context = &other.context; - - // let mut context = self_context.clone().new_max(other_context.clone()); - // let mut self_type_map = TypeIdMap::new(); - // let mut other_type_map = TypeIdMap::new(); - - // let mut i_type = vec![]; - // let mut o_type = vec![]; - - // other.o_type.iter().zip(self.i_type.clone()).try_for_each(|(o_type, i_type)| { - // let new_type_id = context - // .unify(self_context.clone(), - // other_context.clone(), - // &i_type, - // &o_type)?; - // self_type_map.push(i_type, new_type_id)?; - // other_type_map.push(*o_type, new_type_id)?; - // Ok(()) - // })?; - - // TODO: replace with context merging - // match after_zip(other.o_type.clone(), self.i_type.clone()) { - // Ok(other_o_type_remainder) => - // for o_type in other_o_type_remainder { - // let new_o_type = context.push(other.context.clone().get(&o_type)?); - // other_type_map.push(o_type.clone(), new_o_type)?; - // i_type.push(new_o_type.clone()); - // }, - // Err(self_i_type_remainder) => - // for i_type in self_i_type_remainder { - // let new_i_type = context.push(self.context.clone().get(&i_type)?); - // self_type_map.push(i_type.clone(), new_i_type)?; - // o_type.push(new_i_type.clone()); - // }, - // } - - // let mut i_type_prefix = other_type_map.run(other.i_type.clone())?; - // let mut o_type_prefix = self_type_map.run(self.o_type.clone())?; - // i_type_prefix.append(&mut i_type); - // o_type_prefix.append(&mut o_type); - // Ok(Type { - // context: context.clone(), - // i_type: i_type_prefix, - // // .iter().chain(i_type.iter()).collect(), - // o_type: o_type_prefix, - // // o_type: .iter().chain(o_type.iter()).collect(), - - // // i_type: other_type_map.run(other.i_type.clone())?.iter().chain(i_type.iter()).collect(), - // // o_type: self_type_map.run(self.o_type.clone())?.iter().chain(o_type.iter()).collect(), - - // // other.i_type.clone().iter() - // // .map(|x| Ok(other_type_map - // // .get(x) - // // .ok_or_else(|| TypeError::ContextRemapUnknownTypeId { - // // context: context.clone(), - // // type_map: other_type_map.clone(), - // // index: *x, - // // })?.clone())).chain(i_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, - - // // o_type: self.o_type.clone().iter() - // // .map(|x| Ok(self_type_map - // // .get(x) - // // .ok_or_else(|| TypeError::ContextRemapUnknownTypeId { - // // context: context.clone(), - // // type_map: self_type_map.clone(), - // // index: *x, - // // })?.clone())).chain(o_type.iter().map(move |x| Ok(*x))).collect::, TypeError>>()?, - // }) - } - // Formatting: // ``` // Type { @@ -780,17 +609,19 @@ impl Type { // ``` impl Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + // let self_normalized = self.normalize().map_err(|_| fmt::Error)?; + let self_normalized = self; write!(f, "{context}\n[{i_type}] ->\n[{o_type}]", - context = self.context, - i_type = self.i_type.iter().fold(String::new(), |memo, x| { + context = self_normalized.context, + i_type = self_normalized.i_type.iter().fold(String::new(), |memo, x| { let x_str = format!("t{}", x.type_id); if memo == "" { x_str } else { memo + ", " + &x_str.to_string() }}), - o_type = self.o_type.iter().fold(String::new(), |memo, x| { + o_type = self_normalized.o_type.iter().fold(String::new(), |memo, x| { let x_str = format!("t{}", x.type_id); if memo == "" { x_str @@ -848,10 +679,6 @@ mod type_display_tests { - - - - impl Restack { // TODO: fix locations: out locations are mislabeled as in locations pub fn type_of(&self, line_no: LineNo) -> Result { @@ -1055,22 +882,22 @@ pub enum ElemTypeError { }, } -// #[derive(Debug, PartialEq, Error)] -// pub enum TypeIdMapError { -// #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] -// GetUnknownTypeId { -// index: TypeId, -// location: usize, -// type_map: TypeIdMap, -// }, +#[derive(Debug, PartialEq, Error)] +pub enum TypeIdMapError { + #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] + GetUnknownTypeId { + index: TypeId, + location: usize, + type_map: TypeIdMap, + }, -// #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] -// PushExists { -// from: TypeId, -// to: TypeId, -// map: TypeIdMap, -// }, -// } + #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] + PushExists { + from: TypeId, + to: TypeId, + map: TypeIdMap, + }, +} #[derive(Debug, PartialEq, Error)] pub enum ContextError { @@ -1092,12 +919,12 @@ pub enum ContextError { rhs: Context, }, - // #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, resulted in impossible TypeIdMapError: {error:?}")] - // DisjointUnion { - // lhs: Context, - // rhs: Context, - // error: TypeIdMapError, - // }, + #[error("Context::normalize_on applied to invalid basis: type_id: {type_id:?}, context: {context:?}, basis: {basis:?}")] + NormalizeOnInvalidBasis { + type_id: TypeId, + context: Context, + basis: Vec, + }, #[error("Context::update_type_id called on missing 'from: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] UpdateTypeIdFromMissing { @@ -1128,6 +955,15 @@ pub enum ContextError { yi: TypeId, error: ElemTypeError, }, + + #[error("Context::normalize_on building TypeIdMap failed: {0:?}")] + TypeIdMapError(TypeIdMapError), +} + +impl From for ContextError { + fn from(error: TypeIdMapError) -> Self { + Self::TypeIdMapError(error) + } } @@ -1161,15 +997,10 @@ pub enum TypeError { error: Box, }, - // #[error("applying TypeIdMap failed: {0:?}")] - // TypeIdMapError(TypeIdMapError), + #[error("Type::normalize applying TypeIdMap failed: {0:?}")] + TypeIdMapError(TypeIdMapError), } -// impl From for TypeError { -// fn from(error: TypeIdMapError) -> Self { -// Self::TypeIdMapError(error) -// } -// } // pub type Stack = Vec; @@ -1203,46 +1034,63 @@ impl Instructions { } } +// Test program #1: [] -> [] +// +// Instruction::Push(Elem::Bool(true)), +// Instruction::Restack(Restack::id()), +// Instruction::AssertTrue, -// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -// pub struct TypeIdMap { -// map: BTreeMap, -// } +// Test program #2 +// +// ∀ (t0 ∊ {JSON}), +// ∀ (t1 ∊ {JSON}), +// ∀ (t2 ∊ {Object}), +// [t1] -> +// [t0, t2, t1] +// +// Instruction::Push(Elem::Json(Default::default())), +// Instruction::UnpackJson(ElemSymbol::Object), +// Instruction::Restack(Restack::dup()), +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct TypeIdMap { + map: BTreeMap, +} -// impl TypeIdMap { -// pub fn new() -> Self { -// TypeIdMap { -// map: BTreeMap::new(), -// } -// } - -// pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { -// if self.map.contains_key(&from) { -// Err(TypeIdMapError::PushExists { -// from: from, -// to: to, -// map: self.clone(), -// }) -// } else { -// self.map.insert(from, to); -// Ok(()) -// } -// } - -// pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { -// self.map.get(index) -// .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { -// index: index.clone(), -// location: location, -// type_map: self.clone(), -// }) -// } - -// pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { -// type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() -// } -// } + +impl TypeIdMap { + pub fn new() -> Self { + TypeIdMap { + map: BTreeMap::new(), + } + } + + pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { + if self.map.contains_key(&from) { + Err(TypeIdMapError::PushExists { + from: from, + to: to, + map: self.clone(), + }) + } else { + self.map.insert(from, to); + Ok(()) + } + } + + pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { + self.map.get(index) + .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { + index: index.clone(), + location: location, + type_map: self.clone(), + }) + } + + pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { + type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() + } +} From 35505c518f63205bfe19d5420109e683ebefd069 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 23 Feb 2022 18:23:25 -0500 Subject: [PATCH 31/77] type-level lists prototype --- src/types.rs | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) diff --git a/src/types.rs b/src/types.rs index 8ef57a3..2d1d89a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -5,6 +5,8 @@ use std::collections::BTreeMap; use std::cmp; use std::fmt; use std::fmt::{Display, Formatter}; +// use std::alloc::string; +use std::marker::PhantomData; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; @@ -1092,5 +1094,309 @@ impl TypeIdMap { } } +////// +//////////// +////// +//////////// +////////////////// +//////////// +////// +//////////// +////// +//////////// +////////////////// +//////////// +////////////////// +//////////////////////// +////////////////// +//////////// +////////////////// +//////////// +////// +//////////// +////// +//////////// +////////////////// +//////////// +////// +//////////// +////// +//////////// +////////////////// +//////////// +////////////////// +//////////////////////// +////////////////// +//////////// +////////////////// +//////////// +////////////////// +//////////////////////// +////////////////// +//////////////////////// +////////////////////////////// +//////////////////////// +////////////////// +//////////////////////// +////////////////// +//////////// +////////////////// +//////////// +////////////////// +//////////////////////// +////////////////// +//////////// +////////////////// +//////////// +////// +//////////// +////// +//////////// +////////////////// +//////////// +////// +//////////// +////// +//////////// +////////////////// +//////////// +////////////////// +//////////////////////// +////////////////// +//////////// +////////////////// +//////////// +////// +//////////// +////// +//////////// +////////////////// +//////////// +////// +//////////// +////// + +// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// pub struct ElemType { +// type_set: EnumSet, +// info: Vec, +// } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TUnit {} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TBool { + get_bool: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TNumber { + number: serde_json::Number, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TBytes { + bytes: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TString { + string: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TArray { + array: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TObject { + object: serde_json::Map, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct TJson { + json: serde_json::Value, +} + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub enum Elem { +// Unit, +// Bool(bool), +// Number(Number), +// Bytes(Vec), +// String(String), +// Array(Vec), +// Object(Map), +// Json(Value), +// } + +pub trait IsElem { + fn to_elem(&self) -> Elem; +} + +impl IsElem for TUnit { + fn to_elem(&self) -> Elem { + Elem::Unit + } +} + +impl IsElem for TBool { + fn to_elem(&self) -> Elem { + Elem::Bool(self.get_bool) + } +} + +// trait Elaborate { +// type Elab: IsElem; +// } + +pub struct NoHead {} + +pub trait Trait {} +impl Trait for NoHead {} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Nil { + t: PhantomData, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Cons, V: HList> { + t: PhantomData, + hd: U, + tl: V, +} + +pub trait HList { + type Hd: Trait; + type Tl: HList; + + fn is_empty(&self) -> bool; + fn hd(&self) -> Self::Hd; + fn tl(&self) -> Self::Tl; + fn cons>(&self, x: U) -> Cons where Self: Sized; +} + +impl HList for Nil { + type Hd = NoHead; + type Tl = Nil; + + fn is_empty(&self) -> bool { + true + } + + fn hd(&self) -> Self::Hd { + NoHead {} + } + + fn tl(&self) -> Self::Tl { + (*self).clone() + } + + fn cons>(&self, x: U) -> Cons + where + Self: Sized, + { + Cons { + t: PhantomData, + hd: x, + tl: (*self).clone(), + } + } +} + +impl, V: Clone + HList> HList for Cons { + type Hd = U; + type Tl = V; + + fn is_empty(&self) -> bool { + false + } + + fn hd(&self) -> Self::Hd { + self.hd.clone() + } + + fn tl(&self) -> Self::Tl { + self.tl.clone() + } + + fn cons>(&self, x: W) -> Cons { + Cons { + t: PhantomData, + hd: x, + tl: (*self).clone(), + } + } +} + + + + + +// pub struct Nil { +// nil: T, +// } + +// pub struct Cons { +// hd: T, +// tl: U, +// } + +// pub trait SymbolList { +// } + +// pub struct Cons { +// hd: T, +// tl: U, +// } + + + +// impl SymbolList for Nil + + + +// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// pub enum Symbol { +// Unit, +// Bool, +// Number, +// Bytes, +// String, +// Array, +// Object, +// Json, +// } + + +// trait Elaborate { +// type Elab; + +// fn elaborate(x: &Self::Elab) -> Elem; +// fn elaborate_match(x: &Elem) -> Option; +// } + +// #[derive(Clone, Debug, PartialOrd, Ord, Serialize, Deserialize)] +// #[derive(PartialEq, Eq)] +// pub enum SymbolList { +// Nil, +// Cons(Symbol, Box), +// } + + +// trait Elaborates { +// // type Elab; + +// // fn elaborate(x: &Self::Elab) -> Elem; +// // fn elaborate_match(x: &Elem) -> Option; +// } + + +// pub enum Instr>, const RET: EnumSet> { +// Func(Box(dyn Fn( + From f825b0d9620f0f6c53b0b815a6f132e53a1c9f90 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 24 Feb 2022 10:51:06 -0500 Subject: [PATCH 32/77] add dyn-clone, use type equality --- Cargo.toml | 5 +- src/lib.rs | 1 + src/main.rs | 18 ++- src/types.rs | 369 +++++++++++++++++++++++++++++++++------------------ 4 files changed, 256 insertions(+), 137 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cf85739..01b2d50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,15 +6,16 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dyn-clone = "1.0.4" enumset = { version = "1.0.8", features = ["serde"] } generic-array = "0.14" hex = "0.4" hex-literal = "0.3" k256 = { version = "0.10.2", features = ["std", "ecdsa", "serde"] } +quickcheck = "1.0.3" +quickcheck_macros = "1.0.0" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha2 = "0.9" sha3 = "0.9" thiserror = "1.0" -quickcheck = "1.0.3" -quickcheck_macros = "1.0.0" diff --git a/src/lib.rs b/src/lib.rs index fd97009..0f4b739 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ mod elem; pub use elem::{Elem, ElemSymbol}; mod types; pub use types::{Instruction, Instructions}; +// , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList mod parse; pub use parse::{parse, parse_json}; mod executor; diff --git a/src/main.rs b/src/main.rs index 9ff6580..0665065 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; +// use cryptoscript::{demo_triple, demo_triple_with_tl_handles_intermediate_types, HList}; #[cfg(test)] mod tests { @@ -215,8 +216,17 @@ fn main() { // println!("{:?}", exec); println!(""); - match instructions.type_of() { - Ok(r) => println!("\nfinal type:\n{}", r), - Err(e) => println!("{}", e), - } + // match instructions.type_of() { + // Ok(r) => println!("\nfinal type:\n{}", r), + // Err(e) => println!("{}", e), + // } + + // println!(""); + // println!("demo_triple:"); + // demo_triple().fold((), |_memo, x| println!("{:?}", x)); + + // println!(""); + // println!("demo_triple_with_tl_handles_intermediate_types:"); + // demo_triple_with_tl_handles_intermediate_types().fold((), |_memo, x| println!("{:?}", x)); + } diff --git a/src/types.rs b/src/types.rs index 2d1d89a..bee194e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,6 +8,7 @@ use std::fmt::{Display, Formatter}; // use std::alloc::string; use std::marker::PhantomData; +use dyn_clone::DynClone; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -1176,227 +1177,333 @@ impl TypeIdMap { //////////// ////// -// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -// pub struct ElemType { -// type_set: EnumSet, -// info: Vec, -// } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TUnit {} +pub struct TUnit {} #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TBool { +pub struct TBool { get_bool: bool, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TNumber { +pub struct TNumber { number: serde_json::Number, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TBytes { +pub struct TBytes { bytes: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TString { +pub struct TString { string: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TArray { +pub struct TArray { array: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TObject { +pub struct TObject { object: serde_json::Map, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct TJson { +pub struct TJson { json: serde_json::Value, } -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub enum Elem { -// Unit, -// Bool(bool), -// Number(Number), -// Bytes(Vec), -// String(String), -// Array(Vec), -// Object(Map), -// Json(Value), -// } -pub trait IsElem { - fn to_elem(&self) -> Elem; -} -impl IsElem for TUnit { - fn to_elem(&self) -> Elem { - Elem::Unit - } + + +// Deriving a string from the type without any values allows debugging TEq +pub trait TypeName { + fn type_name(x: PhantomData) -> &'static str; } -impl IsElem for TBool { - fn to_elem(&self) -> Elem { - Elem::Bool(self.get_bool) +impl TypeName for TUnit { + fn type_name(_: PhantomData) -> &'static str { + "Unit" } } -// trait Elaborate { -// type Elab: IsElem; -// } - -pub struct NoHead {} -pub trait Trait {} -impl Trait for NoHead {} +pub trait Teq: DynClone { + type Other; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Nil { - t: PhantomData, + fn transport(&self, x: T) -> Self::Other; } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Cons, V: HList> { - t: PhantomData, - hd: U, - tl: V, +impl Teq for U { + type Other = T; + + fn transport(&self, x: T) -> Self::Other { + x + } } -pub trait HList { - type Hd: Trait; - type Tl: HList; +// dyn_clone::clone_trait_object!(Teq); - fn is_empty(&self) -> bool; - fn hd(&self) -> Self::Hd; - fn tl(&self) -> Self::Tl; - fn cons>(&self, x: U) -> Cons where Self: Sized; +// #[derive(Clone)] +pub struct TEq +{ + teq: Box>, } -impl HList for Nil { - type Hd = NoHead; - type Tl = Nil; - - fn is_empty(&self) -> bool { +impl PartialEq for TEq { + fn eq(&self, _other: &Self) -> bool { true } +} - fn hd(&self) -> Self::Hd { - NoHead {} - } +impl Eq for TEq {} - fn tl(&self) -> Self::Tl { - (*self).clone() +impl TEq { + pub fn transport(&self, x: T) -> U { + (*self.teq).transport(x) } - fn cons>(&self, x: U) -> Cons - where - Self: Sized, - { - Cons { - t: PhantomData, - hd: x, - tl: (*self).clone(), + // // TODO: compose + // pub fn compose(&self, uv: TEq) -> TEq { + // TEq { + // teq: Box::new(()), + // } + // } +} + +impl TEq { + pub fn refl(_x: PhantomData) -> Self { + TEq { + teq: Box::new(()), } } } -impl, V: Clone + HList> HList for Cons { - type Hd = U; - type Tl = V; +impl fmt::Debug for TEq { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "Teq {{ teq: Teq<{0}, {1}> }}", TypeName::type_name(PhantomData::), TypeName::type_name(PhantomData::)) + } +} + +impl Clone for TEq { + fn clone(&self) -> Self { + TEq { + teq: dyn_clone::clone_box(&*self.teq), + } - fn is_empty(&self) -> bool { - false + // self.compose(TEq::refl(PhantomData::)) } +} - fn hd(&self) -> Self::Hd { - self.hd.clone() + +#[derive(Clone, PartialEq, Eq)] +pub enum IsElem { + Unit(TEq), + Bool(TEq), + // Number(TEq), + // Bytes(TEq>), + // String(TEq), + // Array(TEq>), + // Object(TEq>), + // Json(TEq), +} + +impl fmt::Debug for IsElem { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "IsElem {}", TypeName::type_name(PhantomData::)) } +} + +// impl fmt::Debug for TEq { +// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { +// write!(f, "Teq {{ teq: Teq<{0}, {1}> }}", TypeName::type_name(PhantomData), TypeName::type_name(PhantomData)) +// } +// } + + - fn tl(&self) -> Self::Tl { - self.tl.clone() + +impl IsElem { + pub fn elem_symbol(&self) -> ElemSymbol { + match self { + Self::Unit(_) => ElemSymbol::Unit, + Self::Bool(_) => ElemSymbol::Bool, + // Self::Number(_) => ElemSymbol::Number, + // Self::Bytes(_) => ElemSymbol::Bytes, + // Self::String(_) => ElemSymbol::String, + // Self::Array(_) => ElemSymbol::Array, + // Self::Object(_) => ElemSymbol::Object, + // Self::Json(_) => ElemSymbol::Json, + } } - fn cons>(&self, x: W) -> Cons { - Cons { - t: PhantomData, - hd: x, - tl: (*self).clone(), + pub fn to_elem(&self, x: T) -> Elem { + match self { + Self::Unit(_) => Elem::Unit, + Self::Bool(eq) => Elem::Bool(eq.transport(x).get_bool), + // Self::Number(eq) => Elem::Number(eq.transport(x)), + // Self::Bytes(eq) => Elem::Bytes(eq.transport(x)), + // Self::String(eq) => Elem::String(eq.transport(x)), + // Self::Array(eq) => Elem::Array(eq.transport(x)), + // Self::Object(eq) => Elem::Object(eq.transport(x)), + // Self::Json(eq) => Elem::Json(eq.transport(x)), } } + + // TODO: from_elem + // fn from_elem(x: &Elem) -> Option where Self: Sized; } +// fn fold(&self, init: B, f: F) -> B where F: Fn(B, Elem) -> B; +pub trait HList: Clone + IntoIterator { + type Hd; + type Tl: HList; + // fn is_empty(&self) -> bool; + fn hd(&self) -> Self::Hd; + fn hd_is_elem(&self) -> IsElem; -// pub struct Nil { -// nil: T, -// } + // fn tl(&self) -> Self::Tl; + // fn cons(&self, x: T, is_elem: IsElem) -> Cons where Self: Sized; -// pub struct Cons { -// hd: T, -// tl: U, -// } +} -// pub trait SymbolList { -// } -// pub struct Cons { -// hd: T, -// tl: U, -// } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Nil { } +impl Iterator for Nil { + type Item = Elem; -// impl SymbolList for Nil + fn next(&mut self) -> Option { + None + } +} +impl HList for Nil { + type Hd = TUnit; + type Tl = Nil; + // fn is_empty(&self) -> bool { + // true + // } -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -// pub enum Symbol { -// Unit, -// Bool, -// Number, -// Bytes, -// String, -// Array, -// Object, -// Json, -// } + fn hd(&self) -> Self::Hd { + TUnit {} + } + fn hd_is_elem(&self) -> IsElem { + IsElem::Unit(TEq::refl(PhantomData::)) + } -// trait Elaborate { -// type Elab; + // fn tl(&self) -> Self::Tl { + // (*self).clone() + // } + + // fn cons>(&self, x: U) -> Cons + // where + // Self: Sized, + // { + // Cons { + // t: PhantomData, + // hd: x, + // tl: (*self).clone(), + // } + // } +} -// fn elaborate(x: &Self::Elab) -> Elem; -// fn elaborate_match(x: &Elem) -> Option; -// } -// #[derive(Clone, Debug, PartialOrd, Ord, Serialize, Deserialize)] -// #[derive(PartialEq, Eq)] -// pub enum SymbolList { -// Nil, -// Cons(Symbol, Box), -// } +#[derive(Clone, PartialEq, Eq)] +pub struct Cons { + is_elem: IsElem, + hd: T, + tl: U, +} +#[derive(Clone, PartialEq, Eq)] +pub struct IterCons { + cons: Cons, + at_head: bool, +} -// trait Elaborates { -// // type Elab; +impl IntoIterator for Cons { + type Item = Elem; + type IntoIter = IterCons; -// // fn elaborate(x: &Self::Elab) -> Elem; -// // fn elaborate_match(x: &Elem) -> Option; -// } + fn into_iter(self) -> Self::IntoIter { + IterCons { + cons: self, + at_head: true, + } + } +} + +impl Iterator for IterCons { + type Item = Elem; + + fn next(&mut self) -> Option { + if self.at_head { + Some(self.cons.hd_is_elem().to_elem(self.cons.hd)) + } else { + let self_cons = self.cons.clone(); + *self = self_cons.into_iter(); + self.next() + } + } +} +impl HList for Cons { + type Hd = T; + type Tl = U; -// pub enum Instr>, const RET: EnumSet> { -// Func(Box(dyn Fn( + // fn is_empty(&self) -> bool { + // false + // } + fn hd(&self) -> Self::Hd { + self.hd + } + fn hd_is_elem(&self) -> IsElem { + self.is_elem.clone() + } + + // fn tl(&self) -> Self::Tl { + // self.tl.clone() + // } + + // fn cons>(&self, x: W) -> Cons { + // Cons { + // t: PhantomData, + // hd: x, + // tl: (*self).clone(), + // } + // } + +} + +// pub fn demo_triple() -> Cons<(), TBool, Cons<(), TUnit, Cons<(), TBool, Nil<()>>>> { +// Nil { t: PhantomData } +// .cons(TBool { get_bool: true }) +// .cons(TUnit { }) +// .cons(TBool { get_bool: false }) +// } + +// pub fn demo_triple_with_tl_handles_intermediate_types() -> Cons<(), TBool, Cons<(), TUnit, Cons<(), TBool, Nil<()>>>> { +// Nil { t: PhantomData } +// .cons(TBool { get_bool: true }) +// .cons(TUnit { }) +// .cons(TBool { get_bool: false }) +// .cons(TBool { get_bool: true }) +// .cons(TUnit { }) +// .tl() +// .tl() +// } From 48d6d199128a8f26a5a4322df72d263c0c562548 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 24 Feb 2022 15:02:00 -0500 Subject: [PATCH 33/77] use Arc --- Cargo.toml | 1 - src/types.rs | 26 ++++++++------------------ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01b2d50..2d4b412 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dyn-clone = "1.0.4" enumset = { version = "1.0.8", features = ["serde"] } generic-array = "0.14" hex = "0.4" diff --git a/src/types.rs b/src/types.rs index bee194e..bdd6452 100644 --- a/src/types.rs +++ b/src/types.rs @@ -7,8 +7,8 @@ use std::fmt; use std::fmt::{Display, Formatter}; // use std::alloc::string; use std::marker::PhantomData; +use std::sync::Arc; -use dyn_clone::DynClone; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -273,6 +273,7 @@ pub struct Context { next_type_id: TypeId, } + // Formatting: // ``` // Context { @@ -1232,7 +1233,7 @@ impl TypeName for TUnit { } -pub trait Teq: DynClone { +pub trait Teq { type Other; fn transport(&self, x: T) -> Self::Other; @@ -1246,12 +1247,10 @@ impl Teq for U { } } -// dyn_clone::clone_trait_object!(Teq); - -// #[derive(Clone)] +#[derive(Clone)] pub struct TEq { - teq: Box>, + teq: Arc>, } impl PartialEq for TEq { @@ -1278,7 +1277,7 @@ impl TEq { impl TEq { pub fn refl(_x: PhantomData) -> Self { TEq { - teq: Box::new(()), + teq: Arc::new(()), } } } @@ -1289,17 +1288,6 @@ impl fmt::Debug for TEq { } } -impl Clone for TEq { - fn clone(&self) -> Self { - TEq { - teq: dyn_clone::clone_box(&*self.teq), - } - - // self.compose(TEq::refl(PhantomData::)) - } -} - - #[derive(Clone, PartialEq, Eq)] pub enum IsElem { Unit(TEq), @@ -1489,6 +1477,8 @@ impl HList for Cons { } + + // pub fn demo_triple() -> Cons<(), TBool, Cons<(), TUnit, Cons<(), TBool, Nil<()>>>> { // Nil { t: PhantomData } // .cons(TBool { get_bool: true }) From ade811b22828c67d03af17adeb357f3ff6c3c37e Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Feb 2022 19:21:00 -0500 Subject: [PATCH 34/77] WIP: debugging IsInstruction for concat --- src/executor.rs | 3 +- src/instruction.rs | 303 +++++ src/lib.rs | 7 +- src/parse.rs | 3 +- src/stack.rs | 102 ++ src/types.rs | 2564 +++++++++++++++++++++++------------------- src/types_scratch.rs | 406 +++++++ 7 files changed, 2230 insertions(+), 1158 deletions(-) create mode 100644 src/instruction.rs create mode 100644 src/stack.rs create mode 100644 src/types_scratch.rs diff --git a/src/executor.rs b/src/executor.rs index 732175a..dc08c7a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,6 +1,7 @@ use crate::restack::{Restack, RestackError}; use crate::elem::{Elem, ElemError}; -use crate::types::{Instruction, Instructions}; +/* use crate::types::{Instruction, Instructions}; */ +use crate::instruction::{Instruction, Instructions}; use thiserror::Error; #[derive(Debug, Default)] diff --git a/src/instruction.rs b/src/instruction.rs new file mode 100644 index 0000000..2a69315 --- /dev/null +++ b/src/instruction.rs @@ -0,0 +1,303 @@ +use crate::restack::{Restack, RestackError}; +use crate::elem::{Elem, ElemSymbol}; +use crate::stack::{LineNo}; +use crate::types::{ElemType, TypeId, Context, Type, TypeError}; + +// use std::collections::BTreeMap; +// use std::cmp; +// use std::fmt; +// use std::fmt::{Display, Formatter}; +// // use std::alloc::string; +// use std::marker::PhantomData; +// use std::sync::Arc; + +// use enumset::{EnumSet, enum_set}; +use serde::{Deserialize, Serialize}; +// use serde_json::{Map, Number, Value}; +use thiserror::Error; + + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub enum Instruction { + Push(Elem), + Restack(Restack), + HashSha256, + CheckLe, + CheckLt, + CheckEq, + Concat, + Slice, + Index, + Lookup, + AssertTrue, + ToJson, + UnpackJson(ElemSymbol), + StringToBytes, +} + + +#[derive(Debug, PartialEq, Error)] +pub enum InstructionTypeError { + // TODO: move to instruction:: + #[error("Instruction::type_of resulted in an error involving: {instruction:?};\n {error:?}")] + InstructionTypeOfDetail { + instruction: Instruction, + error: Box, + }, + + // TODO: move to instruction:: + #[error("Instructions::type_of called on an empty Vec of Instruction's")] + InstructionsTypeOfEmpty, + + // TODO: move to instruction:: + #[error("Instructions::type_of resulted in an error on line: {line_no:?};\n {error:?}")] + InstructionsTypeOfLineNo { + line_no: usize, + error: Box, + }, + + // TODO: move to instruction:: + #[error("Instruction::type_of resulted in restack error: {0:?}")] + InstructionTypeOfRestack(RestackError), + +} + + + +impl Restack { + // TODO: fix locations: out locations are mislabeled as in locations + pub fn type_of(&self, line_no: LineNo) -> Result { + let mut context = Context::new(); + let mut restack_type: Vec = (0..self.restack_depth) + .map(|x| context.push(ElemType::any(vec![line_no.in_at(x)]))) + .collect(); + Ok(Type { + context: context, + i_type: restack_type.clone(), + o_type: self.run(&mut restack_type)?, + }) + } +} + +/// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] +/// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] +/// HashSha256, // : [ bytes ] -> [ bytes ] +/// CheckLe, // : [ x, x ] -> [ bool ] +/// CheckLt, // : [ x, x ] -> [ bool ] +/// CheckEq, // : [ x, x ] -> [ bool ] +/// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] +/// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] +/// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] +/// Lookup, // [ string, object ] -> [ json ] +/// AssertTrue, // [ bool ] -> [] +/// ToJson, // (t: type) : [ t ] -> [ json ] +/// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] +/// StringToBytes, // [ string ] -> [ bytes ] +impl Instruction { + pub fn type_of(&self, line_no: LineNo) -> Result { + match self { + Instruction::Restack(restack) => + Ok(restack + .type_of(line_no) + .or_else(|e| Err(InstructionTypeError::InstructionTypeOfRestack(e)))?), + + Instruction::AssertTrue => { + let mut context = Context::new(); + let bool_var = context + .push(ElemSymbol::Bool + .elem_type(vec![line_no.in_at(0)])); + Ok(Type { + context: context, + i_type: vec![bool_var], + o_type: vec![], + }) + }, + + Instruction::Push(elem) => { + let mut context = Context::new(); + let elem_var = context + .push(elem.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![], + o_type: vec![elem_var], + }) + }, + + Instruction::HashSha256 => { + let mut context = Context::new(); + let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.in_at(0), line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![bytes_var], + o_type: vec![bytes_var], + }) + }, + + Instruction::ToJson => { + let mut context = Context::new(); + let any_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![any_var], + o_type: vec![json_var], + }) + }, + + Instruction::StringToBytes => { + let mut context = Context::new(); + let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); + let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![string_var], + o_type: vec![bytes_var], + }) + }, + + Instruction::UnpackJson(elem_symbol) => { + let mut context = Context::new(); + let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.in_at(0)])); + let elem_symbol_var = context.push(elem_symbol.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![json_var], + o_type: vec![elem_symbol_var], + }) + }, + + Instruction::CheckLe => { + let mut context = Context::new(); + let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); + let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![any_lhs_var, any_rhs_var], + o_type: vec![bool_var], + }) + }, + + Instruction::CheckLt => { + let mut context = Context::new(); + let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); + let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![any_lhs_var, any_rhs_var], + o_type: vec![bool_var], + }) + }, + + Instruction::CheckEq => { + let mut context = Context::new(); + let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); + let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); + let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![any_lhs_var, any_rhs_var], + o_type: vec![bool_var], + }) + }, + + Instruction::Concat => { + let mut context = Context::new(); + let concat_var = context.push(ElemType::concat_type(vec![line_no.in_at(0), line_no.in_at(1), line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![concat_var, concat_var], + o_type: vec![concat_var], + }) + }, + + Instruction::Index => { + let mut context = Context::new(); + let number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); + let index_var = context.push(ElemType::index_type(vec![line_no.in_at(1), line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![number_var, index_var], + o_type: vec![index_var], + }) + }, + + Instruction::Lookup => { + let mut context = Context::new(); + let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); + let object_var = context.push(ElemSymbol::Object.elem_type(vec![line_no.in_at(1), line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![string_var, object_var], + o_type: vec![object_var], + }) + }, + + Instruction::Slice => { + let mut context = Context::new(); + let offset_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); + let length_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(1)])); + let slice_var = context.push(ElemType::slice_type(vec![line_no.in_at(2), line_no.out_at(0)])); + Ok(Type { + context: context, + i_type: vec![offset_number_var, length_number_var, slice_var], + o_type: vec![slice_var], + }) + }, + }.or_else(|e| Err(InstructionTypeError::InstructionTypeOfDetail { + instruction: self.clone(), + error: Box::new(e), + })) + } +} + + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub struct Instructions { + pub instructions: Vec, +} + +impl IntoIterator for Instructions { + type Item = Instruction; + type IntoIter = as std::iter::IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.instructions.into_iter() + } +} + +impl Instructions { + pub fn type_of(&self) -> Result { + let mut current_type = Type::id(); + for (i, instruction) in self.instructions.iter().enumerate() { + current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) + .or_else(|e| Err(InstructionTypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location + line_no: i, + error: Box::new(e), + }))?; + + println!("line {i}: {current_type}", i = i, current_type = current_type); + } + Ok(current_type) + } +} + +// Test program #1: [] -> [] +// +// Instruction::Push(Elem::Bool(true)), +// Instruction::Restack(Restack::id()), +// Instruction::AssertTrue, + +// Test program #2 +// +// ∀ (t0 ∊ {JSON}), +// ∀ (t1 ∊ {JSON}), +// ∀ (t2 ∊ {Object}), +// [t1] -> +// [t0, t2, t1] +// +// Instruction::Push(Elem::Json(Default::default())), +// Instruction::UnpackJson(ElemSymbol::Object), +// Instruction::Restack(Restack::dup()), diff --git a/src/lib.rs b/src/lib.rs index 0f4b739..7b968ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,14 @@ mod arbitrary; pub use arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; mod elem; pub use elem::{Elem, ElemSymbol}; +mod stack; +pub use stack::{Stack, StackError}; mod types; -pub use types::{Instruction, Instructions}; +mod types_scratch; +// pub use types::{Instruction, Instructions}; // , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList +mod instruction; +pub use instruction::{Instruction, Instructions}; mod parse; pub use parse::{parse, parse_json}; mod executor; diff --git a/src/parse.rs b/src/parse.rs index 14e16be..f123edb 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -10,7 +10,8 @@ /// digit hexadecimal number. use crate::elem::{Elem}; -use crate::types::{Instruction, Instructions}; +// use crate::types::{Instruction, Instructions}; +use crate::instruction::{Instruction, Instructions}; use std::str::FromStr; diff --git a/src/stack.rs b/src/stack.rs new file mode 100644 index 0000000..2e281fd --- /dev/null +++ b/src/stack.rs @@ -0,0 +1,102 @@ +// use crate::restack::{RestackError}; +use crate::elem::{Elem, ElemSymbol}; + +// use std::collections::BTreeMap; +// use std::cmp; +// use std::iter::{FromIterator}; + +// use std::fmt; +// use std::fmt::{Display, Formatter}; +// // use std::alloc::string; +// use std::marker::PhantomData; +// use std::sync::Arc; + +// use enumset::{EnumSet, enum_set}; +use serde::{Deserialize, Serialize}; +// use serde_json::{Map, Number, Value}; +use thiserror::Error; + + +// TODO: use for execution +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub struct Stack { + stack: Vec, +} + +impl Stack { + // TODO: since pop can fail, require passing debug info to it + // (so we know what we were expecting) + pub fn pop(&mut self) -> Result { + let result = self.stack.get(0).ok_or_else(|| StackError::EmptyStack).map(|x|x.clone())?; + self.stack = self.stack.drain(1..).collect(); + Ok(result.clone()) + } + + pub fn push(&mut self, elem: Elem) { + let mut memo = vec![elem]; + // memo.append(&mut self.stack.clone()); + memo.append(&mut self.stack); + self.stack = memo; + } +} + +#[derive(Debug, Error)] +pub enum StackError { + #[error("Stack::pop: tried to pop from an empty stack")] + EmptyStack, + + #[error("HList::pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack:?})")] + UnexpectedElemType { + expected: ElemSymbol, + found: Elem, + stack: Stack, + }, + + #[error("Stack::run_instruction: instruction {name:?} produced error: {error:?}\non line number: {line_no:?}")] + RunInstruction { + name: String, + error: String, + line_no: LineNo, + }, +} + +// TODO: relocate LineNo, ArgumentIndex, Location +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct LineNo { + pub line_no: usize, +} + +impl From for LineNo { + fn from(line_no: usize) -> Self { + LineNo { + line_no: line_no, + } + } +} + +pub type ArgumentIndex = usize; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Location { + line_no: LineNo, + argument_index: ArgumentIndex, + is_input: bool, +} + +impl LineNo { + pub fn in_at(&self, argument_index: usize) -> Location { + Location { + line_no: *self, + argument_index: argument_index, + is_input: true, + } + } + + pub fn out_at(&self, argument_index: usize) -> Location { + Location { + line_no: *self, + argument_index: argument_index, + is_input: false, + } + } +} diff --git a/src/types.rs b/src/types.rs index bdd6452..c168c2f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,8 +1,11 @@ -use crate::restack::{Restack, RestackError}; +// use crate::restack::{RestackError}; use crate::elem::{Elem, ElemSymbol}; +use crate::stack::{Stack, StackError, LineNo, Location}; use std::collections::BTreeMap; use std::cmp; +use std::iter::{FromIterator}; + use std::fmt; use std::fmt::{Display, Formatter}; // use std::alloc::string; @@ -11,1090 +14,892 @@ use std::sync::Arc; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; use thiserror::Error; -// typing: -// - unification -// + inference -// + checking against inferred or other type (this + inference = bidirecitonal) -// - two categories of tests: -// + property tests for typing methods themselves -// + test that a function having a particular type -> it runs w/o type errors on such inputs -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -pub enum Instruction { - Push(Elem), - Restack(Restack), - HashSha256, - CheckLe, - CheckLt, - CheckEq, - Concat, - Slice, - Index, - Lookup, - AssertTrue, - ToJson, - UnpackJson(ElemSymbol), - StringToBytes, -} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Empty {} -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct LineNo { - line_no: usize, +impl Empty { + fn absurd(&self, _p: PhantomData) -> T { + match *self {} + } } -impl From for LineNo { - fn from(line_no: usize) -> Self { - LineNo { - line_no: line_no, - } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Nil { } + +impl Iterator for Nil { + type Item = Elem; + + fn next(&mut self) -> Option { + None } } -pub type ArgumentIndex = usize; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Location { - line_no: LineNo, - argument_index: ArgumentIndex, - is_input: bool, -} - -impl LineNo { - pub fn in_at(&self, argument_index: usize) -> Location { - Location { - line_no: *self, - argument_index: argument_index, - is_input: true, - } - } - pub fn out_at(&self, argument_index: usize) -> Location { - Location { - line_no: *self, - argument_index: argument_index, - is_input: false, - } +// #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TUnit {} + +// #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TBool { +// get_bool: bool, +// } + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TNumber { +// number: serde_json::Number, +// } + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TBytes { +// bytes: Vec, +// } + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TString { +// string: String, +// } + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TArray { +// array: Vec, +// } + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TObject { +// object: serde_json::Map, +// } + +// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +// pub struct TJson { +// json: serde_json::Value, +// } + +// Deriving a string from the type without any values allows debugging TEq +pub trait TypeName { + fn type_name(x: PhantomData) -> &'static str; +} + +impl TypeName for () { // TUnit + fn type_name(_: PhantomData) -> &'static str { + "Unit" } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum BaseElemType { - Any, - Concat, - Index, - Slice, - ElemSymbol(ElemSymbol), + + + + + + + + + + + + + + +pub trait Teq { + type Other; + + fn transport(&self, x: T) -> Self::Other; + fn transport_sym(&self, x: Self::Other) -> T; } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct ElemTypeInfo { - base_elem_type: BaseElemType, - location: Location, +impl Teq for U { + type Other = T; + + fn transport(&self, x: T) -> Self::Other { + x + } + + fn transport_sym(&self, x: Self::Other) -> T { + x + } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct ElemType { - type_set: EnumSet, - info: Vec, +#[derive(Clone)] +pub struct TEq +{ + teq: Arc>, } -// Formatting: -// ``` -// ElemType { -// type_set: {A, B, C}, -// info: _, -// } -// ``` -// -// Results in: -// ``` -// {A, B, C} -// ``` -impl Display for ElemType { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, - "{{{}}}", - self.type_set.iter() - .fold(String::new(), - |memo, x| { - let x_str: &'static str = From::from(x); - if memo == "" { - x_str.to_string() - } else { - memo + ", " + &x_str.to_string() - } - } - )) +impl PartialEq for TEq { + fn eq(&self, _other: &Self) -> bool { + true } } -#[cfg(test)] -mod elem_type_display_tests { - use super::*; +impl Eq for TEq {} - #[test] - fn test_empty() { - let elem_type = ElemType { - type_set: EnumSet::empty(), - info: vec![], - }; - assert_eq!("{}", format!("{}", elem_type)); +impl TEq { + pub fn refl(_x: PhantomData) -> Self { + TEq { + teq: Arc::new(()), + } } +} - #[test] - fn test_singleton() { - for elem_symbol in EnumSet::all().iter() { - let elem_type = ElemType { - type_set: EnumSet::only(elem_symbol), - info: vec![], - }; - assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), - format!("{}", elem_type)); - } +impl TEq { + pub fn transport(&self, x: T) -> U { + (*self.teq).transport(x) } - #[test] - fn test_all() { - assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", - format!("{}", ElemType::any(vec![]))); + pub fn transport_sym(&self, x: U) -> T { + (*self.teq).transport_sym(x) } } -impl ElemSymbol { - pub fn elem_type(&self, locations: Vec) -> ElemType { - ElemType { - type_set: EnumSet::only(*self), - info: locations.iter() - .map(|&location| - ElemTypeInfo { - base_elem_type: BaseElemType::ElemSymbol(*self), - location: location, - }).collect(), - } +impl fmt::Debug for TEq { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "Teq {{ teq: Teq<{0}, {1}> }}", + TypeName::type_name(PhantomData::), + TypeName::type_name(PhantomData::)) } } -impl Elem { - pub fn elem_type(&self, locations: Vec) -> ElemType { - self.symbol().elem_type(locations) - } +#[derive(Clone, PartialEq, Eq)] +pub enum IsElem { + Unit(TEq), + Bool(TEq), + Number(TEq), + Bytes(TEq>), + String(TEq), + Array(TEq>), + Object(TEq>), + Json(TEq), } -impl ElemType { - fn from_locations(type_set: EnumSet, - base_elem_type: BaseElemType, - locations: Vec) -> Self { - ElemType { - type_set: type_set, - info: locations.iter() - .map(|&location| - ElemTypeInfo { - base_elem_type: base_elem_type, - location: location, - }).collect(), - } +impl fmt::Debug for IsElem { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "IsElem {}", TypeName::type_name(PhantomData::)) } +} - pub fn any(locations: Vec) -> Self { - Self::from_locations( - EnumSet::all(), - BaseElemType::Any, - locations) - } +// impl fmt::Debug for TEq { +// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { +// write!(f, "Teq {{ teq: Teq<{0}, {1}> }}", TypeName::type_name(PhantomData), TypeName::type_name(PhantomData)) +// } +// } - pub fn concat_type(locations: Vec) -> Self { - Self::from_locations( - enum_set!(ElemSymbol::Bytes | - ElemSymbol::String | - ElemSymbol::Array | - ElemSymbol::Object), - BaseElemType::Concat, - locations) + + + + +pub trait AnElem: Clone + std::fmt::Debug { + fn is_elem(x: PhantomData) -> IsElem where Self: Sized; +} + +// impl AnElem for Elem { + + +impl AnElem for () { + fn is_elem(_x: PhantomData) -> IsElem { + IsElem::Unit(TEq::refl(PhantomData)) } +} - pub fn index_type(locations: Vec) -> Self { - Self::from_locations( - enum_set!(ElemSymbol::Array | - ElemSymbol::Object), - BaseElemType::Index, - locations) +impl AnElem for bool { + fn is_elem(_x: PhantomData) -> IsElem { + IsElem::Bool(TEq::refl(PhantomData)) } +} - pub fn slice_type(locations: Vec) -> Self { - Self::concat_type(locations) +impl AnElem for Vec { + fn is_elem(_x: PhantomData) -> IsElem { + IsElem::Bytes(TEq::refl(PhantomData)) } +} - pub fn unify(&self, other: Self) -> Result { - let both = self.type_set.intersection(other.type_set); - if both.is_empty() { - Err(ElemTypeError::UnifyEmpty { - lhs: self.clone(), - rhs: other.clone(), - }) - } else { - let mut both_info = self.info.clone(); - both_info.append(&mut other.info.clone()); - Ok(ElemType { - type_set: both, - info: both_info, - }) - } +impl AnElem for String { + fn is_elem(_x: PhantomData) -> IsElem { + IsElem::String(TEq::refl(PhantomData)) } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeId { - type_id: usize, +impl AnElem for Value { + fn is_elem(_x: PhantomData) -> IsElem { + IsElem::Json(TEq::refl(PhantomData)) + } } -impl TypeId { - // TODO: test by checking: - // xs.map(TypeId).fold(x, offset) = TypeId(xs.fold(x, +)) - pub fn offset(&self, offset: TypeId) -> Self { - TypeId { - type_id: self.type_id + offset.type_id, - } +impl AnElem for Map { + fn is_elem(_x: PhantomData) -> IsElem { + IsElem::Object(TEq::refl(PhantomData)) } +} - pub fn update_type_id(&self, from: Self, to: Self) -> Self { - if *self == from { - to - } else { - *self +impl IsElem { + pub fn elem_symbol(&self) -> ElemSymbol { + match self { + Self::Unit(_) => ElemSymbol::Unit, + Self::Bool(_) => ElemSymbol::Bool, + Self::Number(_) => ElemSymbol::Number, + Self::Bytes(_) => ElemSymbol::Bytes, + Self::String(_) => ElemSymbol::String, + Self::Array(_) => ElemSymbol::Array, + Self::Object(_) => ElemSymbol::Object, + Self::Json(_) => ElemSymbol::Json, } } -} -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Context { - context: BTreeMap, - next_type_id: TypeId, + pub fn to_elem(&self, x: T) -> Elem { + match self { + Self::Unit(_) => Elem::Unit, + Self::Bool(eq) => Elem::Bool(eq.transport(x)), + Self::Number(eq) => Elem::Number(eq.transport(x)), + Self::Bytes(eq) => Elem::Bytes(eq.transport(x)), + Self::String(eq) => Elem::String(eq.transport(x)), + Self::Array(eq) => Elem::Array(eq.transport(x)), + Self::Object(eq) => Elem::Object(eq.transport(x)), + Self::Json(eq) => Elem::Json(eq.transport(x)), + } + } + + // TODO: from_elem + fn from_elem(self, x: Elem) -> Option { + match (self, x) { + (Self::Unit(eq), Elem::Unit) => Some(eq.transport_sym(())), + (Self::Bool(eq), Elem::Bool(x)) => Some(eq.transport_sym(x)), + // (Self::Number(eq), Elem::Number(x)) => Some(eq.transport_sym(x)), + // (Self::Bytes(eq), Elem::Bytes(x)) => Some(eq.transport_sym(x)), + // (Self::String(eq), Elem::String(x)) => Some(eq.transport_sym(x)), + // (Self::Array(eq), Elem::Array(x)) => Some(eq.transport_sym(x)), + // (Self::Object(eq), Elem::Object(x)) => Some(eq.transport_sym(x)), + // (Self::Json(eq), Elem::Json(x)) => Some(eq.transport_sym(x)), + _ => None, + } + } } -// Formatting: -// ``` -// Context { -// context: [ -// (t0, {A, B, C}), -// (t1, {B, C}), -// .. -// (tN, {D, E, F})], -// next_type_id: N+1, -// } -// ``` -// -// Results in: -// ``` -// ∀ (t0 ∊ {A, B, C}), -// ∀ (t1 ∊ {B, C}), -// .. -// ∀ (tN ∊ {D, E, F}), -// ``` -impl Display for Context { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, - "{}", - self.context.iter() - .fold(String::new(), |memo, (i, xs)| { - memo + - "\n" + - &format!("∀ (t{i} ∊ {xs}),", i = i.type_id, xs = xs).to_string() - })) - } + +pub trait ElemList: Clone + IntoIterator { + type Hd: AnElem; + type Tl: ElemList; + + fn is_empty(&self) -> bool; + fn hd(&self) -> Self::Hd; + fn tl(&self) -> Self::Tl; + fn cons(self, x: T) -> ConsElem where Self: Sized; + fn pop(x: PhantomData, stack: &mut Stack) -> Result; } -#[cfg(test)] -mod context_display_tests { - use super::*; +impl ElemList for Nil { + type Hd = (); + type Tl = Nil; - #[test] - fn test_empty() { - let big_type_id = TypeId { - type_id: 2^32 - }; - let context = Context { - context: BTreeMap::new(), - next_type_id: big_type_id, - }; - assert_eq!("", format!("{}", context)); + fn is_empty(&self) -> bool { + true } - #[test] - fn test_singleton() { - for elem_symbol in EnumSet::all().iter() { - let elem_type = ElemType { - type_set: EnumSet::only(elem_symbol), - info: vec![], - }; - let mut context_map = BTreeMap::new(); - context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); - let context = Context { - context: context_map, - next_type_id: TypeId { - type_id: 1, - }, - }; - assert_eq!(format!("\n∀ (t0 ∊ {}),", elem_type), format!("{}", context)); - } + fn hd(&self) -> Self::Hd { + () } -} -impl Context { - pub fn new() -> Self { - Context { - context: BTreeMap::new(), - next_type_id: TypeId { - type_id: 0, - }, - } + fn tl(&self) -> Self::Tl { + Self {} } - pub fn is_valid(&self) -> bool { - !self.context.keys().any(|x| *x >= self.next_type_id) + fn cons(self, x: T) -> ConsElem + where + Self: Sized, + { + ConsElem { + hd: x, + tl: self, + } } - pub fn size(&self) -> usize { - self.context.len() + fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { + Ok(Nil {}) } +} - pub fn push(&mut self, elem_type: ElemType) -> TypeId { - let push_id = self.next_type_id; - self.context.insert(push_id, elem_type); - self.next_type_id = TypeId { - type_id: push_id.type_id + 1, - }; - push_id - } +#[derive(Clone, PartialEq, Eq)] +pub struct ConsElem { + hd: T, + tl: U, +} - // NormalizeOnInvalidBasis is possible iff a `TypeId` in (basis) is repeated - // or missing from (self) - pub fn normalize_on(&self, basis: Vec) -> Result<(Self, TypeIdMap), ContextError> { - let mut source = self.clone(); - let mut result = Self::new(); - let mut type_map = TypeIdMap::new(); - for &type_id in &basis { - match source.context.remove(&type_id) { - None => Err(ContextError::NormalizeOnInvalidBasis { - type_id: type_id, - context: self.clone(), - basis: basis.clone().into_iter().collect(), - }), - Some(elem_type) => { - let new_type_id = result.next_type_id; - result.push(elem_type); - type_map.push(type_id, new_type_id)?; - Ok(()) - }, - }? +#[derive(Clone, PartialEq, Eq)] +pub struct IterConsElem { + cons: ConsElem, + at_head: bool, +} + +impl IntoIterator for ConsElem { + type Item = Elem; + type IntoIter = IterConsElem; + + fn into_iter(self) -> Self::IntoIter { + IterConsElem { + cons: self, + at_head: true, } - Ok((result, type_map)) } +} - pub fn offset(&self, offset: TypeId) -> Self { - Context { - context: self.context.iter().map(|(k, x)| (k.offset(offset), x.clone())).collect(), - next_type_id: self.next_type_id.offset(offset), +impl Iterator for IterConsElem { + type Item = Elem; + + fn next(&mut self) -> Option { + if self.at_head { + Some(AnElem::is_elem(PhantomData::).to_elem(self.cons.hd.clone())) + } else { + let self_cons = self.cons.clone(); + *self = self_cons.into_iter(); + self.next() } } +} - pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), ContextError> { - if self.context.contains_key(&from) { - Ok(()) - } else { - Err(ContextError::UpdateTypeIdFromMissing { - from: from, - to: to, - context: self.clone(), - }) - }?; - if self.context.contains_key(&to) { - Err(ContextError::UpdateTypeIdToPresent { - from: from, - to: to, - context: self.clone(), - }) - } else { - Ok(()) - }?; - self.context = self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(); - self.next_type_id = cmp::max(self.next_type_id, to); - Ok(()) +impl ElemList for ConsElem { + type Hd = T; + type Tl = U; + + fn is_empty(&self) -> bool { + false } - pub fn disjoint_union(&mut self, other: Self) -> Result<(), ContextError> { - for (&type_id, elem_type) in other.context.iter() { - match self.context.insert(type_id, elem_type.clone()) { - None => { - Ok(()) - }, - Some(conflicting_elem_type) => Err(ContextError::DisjointUnion { - type_id: type_id, - elem_type: elem_type.clone(), - conflicting_elem_type: conflicting_elem_type, - lhs: self.clone(), - rhs: other.clone(), - }), - }? - } - self.next_type_id = cmp::max(self.next_type_id, other.next_type_id); - Ok(()) + fn hd(&self) -> Self::Hd { + self.hd.clone() } - pub fn get(&mut self, index: &TypeId, error: &dyn Fn() -> ContextError) -> Result { - Ok(self.context.get(index).ok_or_else(|| ContextError::GetUnknownTypeId { - context: self.clone(), - index: *index, - error: Box::new(error()), - })?.clone()) + fn tl(&self) -> Self::Tl { + self.tl.clone() } - // unify the types of two TypeId's into the rhs - // removing the lhs - pub fn unify(&mut self, xi: TypeId, yi: TypeId) -> Result<(), ContextError> { - let x_type = self.context.remove(&xi).ok_or_else(|| ContextError::Unify { - xs: self.clone(), - xi: xi.clone(), - yi: yi.clone(), - is_lhs: true, - })?; - let y_type = self.context.remove(&yi).ok_or_else(|| ContextError::Unify { - xs: self.clone(), - xi: xi.clone(), - yi: yi.clone(), - is_lhs: false, - })?; - let xy_type = x_type.unify(y_type).or_else(|e| Err(ContextError::UnifyElemType { - xs: self.clone(), - xi: xi.clone(), - yi: yi.clone(), - error: e, - }))?; - self.context.insert(yi, xy_type); - Ok(()) + fn cons(self, x: V) -> ConsElem + where + Self: Sized, + { + ConsElem { + hd: x, + tl: self, + } + } + + // TODO: add better errors + fn pop(_x: PhantomData, stack: &mut Stack) -> Result { + let hd_elem = stack.pop()?; + Ok(ConsElem { + hd: AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { + expected: AnElem::is_elem(PhantomData::).elem_symbol(), + found: hd_elem.clone(), + stack: stack.clone(), + })?, + tl: Self::Tl::pop(PhantomData, stack)?, + }) } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Type { - context: Context, - i_type: Vec, - o_type: Vec, +pub trait FromElemList { + type AsElemList: ElemList; + fn to_hlist_type(x: PhantomData) -> PhantomData; + // fn to_hlist(&self) -> Self::AsElemList; + fn from_hlist(t: PhantomData, x: Self::AsElemList) -> Self; } -impl Type { - pub fn id() -> Self { - Type { - context: Context::new(), - i_type: vec![], - o_type: vec![], - } - } +impl FromElemList for () { + type AsElemList = Nil; + fn to_hlist_type(_x: PhantomData) -> PhantomData { PhantomData } + fn from_hlist(_t: PhantomData, _x: Self::AsElemList) -> Self { () } +} - pub fn next_type_id(&self) -> TypeId { - self.context.next_type_id +impl FromElemList for (T, U) +where + T: AnElem, + U: AnElem, +{ + type AsElemList = ConsElem>; + fn to_hlist_type(_x: PhantomData) -> PhantomData { PhantomData } + // fn to_hlist(&self) -> Self::AsElemList { + // Nil.cons(self.0).cons(self.1) + // } + + fn from_hlist(_t: PhantomData, x: Self::AsElemList) -> Self { + (x.hd(), x.tl().hd()) } +} - // check whether all the TypeId's are valid - pub fn is_valid(&self) -> bool { - let next_type_id = self.next_type_id(); - self.context.is_valid() && - !(self.i_type.iter().any(|x| *x >= next_type_id) || - self.o_type.iter().any(|x| *x >= next_type_id)) - } - - // equivalent to running update_type_id w/ offset from largest to smallest - // existing TypeId - pub fn offset(&self, offset: TypeId) -> Self { - Type { - context: self.context.offset(offset), - i_type: self.i_type.iter().map(|x| x.offset(offset)).collect(), - o_type: self.o_type.iter().map(|x| x.offset(offset)).collect(), - } - } +impl FromElemList for ConsElem +where + T: AnElem, + U: ElemList, +{ + type AsElemList = Self; + fn to_hlist_type(_x: PhantomData) -> PhantomData { PhantomData } + // fn to_hlist(&self) -> Self::AsElemList { + // Nil.cons(self.0).cons(self.1) + // } - pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeError> { - self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?; - self.i_type = self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(); - self.o_type = self.o_type.iter().map(|x| x.update_type_id(from, to)).collect(); - Ok(()) - } + fn from_hlist(_t: PhantomData, x: Self::AsElemList) -> Self { x } +} - pub fn normalize(&self) -> Result { - let mut basis = self.i_type.clone(); - basis.append(&mut self.o_type.clone()); - basis.dedup(); - let (new_context, type_map) = self.context.normalize_on(basis).map_err(|e| TypeError::ContextError(e))?; - Ok(Type { - context: new_context, - i_type: type_map.run(self.i_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, - o_type: type_map.run(self.o_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, - }) - } - // f : self - // g : other - // self.compose(other) : (f ++ g).type_of() - // - // input -> - // other.i_type - // other.o_type - // self.i_type - // self.o_type - // -> output - // - // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context - // 2. collect the remainder and add them to the context - // 3. add the remainder to (self.i_type, other.o_type), with replaced variables - pub fn compose(&self, other: Self) -> Result { - println!(""); - println!("composing:\n{0}\n\nAND\n{1}\n", self, other); - let mut context = self.context.clone(); - // println!("context: {}", context); - // println!("context.next_type_id: {:?}", context.next_type_id.type_id); +// TODO: add necessary traits, methods and rename to IsInstructionError or IsInstrError +pub trait AnError: std::fmt::Debug { + // fn to_stack_error(&self, line_no: LineNo) -> StackError; +} - let offset_other = other.offset(self.next_type_id()); - // println!("offset_other: {}", offset_other); +impl StackError { + fn instruction_default(name: &str, error_str: &str, line_no: LineNo) -> Self { + Self::RunInstruction { + name: name.to_string(), + error: error_str.to_string(), + line_no: line_no, + } + } +} - context.disjoint_union(offset_other.context.clone()) - .map_err(|e| TypeError::ContextError(e))?; - // println!("context union: {}", context); +impl AnError for Empty { + // fn to_stack_error(&self, _line_no: LineNo) -> StackError { + // self.absurd(PhantomData) + // } +} - let mut mut_offset_other = offset_other.clone(); - let mut zip_len = 0; - let other_o_type = offset_other.o_type.iter().clone(); - let self_i_type = self.i_type.iter().clone(); - other_o_type.zip(self_i_type).try_for_each(|(&o_type, &i_type)| { - zip_len += 1; - context - .unify(o_type, i_type) - .map_err(|e| TypeError::ContextError(e))?; - mut_offset_other - .update_type_id(o_type, i_type)?; - Ok(()) - })?; +// TODO: add Default, etc +pub trait IsInstruction: std::fmt::Debug { + type In: FromElemList; + type Out: AnElem; + type Error: AnError; - Ok(Type { - context: context, - i_type: mut_offset_other.i_type.iter().chain(self.i_type.iter().skip(zip_len)).copied().collect(), - o_type: self.o_type.iter().chain(mut_offset_other.o_type.iter().skip(zip_len)).copied().collect(), - }) - } + fn run(&self, x: Self::In) -> Result; } -// Formatting: -// ``` -// Type { -// context: Context { -// context: [ -// (t0, {A, B, C}), -// (t1, {B, C}), -// .. -// (tN, {D, E, F})], -// next_type_id: N+1, -// }, -// i_type: [0, 1, .., N], -// 0_type: [i, j, .., k], -// } -// ``` -// -// Results in: -// ``` -// ∀ (t0 ∊ {A, B, C}), -// ∀ (t1 ∊ {B, C}), -// .. -// ∀ (tN ∊ {D, E, F}), -// [t0, t1, .., tN] -> -// [ti, tj, .., tk] -// ``` -impl Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - // let self_normalized = self.normalize().map_err(|_| fmt::Error)?; - let self_normalized = self; - write!(f, - "{context}\n[{i_type}] ->\n[{o_type}]", - context = self_normalized.context, - i_type = self_normalized.i_type.iter().fold(String::new(), |memo, x| { - let x_str = format!("t{}", x.type_id); - if memo == "" { - x_str - } else { - memo + ", " + &x_str.to_string() - }}), - o_type = self_normalized.o_type.iter().fold(String::new(), |memo, x| { - let x_str = format!("t{}", x.type_id); - if memo == "" { - x_str - } else { - memo + ", " + &x_str.to_string() - }})) +impl Stack { + fn run_instruction(&mut self, instr: T, line_no: LineNo) -> Result<(), StackError> { + let input = ElemList::pop(FromElemList::to_hlist_type(PhantomData::), self)?; + let output = instr.run(FromElemList::from_hlist(PhantomData::, input)).map_err(|e| StackError::RunInstruction { + name: format!("{:?}", instr), + error: format!("{:?}", e), + line_no: line_no})?; + Ok(self.push(AnElem::is_elem(PhantomData::).to_elem(output))) } } -#[cfg(test)] -mod type_display_tests { - use super::*; - #[test] - fn test_empty() { - let big_type_id = TypeId { - type_id: 2^32 - }; - let context = Context { - context: BTreeMap::new(), - next_type_id: big_type_id, - }; - let example_type = Type { - context: context, - i_type: vec![], - o_type: vec![], - }; - assert_eq!("\n[] ->\n[]", format!("{}", example_type)); - } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct AssertTrue {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct AssertTrueError {} +impl AnError for AssertTrueError {} - #[test] - fn test_singleton() { - for elem_symbol in EnumSet::all().iter() { - let elem_type = ElemType { - type_set: EnumSet::only(elem_symbol), - info: vec![], - }; - let mut context_map = BTreeMap::new(); - context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); - let context = Context { - context: context_map, - next_type_id: TypeId { - type_id: 1, - }, - }; - let example_type = Type { - context: context, - i_type: vec![TypeId { type_id: 0 }, TypeId { type_id: 0 }], - o_type: vec![TypeId { type_id: 0 }], - }; - assert_eq!(format!("\n∀ (t0 ∊ {}),\n[t0, t0] ->\n[t0]", elem_type), format!("{}", example_type)); +impl IsInstruction for AssertTrue { + type In = ConsElem; + type Out = bool; + type Error = AssertTrueError; + + fn run(&self, x: Self::In) -> Result { + if x.hd() { + Ok(x.hd()) + } else { + Err(AssertTrueError {}) } } } - -impl Restack { - // TODO: fix locations: out locations are mislabeled as in locations - pub fn type_of(&self, line_no: LineNo) -> Result { - let mut context = Context::new(); - let mut restack_type: Vec = (0..self.restack_depth) - .map(|x| context.push(ElemType::any(vec![line_no.in_at(x)]))) - .collect(); - Ok(Type { - context: context, - i_type: restack_type.clone(), - o_type: self.run(&mut restack_type)?, - }) - } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Push { + push: T, } -/// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] -/// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] -/// HashSha256, // : [ bytes ] -> [ bytes ] -/// CheckLe, // : [ x, x ] -> [ bool ] -/// CheckLt, // : [ x, x ] -> [ bool ] -/// CheckEq, // : [ x, x ] -> [ bool ] -/// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] -/// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] -/// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] -/// Lookup, // [ string, object ] -> [ json ] -/// AssertTrue, // [ bool ] -> [] -/// ToJson, // (t: type) : [ t ] -> [ json ] -/// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] -/// StringToBytes, // [ string ] -> [ bytes ] -impl Instruction { - pub fn type_of(&self, line_no: LineNo) -> Result { - match self { - Instruction::Restack(restack) => - Ok(restack - .type_of(line_no) - .or_else(|e| Err(TypeError::InstructionTypeOfRestack(e)))?), - - Instruction::AssertTrue => { - let mut context = Context::new(); - let bool_var = context - .push(ElemSymbol::Bool - .elem_type(vec![line_no.in_at(0)])); - Ok(Type { - context: context, - i_type: vec![bool_var], - o_type: vec![], - }) - }, +impl IsInstruction for Push { + type In = (); + type Out = T; + type Error = Empty; - Instruction::Push(elem) => { - let mut context = Context::new(); - let elem_var = context - .push(elem.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![], - o_type: vec![elem_var], - }) - }, + fn run(&self, _x: Self::In) -> Result { + Ok(self.push.clone()) + } +} - Instruction::HashSha256 => { - let mut context = Context::new(); - let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.in_at(0), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![bytes_var], - o_type: vec![bytes_var], - }) - }, - Instruction::ToJson => { - let mut context = Context::new(); - let any_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_var], - o_type: vec![json_var], - }) - }, - Instruction::StringToBytes => { - let mut context = Context::new(); - let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); - let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![string_var], - o_type: vec![bytes_var], - }) - }, - Instruction::UnpackJson(elem_symbol) => { - let mut context = Context::new(); - let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.in_at(0)])); - let elem_symbol_var = context.push(elem_symbol.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![json_var], - o_type: vec![elem_symbol_var], - }) - }, - Instruction::CheckLe => { - let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); - let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_lhs_var, any_rhs_var], - o_type: vec![bool_var], - }) - }, +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct HashSha256 {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct HashSha256Error {} +impl AnError for HashSha256Error {} - Instruction::CheckLt => { - let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); - let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_lhs_var, any_rhs_var], - o_type: vec![bool_var], - }) - }, +impl IsInstruction for HashSha256 { + type In = ConsElem, Nil>; + type Out = Vec; + type Error = Empty; - Instruction::CheckEq => { - let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); - let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_lhs_var, any_rhs_var], - o_type: vec![bool_var], - }) - }, + fn run(&self, x: Self::In) -> Result { + Ok(super::sha256(&x.hd())) + } +} - Instruction::Concat => { - let mut context = Context::new(); - let concat_var = context.push(ElemType::concat_type(vec![line_no.in_at(0), line_no.in_at(1), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![concat_var, concat_var], - o_type: vec![concat_var], - }) - }, - Instruction::Index => { - let mut context = Context::new(); - let number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); - let index_var = context.push(ElemType::index_type(vec![line_no.in_at(1), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![number_var, index_var], - o_type: vec![index_var], - }) - }, +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Concat { + t: PhantomData, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct ConcatError {} +impl AnError for ConcatError {} - Instruction::Lookup => { - let mut context = Context::new(); - let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); - let object_var = context.push(ElemSymbol::Object.elem_type(vec![line_no.in_at(1), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![string_var, object_var], - o_type: vec![object_var], - }) - }, +impl::Item>> IsInstruction for Concat { + type In = (T, T); + type Out = T; + type Error = Empty; - Instruction::Slice => { - let mut context = Context::new(); - let offset_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); - let length_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(1)])); - let slice_var = context.push(ElemType::slice_type(vec![line_no.in_at(2), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![offset_number_var, length_number_var, slice_var], - o_type: vec![slice_var], - }) - }, - }.or_else(|e| Err(TypeError::InstructionTypeOfDetail { - instruction: self.clone(), - error: Box::new(e), - })) + fn run(&self, x: Self::In) -> Result { + let (lhs, rhs) = x; + Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) } } -// TODO: split up TypeError -// TODO: add layers of detail to TypeIdMapGetUnknownTypeId + // pub fn concat(self, other: Self) -> Result { + // match (self, other) { + // (Self::Bytes(x), Self::Bytes(y)) => Ok(Self::Bytes(Self::concat_generic(x, y))), + // (Self::String(x), Self::String(y)) => { + // Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) + // .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) + // }, + // (Self::Array(x), Self::Array(y)) => Ok(Self::Array(Self::concat_generic(x, y))), + // (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), + // (some_x, some_y) => { + // Err(ElemError::ConcatUnsupportedTypes { + // lhs: some_x.symbol_str(), + // rhs: some_y.symbol_str() + // }) + // }, + // } + // } -#[derive(Debug, PartialEq, Error)] -pub enum ElemTypeError { - #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] - UnifyEmpty { - lhs: ElemType, - rhs: ElemType, - // location: TyUnifyLocation, - }, + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Slice {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct SliceError {} +impl AnError for SliceError {} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Index {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct IndexError {} +impl AnError for IndexError {} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Lookup {} +#[derive(Clone, Debug, PartialEq, Eq)] +struct LookupError { + key: String, + map: Map, } +impl AnError for LookupError {} -#[derive(Debug, PartialEq, Error)] -pub enum TypeIdMapError { - #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] - GetUnknownTypeId { - index: TypeId, - location: usize, - type_map: TypeIdMap, - }, +impl IsInstruction for Lookup { + type In = (String, Map); + type Out = Value; + type Error = LookupError; - #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] - PushExists { - from: TypeId, - to: TypeId, - map: TypeIdMap, - }, + fn run(&self, x: Self::In) -> Result { + let (key, map) = x; + Ok(map.get(&key) + .ok_or_else(|| LookupError { + key: key, + map: map.clone(), + })?.clone()) + } } -#[derive(Debug, PartialEq, Error)] -pub enum ContextError { - #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}, error: {error:?}")] - GetUnknownTypeId { - context: Context, - index: TypeId, - error: Box, - }, - #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, / - with type_id: {type_id:?}, and elem_type: {elem_type:?}, conflicted / - with lhs entry conflicting_elem_type: {conflicting_elem_type:?}")] - DisjointUnion { - type_id: TypeId, - elem_type: ElemType, - conflicting_elem_type: ElemType, - lhs: Context, - rhs: Context, - }, +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct UnpackJson { + t: PhantomData, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct UnpackJsonError {} +impl AnError for UnpackJsonError {} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct StringToBytes {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct StringToBytesError {} +impl AnError for StringToBytesError {} + + + +// TODO: POLYMORPHIC W/ ANY: PERHAPS Elem: AnElem ?? +// +// ideas: +// 1. use macros when it's a trait +// 2. gradual typing: allow Elem to be AnElem + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckLe {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckLeError {} +impl AnError for CheckLeError {} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckLt {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckLtError {} +impl AnError for CheckLtError {} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckEq {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckEqError {} +impl AnError for CheckEqError {} + + + + + + // pub fn check_le(self, other: Self) -> Result { + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckLeIncomparableTypes { + // lhs: self.symbol_str(), + // rhs: other.symbol_str() })? { + // cmp::Ordering::Less => true, + // cmp::Ordering::Equal => true, + // cmp::Ordering::Greater => false, + // }; + // Ok(Self::Bool(result)) + // } + + // pub fn check_lt(self, other: Self) -> Result { + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckLtIncomparableTypes { + // lhs: self.symbol_str(), + // rhs: other.symbol_str() })? { + // cmp::Ordering::Less => true, + // _ => false, + // }; + // Ok(Self::Bool(result)) + // } + + // pub fn check_eq(self, other: Self) -> Result { + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckEqIncomparableTypes { + // lhs: self.symbol_str(), + // rhs: other.symbol_str() })? { + // cmp::Ordering::Equal => true, + // _ => false, + // }; + // Ok(Self::Bool(result)) + // } + + // fn slice_generic::Item>>(offset: Number, + // length: Number, + // iterable: T, + // elem_symbol: ElemSymbol) -> + // Result { + // let u_offset = offset.as_u64() + // .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) + // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + // let u_length = length.as_u64() + // .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) + // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + // let u_offset_plus_length = u_offset.checked_add(u_length) + // .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; + // if iterable.clone().into_iter().count() < u_offset_plus_length { + // Err(ElemError::SliceTooShort { + // offset: u_offset, + // length: u_length, + // iterable: From::from(elem_symbol), + // }) + // } else { + // Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) + // } + // } + + // pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { + // match (maybe_offset, maybe_length, maybe_iterable) { + // (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => + // Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), + // (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { + // let iterator_vec = Vec::from(iterator.clone()); + // Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) + // .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) + // }, + // (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => + // Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), + // (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => + // Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), + // (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { + // Err(ElemError::SliceUnsupportedTypes { + // maybe_not_offset: maybe_not_offset.symbol_str(), + // maybe_not_length: maybe_not_length.symbol_str(), + // maybe_not_iterable: maybe_not_iterable.symbol_str(), + // }) + // } + // } + // } + + // fn index_generic::Item>>(index: Number, + // iterable: T, + // elem_symbol: ElemSymbol) -> + // Result<::Item, ElemError> { + // let u_index: usize = index.as_u64() + // .ok_or_else(|| ElemError::IndexNotU64(index.clone())) + // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; + // if iterable.clone().into_iter().count() <= u_index { + // return Err(ElemError::IndexTooShort { + // index: u_index, + // iterable: From::from(elem_symbol), + // }) + // } else { + // match iterable.into_iter().skip(u_index).next() { + // None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), + // Some(x) => Ok(x), + // } + // } + // } + + // pub fn index(self, maybe_iterable: Self) -> Result { + // match (self, maybe_iterable) { + // // (Self::Number(index), Self::Bytes(iterator)) => + // // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), + // (Self::Number(index), Self::Array(iterator)) => + // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), + // (Self::Number(index), Self::Object(iterator)) => + // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), + // (maybe_not_index, maybe_not_iterable) => { + // Err(ElemError::IndexUnsupportedTypes { + // maybe_not_index: maybe_not_index.symbol_str(), + // maybe_not_iterable: maybe_not_iterable.symbol_str(), + // }) + // } + // } + // } + + // pub fn to_json(self) -> Result { + // Ok(Self::Json(serde_json::to_value(self)?)) + // } + + // pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { + // match (self, elem_symbol) { + // (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), + // (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), + // (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), + // (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), + // (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), + // (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), + // (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { + // json: json, + // elem_symbol: From::from(elem_symbol), + // }), + // (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { + // non_json: non_json.symbol_str(), + // elem_symbol: From::from(elem_symbol), + // }), + // } + // } + + // pub fn string_to_bytes(self) -> Result { + // match self { + // Self::String(x) => Ok(Self::Bytes(x.into_bytes())), + // other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), + // } + // } + + + + + + +// ToJson, +// Restack(Restack), + +// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +// pub enum Instruction { +// Push(Elem), +// Restack(Restack), +// HashSha256, +// CheckLe, +// CheckLt, +// CheckEq, +// Concat, +// Slice, +// Index, +// Lookup, +// AssertTrue, +// ToJson, +// UnpackJson(ElemSymbol), +// StringToBytes, +// } + + + +// pub fn demo_triple() -> ConsElem<(), TBool, ConsElem<(), TUnit, ConsElem<(), TBool, Nil<()>>>> { +// Nil { t: PhantomData } +// .cons(TBool { get_bool: true }) +// .cons(TUnit { }) +// .cons(TBool { get_bool: false }) +// } + +// pub fn demo_triple_with_tl_handles_intermediate_types() -> ConsElem<(), TBool, ConsElem<(), TUnit, ConsElem<(), TBool, Nil<()>>>> { +// Nil { t: PhantomData } +// .cons(TBool { get_bool: true }) +// .cons(TUnit { }) +// .cons(TBool { get_bool: false }) +// .cons(TBool { get_bool: true }) +// .cons(TUnit { }) +// .tl() +// .tl() +// } + + + - #[error("Context::normalize_on applied to invalid basis: type_id: {type_id:?}, context: {context:?}, basis: {basis:?}")] - NormalizeOnInvalidBasis { - type_id: TypeId, - context: Context, - basis: Vec, - }, - #[error("Context::update_type_id called on missing 'from: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] - UpdateTypeIdFromMissing { - from: TypeId, - to: TypeId, - context: Context, - }, - #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] - UpdateTypeIdToPresent { - from: TypeId, - to: TypeId, - context: Context, - }, - #[error("Context::unify failed:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n is_lhs: {is_lhs:?}\n")] - Unify { - xs: Context, - xi: TypeId, - yi: TypeId, - is_lhs: bool, - }, - #[error("Context::unify failed to unify ElemType's:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n elem_error: {error:?}\n")] - UnifyElemType { - xs: Context, - xi: TypeId, - yi: TypeId, - error: ElemTypeError, - }, - #[error("Context::normalize_on building TypeIdMap failed: {0:?}")] - TypeIdMapError(TypeIdMapError), -} -impl From for ContextError { - fn from(error: TypeIdMapError) -> Self { - Self::TypeIdMapError(error) - } -} -#[derive(Debug, PartialEq, Error)] -pub enum TypeError { - #[error("ContextError {0}")] - ContextError(ContextError), - #[error("TypeError::update_type_id failed when updating the Context: {0}")] - UpdateTypeId(ContextError), - #[error("TypeError::compose disjoint_union {0}")] - ComposeDisjointUnion(ContextError), - #[error("Instruction::type_of resulted in restack error: {0:?}")] - InstructionTypeOfRestack(RestackError), - #[error("Instruction::type_of resulted in an error involving: {instruction:?};\n {error:?}")] - InstructionTypeOfDetail { - instruction: Instruction, - error: Box, - }, - #[error("Instructions::type_of called on an empty Vec of Instruction's")] - InstructionsTypeOfEmpty, - #[error("Instructions::type_of resulted in an error on line: {line_no:?};\n {error:?}")] - InstructionsTypeOfLineNo { - line_no: usize, - error: Box, - }, - #[error("Type::normalize applying TypeIdMap failed: {0:?}")] - TypeIdMapError(TypeIdMapError), -} -// pub type Stack = Vec; -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -pub struct Instructions { - pub instructions: Vec, -} -impl IntoIterator for Instructions { - type Item = Instruction; - type IntoIter = as std::iter::IntoIterator>::IntoIter; - fn into_iter(self) -> Self::IntoIter { - self.instructions.into_iter() - } -} -impl Instructions { - pub fn type_of(&self) -> Result { - let mut current_type = Type::id(); - for (i, instruction) in self.instructions.iter().enumerate() { - current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) - .or_else(|e| Err(TypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location - line_no: i, - error: Box::new(e), - }))?; - println!("line {i}: {current_type}", i = i, current_type = current_type); - } - Ok(current_type) - } -} -// Test program #1: [] -> [] -// -// Instruction::Push(Elem::Bool(true)), -// Instruction::Restack(Restack::id()), -// Instruction::AssertTrue, -// Test program #2 -// -// ∀ (t0 ∊ {JSON}), -// ∀ (t1 ∊ {JSON}), -// ∀ (t2 ∊ {Object}), -// [t1] -> -// [t0, t2, t1] -// -// Instruction::Push(Elem::Json(Default::default())), -// Instruction::UnpackJson(ElemSymbol::Object), -// Instruction::Restack(Restack::dup()), -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeIdMap { - map: BTreeMap, -} -impl TypeIdMap { - pub fn new() -> Self { - TypeIdMap { - map: BTreeMap::new(), - } - } - pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { - if self.map.contains_key(&from) { - Err(TypeIdMapError::PushExists { - from: from, - to: to, - map: self.clone(), - }) - } else { - self.map.insert(from, to); - Ok(()) - } - } - pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { - self.map.get(index) - .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { - index: index.clone(), - location: location, - type_map: self.clone(), - }) - } - pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { - type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() - } -} ////// //////////// @@ -1179,321 +984,770 @@ impl TypeIdMap { ////// -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TUnit {} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TBool { - get_bool: bool, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TNumber { - number: serde_json::Number, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TBytes { - bytes: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TString { - string: String, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TArray { - array: Vec, -} +// typing: +// - unification +// + inference +// + checking against inferred or other type (this + inference = bidirecitonal) +// - two categories of tests: +// + property tests for typing methods themselves +// + test that a function having a particular type -> it runs w/o type errors on such inputs -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TObject { - object: serde_json::Map, +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum BaseElemType { + Any, + Concat, + Index, + Slice, + ElemSymbol(ElemSymbol), } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct TJson { - json: serde_json::Value, +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemTypeInfo { + base_elem_type: BaseElemType, + location: Location, } - - - - -// Deriving a string from the type without any values allows debugging TEq -pub trait TypeName { - fn type_name(x: PhantomData) -> &'static str; +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemType { + type_set: EnumSet, + info: Vec, } -impl TypeName for TUnit { - fn type_name(_: PhantomData) -> &'static str { - "Unit" +// Formatting: +// ``` +// ElemType { +// type_set: {A, B, C}, +// info: _, +// } +// ``` +// +// Results in: +// ``` +// {A, B, C} +// ``` +impl Display for ElemType { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{{{}}}", + self.type_set.iter() + .fold(String::new(), + |memo, x| { + let x_str: &'static str = From::from(x); + if memo == "" { + x_str.to_string() + } else { + memo + ", " + &x_str.to_string() + } + } + )) } } +#[cfg(test)] +mod elem_type_display_tests { + use super::*; -pub trait Teq { - type Other; + #[test] + fn test_empty() { + let elem_type = ElemType { + type_set: EnumSet::empty(), + info: vec![], + }; + assert_eq!("{}", format!("{}", elem_type)); + } - fn transport(&self, x: T) -> Self::Other; + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), + format!("{}", elem_type)); + } + } + + #[test] + fn test_all() { + assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", + format!("{}", ElemType::any(vec![]))); + } } -impl Teq for U { - type Other = T; +impl ElemSymbol { + pub fn elem_type(&self, locations: Vec) -> ElemType { + ElemType { + type_set: EnumSet::only(*self), + info: locations.iter() + .map(|&location| + ElemTypeInfo { + base_elem_type: BaseElemType::ElemSymbol(*self), + location: location, + }).collect(), + } + } +} - fn transport(&self, x: T) -> Self::Other { - x +impl Elem { + pub fn elem_type(&self, locations: Vec) -> ElemType { + self.symbol().elem_type(locations) } } -#[derive(Clone)] -pub struct TEq -{ - teq: Arc>, +impl ElemType { + fn from_locations(type_set: EnumSet, + base_elem_type: BaseElemType, + locations: Vec) -> Self { + ElemType { + type_set: type_set, + info: locations.iter() + .map(|&location| + ElemTypeInfo { + base_elem_type: base_elem_type, + location: location, + }).collect(), + } + } + + pub fn any(locations: Vec) -> Self { + Self::from_locations( + EnumSet::all(), + BaseElemType::Any, + locations) + } + + pub fn concat_type(locations: Vec) -> Self { + Self::from_locations( + enum_set!(ElemSymbol::Bytes | + ElemSymbol::String | + ElemSymbol::Array | + ElemSymbol::Object), + BaseElemType::Concat, + locations) + } + + pub fn index_type(locations: Vec) -> Self { + Self::from_locations( + enum_set!(ElemSymbol::Array | + ElemSymbol::Object), + BaseElemType::Index, + locations) + } + + pub fn slice_type(locations: Vec) -> Self { + Self::concat_type(locations) + } + + pub fn unify(&self, other: Self) -> Result { + let both = self.type_set.intersection(other.type_set); + if both.is_empty() { + Err(ElemTypeError::UnifyEmpty { + lhs: self.clone(), + rhs: other.clone(), + }) + } else { + let mut both_info = self.info.clone(); + both_info.append(&mut other.info.clone()); + Ok(ElemType { + type_set: both, + info: both_info, + }) + } + } } -impl PartialEq for TEq { - fn eq(&self, _other: &Self) -> bool { - true +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct TypeId { + type_id: usize, +} + +impl TypeId { + // TODO: test by checking: + // xs.map(TypeId).fold(x, offset) = TypeId(xs.fold(x, +)) + pub fn offset(&self, offset: TypeId) -> Self { + TypeId { + type_id: self.type_id + offset.type_id, + } + } + + pub fn update_type_id(&self, from: Self, to: Self) -> Self { + if *self == from { + to + } else { + *self + } } } -impl Eq for TEq {} +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Context { + context: BTreeMap, + next_type_id: TypeId, +} -impl TEq { - pub fn transport(&self, x: T) -> U { - (*self.teq).transport(x) + +// Formatting: +// ``` +// Context { +// context: [ +// (t0, {A, B, C}), +// (t1, {B, C}), +// .. +// (tN, {D, E, F})], +// next_type_id: N+1, +// } +// ``` +// +// Results in: +// ``` +// ∀ (t0 ∊ {A, B, C}), +// ∀ (t1 ∊ {B, C}), +// .. +// ∀ (tN ∊ {D, E, F}), +// ``` +impl Display for Context { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{}", + self.context.iter() + .fold(String::new(), |memo, (i, xs)| { + memo + + "\n" + + &format!("∀ (t{i} ∊ {xs}),", i = i.type_id, xs = xs).to_string() + })) } +} - // // TODO: compose - // pub fn compose(&self, uv: TEq) -> TEq { - // TEq { - // teq: Box::new(()), - // } - // } +#[cfg(test)] +mod context_display_tests { + use super::*; + + #[test] + fn test_empty() { + let big_type_id = TypeId { + type_id: 2^32 + }; + let context = Context { + context: BTreeMap::new(), + next_type_id: big_type_id, + }; + assert_eq!("", format!("{}", context)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + let mut context_map = BTreeMap::new(); + context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); + let context = Context { + context: context_map, + next_type_id: TypeId { + type_id: 1, + }, + }; + assert_eq!(format!("\n∀ (t0 ∊ {}),", elem_type), format!("{}", context)); + } + } } -impl TEq { - pub fn refl(_x: PhantomData) -> Self { - TEq { - teq: Arc::new(()), +impl Context { + pub fn new() -> Self { + Context { + context: BTreeMap::new(), + next_type_id: TypeId { + type_id: 0, + }, + } + } + + pub fn is_valid(&self) -> bool { + !self.context.keys().any(|x| *x >= self.next_type_id) + } + + pub fn size(&self) -> usize { + self.context.len() + } + + pub fn push(&mut self, elem_type: ElemType) -> TypeId { + let push_id = self.next_type_id; + self.context.insert(push_id, elem_type); + self.next_type_id = TypeId { + type_id: push_id.type_id + 1, + }; + push_id + } + + // NormalizeOnInvalidBasis is possible iff a `TypeId` in (basis) is repeated + // or missing from (self) + pub fn normalize_on(&self, basis: Vec) -> Result<(Self, TypeIdMap), ContextError> { + let mut source = self.clone(); + let mut result = Self::new(); + let mut type_map = TypeIdMap::new(); + for &type_id in &basis { + match source.context.remove(&type_id) { + None => Err(ContextError::NormalizeOnInvalidBasis { + type_id: type_id, + context: self.clone(), + basis: basis.clone().into_iter().collect(), + }), + Some(elem_type) => { + let new_type_id = result.next_type_id; + result.push(elem_type); + type_map.push(type_id, new_type_id)?; + Ok(()) + }, + }? + } + Ok((result, type_map)) + } + + pub fn offset(&self, offset: TypeId) -> Self { + Context { + context: self.context.iter().map(|(k, x)| (k.offset(offset), x.clone())).collect(), + next_type_id: self.next_type_id.offset(offset), + } + } + + pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), ContextError> { + if self.context.contains_key(&from) { + Ok(()) + } else { + Err(ContextError::UpdateTypeIdFromMissing { + from: from, + to: to, + context: self.clone(), + }) + }?; + if self.context.contains_key(&to) { + Err(ContextError::UpdateTypeIdToPresent { + from: from, + to: to, + context: self.clone(), + }) + } else { + Ok(()) + }?; + self.context = self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(); + self.next_type_id = cmp::max(self.next_type_id, to); + Ok(()) + } + + pub fn disjoint_union(&mut self, other: Self) -> Result<(), ContextError> { + for (&type_id, elem_type) in other.context.iter() { + match self.context.insert(type_id, elem_type.clone()) { + None => { + Ok(()) + }, + Some(conflicting_elem_type) => Err(ContextError::DisjointUnion { + type_id: type_id, + elem_type: elem_type.clone(), + conflicting_elem_type: conflicting_elem_type, + lhs: self.clone(), + rhs: other.clone(), + }), + }? + } + self.next_type_id = cmp::max(self.next_type_id, other.next_type_id); + Ok(()) + } + + pub fn get(&mut self, index: &TypeId, error: &dyn Fn() -> ContextError) -> Result { + Ok(self.context.get(index).ok_or_else(|| ContextError::GetUnknownTypeId { + context: self.clone(), + index: *index, + error: Box::new(error()), + })?.clone()) + } + + // unify the types of two TypeId's into the rhs + // removing the lhs + pub fn unify(&mut self, xi: TypeId, yi: TypeId) -> Result<(), ContextError> { + let x_type = self.context.remove(&xi).ok_or_else(|| ContextError::Unify { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + is_lhs: true, + })?; + let y_type = self.context.remove(&yi).ok_or_else(|| ContextError::Unify { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + is_lhs: false, + })?; + let xy_type = x_type.unify(y_type).or_else(|e| Err(ContextError::UnifyElemType { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + error: e, + }))?; + self.context.insert(yi, xy_type); + Ok(()) + } +} + + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Type { + pub context: Context, + pub i_type: Vec, + pub o_type: Vec, +} + +impl Type { + pub fn id() -> Self { + Type { + context: Context::new(), + i_type: vec![], + o_type: vec![], + } + } + + pub fn next_type_id(&self) -> TypeId { + self.context.next_type_id + } + + // check whether all the TypeId's are valid + pub fn is_valid(&self) -> bool { + let next_type_id = self.next_type_id(); + self.context.is_valid() && + !(self.i_type.iter().any(|x| *x >= next_type_id) || + self.o_type.iter().any(|x| *x >= next_type_id)) + } + + // equivalent to running update_type_id w/ offset from largest to smallest + // existing TypeId + pub fn offset(&self, offset: TypeId) -> Self { + Type { + context: self.context.offset(offset), + i_type: self.i_type.iter().map(|x| x.offset(offset)).collect(), + o_type: self.o_type.iter().map(|x| x.offset(offset)).collect(), } } + + pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeError> { + self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?; + self.i_type = self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(); + self.o_type = self.o_type.iter().map(|x| x.update_type_id(from, to)).collect(); + Ok(()) + } + + pub fn normalize(&self) -> Result { + let mut basis = self.i_type.clone(); + basis.append(&mut self.o_type.clone()); + basis.dedup(); + let (new_context, type_map) = self.context.normalize_on(basis).map_err(|e| TypeError::ContextError(e))?; + Ok(Type { + context: new_context, + i_type: type_map.run(self.i_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, + o_type: type_map.run(self.o_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, + }) + } + + // f : self + // g : other + // self.compose(other) : (f ++ g).type_of() + // + // input -> + // other.i_type + // other.o_type + // self.i_type + // self.o_type + // -> output + // + // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context + // 2. collect the remainder and add them to the context + // 3. add the remainder to (self.i_type, other.o_type), with replaced variables + pub fn compose(&self, other: Self) -> Result { + println!(""); + println!("composing:\n{0}\n\nAND\n{1}\n", self, other); + + let mut context = self.context.clone(); + // println!("context: {}", context); + // println!("context.next_type_id: {:?}", context.next_type_id.type_id); + + let offset_other = other.offset(self.next_type_id()); + // println!("offset_other: {}", offset_other); + + context.disjoint_union(offset_other.context.clone()) + .map_err(|e| TypeError::ContextError(e))?; + // println!("context union: {}", context); + + let mut mut_offset_other = offset_other.clone(); + let mut zip_len = 0; + let other_o_type = offset_other.o_type.iter().clone(); + let self_i_type = self.i_type.iter().clone(); + other_o_type.zip(self_i_type).try_for_each(|(&o_type, &i_type)| { + zip_len += 1; + context + .unify(o_type, i_type) + .map_err(|e| TypeError::ContextError(e))?; + mut_offset_other + .update_type_id(o_type, i_type)?; + Ok(()) + })?; + + Ok(Type { + context: context, + i_type: mut_offset_other.i_type.iter().chain(self.i_type.iter().skip(zip_len)).copied().collect(), + o_type: self.o_type.iter().chain(mut_offset_other.o_type.iter().skip(zip_len)).copied().collect(), + }) + } +} + +// Formatting: +// ``` +// Type { +// context: Context { +// context: [ +// (t0, {A, B, C}), +// (t1, {B, C}), +// .. +// (tN, {D, E, F})], +// next_type_id: N+1, +// }, +// i_type: [0, 1, .., N], +// 0_type: [i, j, .., k], +// } +// ``` +// +// Results in: +// ``` +// ∀ (t0 ∊ {A, B, C}), +// ∀ (t1 ∊ {B, C}), +// .. +// ∀ (tN ∊ {D, E, F}), +// [t0, t1, .., tN] -> +// [ti, tj, .., tk] +// ``` +impl Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + // let self_normalized = self.normalize().map_err(|_| fmt::Error)?; + let self_normalized = self; + write!(f, + "{context}\n[{i_type}] ->\n[{o_type}]", + context = self_normalized.context, + i_type = self_normalized.i_type.iter().fold(String::new(), |memo, x| { + let x_str = format!("t{}", x.type_id); + if memo == "" { + x_str + } else { + memo + ", " + &x_str.to_string() + }}), + o_type = self_normalized.o_type.iter().fold(String::new(), |memo, x| { + let x_str = format!("t{}", x.type_id); + if memo == "" { + x_str + } else { + memo + ", " + &x_str.to_string() + }})) + } } -impl fmt::Debug for TEq { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "Teq {{ teq: Teq<{0}, {1}> }}", TypeName::type_name(PhantomData::), TypeName::type_name(PhantomData::)) - } -} +#[cfg(test)] +mod type_display_tests { + use super::*; -#[derive(Clone, PartialEq, Eq)] -pub enum IsElem { - Unit(TEq), - Bool(TEq), - // Number(TEq), - // Bytes(TEq>), - // String(TEq), - // Array(TEq>), - // Object(TEq>), - // Json(TEq), -} + #[test] + fn test_empty() { + let big_type_id = TypeId { + type_id: 2^32 + }; + let context = Context { + context: BTreeMap::new(), + next_type_id: big_type_id, + }; + let example_type = Type { + context: context, + i_type: vec![], + o_type: vec![], + }; + assert_eq!("\n[] ->\n[]", format!("{}", example_type)); + } -impl fmt::Debug for IsElem { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "IsElem {}", TypeName::type_name(PhantomData::)) + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + let mut context_map = BTreeMap::new(); + context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); + let context = Context { + context: context_map, + next_type_id: TypeId { + type_id: 1, + }, + }; + let example_type = Type { + context: context, + i_type: vec![TypeId { type_id: 0 }, TypeId { type_id: 0 }], + o_type: vec![TypeId { type_id: 0 }], + }; + assert_eq!(format!("\n∀ (t0 ∊ {}),\n[t0, t0] ->\n[t0]", elem_type), format!("{}", example_type)); + } } } -// impl fmt::Debug for TEq { -// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { -// write!(f, "Teq {{ teq: Teq<{0}, {1}> }}", TypeName::type_name(PhantomData), TypeName::type_name(PhantomData)) -// } -// } - - -impl IsElem { - pub fn elem_symbol(&self) -> ElemSymbol { - match self { - Self::Unit(_) => ElemSymbol::Unit, - Self::Bool(_) => ElemSymbol::Bool, - // Self::Number(_) => ElemSymbol::Number, - // Self::Bytes(_) => ElemSymbol::Bytes, - // Self::String(_) => ElemSymbol::String, - // Self::Array(_) => ElemSymbol::Array, - // Self::Object(_) => ElemSymbol::Object, - // Self::Json(_) => ElemSymbol::Json, - } - } +// TODO: split up TypeError +// TODO: add layers of detail to TypeIdMapGetUnknownTypeId - pub fn to_elem(&self, x: T) -> Elem { - match self { - Self::Unit(_) => Elem::Unit, - Self::Bool(eq) => Elem::Bool(eq.transport(x).get_bool), - // Self::Number(eq) => Elem::Number(eq.transport(x)), - // Self::Bytes(eq) => Elem::Bytes(eq.transport(x)), - // Self::String(eq) => Elem::String(eq.transport(x)), - // Self::Array(eq) => Elem::Array(eq.transport(x)), - // Self::Object(eq) => Elem::Object(eq.transport(x)), - // Self::Json(eq) => Elem::Json(eq.transport(x)), - } - } - // TODO: from_elem - // fn from_elem(x: &Elem) -> Option where Self: Sized; +#[derive(Debug, PartialEq, Error)] +pub enum ElemTypeError { + #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] + UnifyEmpty { + lhs: ElemType, + rhs: ElemType, + // location: TyUnifyLocation, + }, } +#[derive(Debug, PartialEq, Error)] +pub enum TypeIdMapError { + #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] + GetUnknownTypeId { + index: TypeId, + location: usize, + type_map: TypeIdMap, + }, -// fn fold(&self, init: B, f: F) -> B where F: Fn(B, Elem) -> B; -pub trait HList: Clone + IntoIterator { - type Hd; - type Tl: HList; + #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] + PushExists { + from: TypeId, + to: TypeId, + map: TypeIdMap, + }, +} - // fn is_empty(&self) -> bool; +#[derive(Debug, PartialEq, Error)] +pub enum ContextError { + #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}, error: {error:?}")] + GetUnknownTypeId { + context: Context, + index: TypeId, + error: Box, + }, - fn hd(&self) -> Self::Hd; - fn hd_is_elem(&self) -> IsElem; + #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, / + with type_id: {type_id:?}, and elem_type: {elem_type:?}, conflicted / + with lhs entry conflicting_elem_type: {conflicting_elem_type:?}")] + DisjointUnion { + type_id: TypeId, + elem_type: ElemType, + conflicting_elem_type: ElemType, + lhs: Context, + rhs: Context, + }, - // fn tl(&self) -> Self::Tl; - // fn cons(&self, x: T, is_elem: IsElem) -> Cons where Self: Sized; + #[error("Context::normalize_on applied to invalid basis: type_id: {type_id:?}, context: {context:?}, basis: {basis:?}")] + NormalizeOnInvalidBasis { + type_id: TypeId, + context: Context, + basis: Vec, + }, -} + #[error("Context::update_type_id called on missing 'from: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] + UpdateTypeIdFromMissing { + from: TypeId, + to: TypeId, + context: Context, + }, + #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] + UpdateTypeIdToPresent { + from: TypeId, + to: TypeId, + context: Context, + }, + #[error("Context::unify failed:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n is_lhs: {is_lhs:?}\n")] + Unify { + xs: Context, + xi: TypeId, + yi: TypeId, + is_lhs: bool, + }, -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Nil { } + #[error("Context::unify failed to unify ElemType's:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n elem_error: {error:?}\n")] + UnifyElemType { + xs: Context, + xi: TypeId, + yi: TypeId, + error: ElemTypeError, + }, -impl Iterator for Nil { - type Item = Elem; + #[error("Context::normalize_on building TypeIdMap failed: {0:?}")] + TypeIdMapError(TypeIdMapError), +} - fn next(&mut self) -> Option { - None +impl From for ContextError { + fn from(error: TypeIdMapError) -> Self { + Self::TypeIdMapError(error) } } -impl HList for Nil { - type Hd = TUnit; - type Tl = Nil; - - // fn is_empty(&self) -> bool { - // true - // } - fn hd(&self) -> Self::Hd { - TUnit {} - } +#[derive(Debug, PartialEq, Error)] +pub enum TypeError { + #[error("ContextError {0}")] + ContextError(ContextError), - fn hd_is_elem(&self) -> IsElem { - IsElem::Unit(TEq::refl(PhantomData::)) - } + #[error("TypeError::update_type_id failed when updating the Context: {0}")] + UpdateTypeId(ContextError), - // fn tl(&self) -> Self::Tl { - // (*self).clone() - // } + #[error("TypeError::compose disjoint_union {0}")] + ComposeDisjointUnion(ContextError), - // fn cons>(&self, x: U) -> Cons - // where - // Self: Sized, - // { - // Cons { - // t: PhantomData, - // hd: x, - // tl: (*self).clone(), - // } - // } + #[error("Type::normalize applying TypeIdMap failed: {0:?}")] + TypeIdMapError(TypeIdMapError), } -#[derive(Clone, PartialEq, Eq)] -pub struct Cons { - is_elem: IsElem, - hd: T, - tl: U, -} - -#[derive(Clone, PartialEq, Eq)] -pub struct IterCons { - cons: Cons, - at_head: bool, +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct TypeIdMap { + map: BTreeMap, } -impl IntoIterator for Cons { - type Item = Elem; - type IntoIter = IterCons; - fn into_iter(self) -> Self::IntoIter { - IterCons { - cons: self, - at_head: true, +impl TypeIdMap { + pub fn new() -> Self { + TypeIdMap { + map: BTreeMap::new(), } } -} -impl Iterator for IterCons { - type Item = Elem; - - fn next(&mut self) -> Option { - if self.at_head { - Some(self.cons.hd_is_elem().to_elem(self.cons.hd)) + pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { + if self.map.contains_key(&from) { + Err(TypeIdMapError::PushExists { + from: from, + to: to, + map: self.clone(), + }) } else { - let self_cons = self.cons.clone(); - *self = self_cons.into_iter(); - self.next() + self.map.insert(from, to); + Ok(()) } } -} - -impl HList for Cons { - type Hd = T; - type Tl = U; - // fn is_empty(&self) -> bool { - // false - // } - - fn hd(&self) -> Self::Hd { - self.hd + pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { + self.map.get(index) + .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { + index: index.clone(), + location: location, + type_map: self.clone(), + }) } - fn hd_is_elem(&self) -> IsElem { - self.is_elem.clone() + pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { + type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() } - - // fn tl(&self) -> Self::Tl { - // self.tl.clone() - // } - - // fn cons>(&self, x: W) -> Cons { - // Cons { - // t: PhantomData, - // hd: x, - // tl: (*self).clone(), - // } - // } - } - - -// pub fn demo_triple() -> Cons<(), TBool, Cons<(), TUnit, Cons<(), TBool, Nil<()>>>> { -// Nil { t: PhantomData } -// .cons(TBool { get_bool: true }) -// .cons(TUnit { }) -// .cons(TBool { get_bool: false }) -// } - -// pub fn demo_triple_with_tl_handles_intermediate_types() -> Cons<(), TBool, Cons<(), TUnit, Cons<(), TBool, Nil<()>>>> { -// Nil { t: PhantomData } -// .cons(TBool { get_bool: true }) -// .cons(TUnit { }) -// .cons(TBool { get_bool: false }) -// .cons(TBool { get_bool: true }) -// .cons(TUnit { }) -// .tl() -// .tl() -// } - diff --git a/src/types_scratch.rs b/src/types_scratch.rs new file mode 100644 index 0000000..491e24b --- /dev/null +++ b/src/types_scratch.rs @@ -0,0 +1,406 @@ +// use crate::elem::{Elem, ElemSymbol}; +use crate::stack::{Stack, StackError}; +use crate::types::{AnElem, AnError, Nil, Teq, TEq, TypeName}; + +use std::iter::{FromIterator}; + +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::marker::PhantomData; +use std::sync::Arc; + +use generic_array::{GenericArray, ArrayLength}; + +// #[derive(Clone)] +// struct Args::Dyn>>> { +// args: GenericArray::Dyn>, N>, +// } + +// #[derive(Clone)] +// struct Returning { +// r: PhantomData, +// args: Args, +// } + + + +// fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; +#[derive(Clone, PartialEq, Eq)] +pub struct Cons { + hd: T, + tl: U, +} + + +pub trait IList {} + +impl IList for Nil {} +impl IList for Cons {} + +pub trait IOList: IList { + type Return; +} + + +// #[derive(Clone, PartialEq, Eq)] +pub struct ConsOut { + r: PhantomData, + hd: T, + tl: U, +} + +impl IList for ConsOut {} +impl IOList for ConsOut { + type Return = R; +} +impl IOList for Cons { + type Return = ::Return; +} + +pub trait IsInstructionT: std::fmt::Debug { + type In: IOList; + // type Out: AnElem; + type Error: AnError; + + fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; +} + +// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +// struct Concat { +// t: PhantomData, +// } +// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +// struct ConcatError {} +// impl AnError for ConcatError {} + +// impl::Item>> IsInstruction for Concat { +// type In = (T, T); +// type Out = T; +// type Error = Empty; + +// fn run(&self, x: Self::In) -> Result { +// let (lhs, rhs) = x; +// Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) +// } +// } + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//#[derive(Clone, PartialEq, Eq)] +//pub struct ConsT, V: TList> { +// t: PhantomData, +// hd: U, +// tl: V, +//} + +//// + IntoIterator +//pub trait TList: Clone { +// type T; +// type Hd: AnElem + Trait; +// type Tl: TList; + +// fn is_empty(&self) -> bool; +// fn hd(&self) -> Self::Hd; +// fn tl(&self) -> Self::Tl; +// fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; +// fn pop(x: PhantomData, stack: &mut Stack) -> Result; +//} + +//impl TList for Nil { +// type T = Nil; +// type Hd = (); +// type Tl = Nil; + +// fn is_empty(&self) -> bool { +// true +// } + +// fn hd(&self) -> Self::Hd { +// () +// } + +// fn tl(&self) -> Self::Tl { +// Self {} +// } + +// fn cons(self, u: PhantomData, x: V) -> ConsT +// where +// V: AnElem + Trait, +// Self: Sized, +// { +// ConsT { +// t: PhantomData, +// hd: x, +// tl: self, +// } +// } + +// fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { +// Ok(Nil {}) +// } +//} + +//impl TList for ConsT +//where +// T: Clone, +// U: AnElem + Trait, +// V: TList, +//{ +// type T = T; +// type Hd = U; +// type Tl = V; + +// fn is_empty(&self) -> bool { +// false +// } + +// fn hd(&self) -> Self::Hd { +// self.hd.clone() +// } + +// fn tl(&self) -> Self::Tl { +// self.tl.clone() +// } + +// fn cons(self, u: PhantomData, x: B) -> ConsT +// where +// B: AnElem + Trait, +// Self: Sized, +// { +// ConsT { +// t: PhantomData, +// hd: x, +// tl: self, +// } +// } + +// // TODO: add better errors +// fn pop(_x: PhantomData, stack: &mut Stack) -> Result { +// let hd_elem = stack.pop()?; + +// // TODO: IMPLEMENT +// Err(StackError::EmptyStack) + +// // Ok(Cons { +// // hd: AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { +// // expected: AnElem::is_elem(PhantomData::).elem_symbol(), +// // found: hd_elem.clone(), +// // stack: stack.clone(), +// // })?, +// // tl: Self::Tl::pop(PhantomData, stack)?, +// // }) +// } +//} + + + + + + + +// impl DecTrait for T { +// fn dec(_t: PhantomData, _s: PhantomData) -> Result>, DecTraitError> { +// Ok(Arc::new(())) +// } +// } + +// // : DecTrait +// // : core::fmt::Debug +// pub trait Trait {} + +// impl Trait for U +// where +// T: AnElem, +// {} + +// impl Trait, V> for W +// where +// T: AnElem, +// U: HList, +// V: AnElem, +// W: Trait, +// W: Trait, +// {} + +// #[derive(Debug)] +// pub struct IterTrait {} +// impl Trait for U +// where +// T: AnElem + IntoIterator + FromIterator<::Item>, +// {} + + +// fn ok(x: T) -> T +// where +// // T: AnElem + IntoIterator + FromIterator<::Item>, +// T: AnElem, +// (): Trait, +// { +// // x +// x.into_iter().collect() +// } + +// TODO: remove Trait entirely, it simply doesn't work! + +// #[derive(Clone)] +// pub struct DynTrait { +// dyn_trait: Arc>, +// } + +// impl std::fmt::Debug for dyn Trait { +// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { +// write!(f, "IterTrait {}", TypeName::type_name(PhantomData::)) +// } +// } + +// impl AnElem for DynTrait { +// fn is_elem(x: PhantomData) -> IsElem where Self: Sized { +// AnElem::is_elem(Ph + +// impl IntoIterator for dyn Trait {} +// impl FromIterator<::Item> for dyn Trait {} + +// #[derive(Debug)] +// pub struct TeqTrait { +// t: PhantomData +// } +// impl Trait, U> for V +// where +// U: AnElem + Teq, +// {} + + +////////////////////////////////////////////////////////////////////////////////////////////// +//pub trait ElemTrait { +// // // sym +// // fn transport_trait(eq: TEq, xs: Arc>) -> Arc>; +// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError>; +//} + +//impl ElemTrait for Nil { +// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError> { +// Ok(Arc::new(())) +// } +//} + +//impl ElemTrait<()> for IterTrait { +// fn dec(s: PhantomData, t: PhantomData<()>) -> Result>, TraitError> { +// Err(TraitError::TODO) +// } +//} + +//impl ElemTrait> for IterTrait { +// fn dec(s: PhantomData, t: PhantomData>) -> Result>>, TraitError> { +// Ok(Arc::new(())) +// } +//} + +// impl dyn Trait { +// fn ok(&self, x: T) -> T { +// self.into_iter().collect() +// } +// } + +// fn transport_trait(eq: TEq, xs: Arc>) -> Arc> { +// Arc::new(()) +// } + +// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError> { +// match AnElem::is_elem(PhantomData) { +// Unit(eq) => self.transport_trait(eq, Arc::new(())), +// Bool(eq) => self.transport_trait(eq, Arc::new(())), +// Number(eq) => self.transport_trait(eq, Arc::new(())), +// Bytes(eq) => self.transport_trait(eq, Arc::new(())), +// String(eq) => self.transport_trait(eq, Arc::new(())), +// Array(eq) => self.transport_trait(eq, Arc::new(())), +// Object(eq) => self.transport_trait(eq, Arc::new(())), +// Json(eq) => self.transport_trait(eq, Arc::new(())), + +// } +// } + + +// impl ElemTrait for IterTrait { +// fn transport_trait(eq: TEq, xs: Arc>) -> Arc> { +// Arc::new(()) +// } + +// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError> { +// match AnElem::is_elem(PhantomData) { +// Unit(eq) => self.transport_trait(eq, Arc::new(())), +// Bool(eq) => self.transport_trait(eq, Arc::new(())), +// Number(eq) => self.transport_trait(eq, Arc::new(())), +// Bytes(eq) => self.transport_trait(eq, Arc::new(())), +// String(eq) => self.transport_trait(eq, Arc::new(())), +// Array(eq) => self.transport_trait(eq, Arc::new(())), +// Object(eq) => self.transport_trait(eq, Arc::new(())), +// Json(eq) => self.transport_trait(eq, Arc::new(())), + +// } +// } + +// pub enum TraitError { +// TODO, +// } + +// pub trait AnElem: Clone + std::fmt::Debug { +// fn is_elem(x: PhantomData) -> IsElem where Self: Sized; +// } + + + +// pub trait DynTrait: ElemTrait { +// type Dyn; + +// fn from_trait(t: PhantomData, x: Arc>) -> Self::Dyn; +// } + +// impl DynTrait for Nil { +// type Dyn = Arc; + +// fn from_trait(t: PhantomData, x: Arc>) -> Self::Dyn { +// Arc::new(()) +// } +// } + +// #[derive(Debug)] +// pub struct IterTrait {} + +// impl DynTrait for IterTrait + +// impl Trait for U +// where +// T: AnElem + IntoIterator + FromIterator<::Item>, +// {} + + +// pub trait TraitArray { +// fn run_trait_array(&self) -> () where (): Trait + +// pub trait TraitArray { +// fn run_trait_array(&self) -> () where (): Trait + +// pub struct Foo { +// GenericArray + +// pub trait TraitArray> { +// // fn ok(&self) -> + +// // fn ok(&self) -> (); +// } + +// pub trait TraitArray { +// type ElemT; +// type N: ArrayLength; + +// fn has_trait(&self) -> Arc>; +// fn array(&self) -> GenericArray; +// } From 4ccf649d52d3110448725a25e9c9ecd150f194db Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Feb 2022 19:55:46 -0500 Subject: [PATCH 35/77] cleanup and get working with return types --- src/types.rs | 2 +- src/types_scratch.rs | 553 +++++++++++++++---------------------------- 2 files changed, 192 insertions(+), 363 deletions(-) diff --git a/src/types.rs b/src/types.rs index c168c2f..5b8bab6 100644 --- a/src/types.rs +++ b/src/types.rs @@ -262,7 +262,7 @@ impl IsElem { } // TODO: from_elem - fn from_elem(self, x: Elem) -> Option { + pub fn from_elem(self, x: Elem) -> Option { match (self, x) { (Self::Unit(eq), Elem::Unit) => Some(eq.transport_sym(())), (Self::Bool(eq), Elem::Bool(x)) => Some(eq.transport_sym(x)), diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 491e24b..2fe72e1 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,406 +1,235 @@ -// use crate::elem::{Elem, ElemSymbol}; +use crate::elem::{Elem}; use crate::stack::{Stack, StackError}; -use crate::types::{AnElem, AnError, Nil, Teq, TEq, TypeName}; +use crate::types::{Empty, AnElem, AnError, Nil, Teq, TEq, TypeName}; use std::iter::{FromIterator}; -use std::fmt; -use std::fmt::{Display, Formatter}; +// use std::fmt; +// use std::fmt::{Display, Formatter}; use std::marker::PhantomData; -use std::sync::Arc; +// use std::sync::Arc; use generic_array::{GenericArray, ArrayLength}; -// #[derive(Clone)] -// struct Args::Dyn>>> { -// args: GenericArray::Dyn>, N>, -// } - -// #[derive(Clone)] -// struct Returning { -// r: PhantomData, -// args: Args, -// } - - // fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; #[derive(Clone, PartialEq, Eq)] -pub struct Cons { +pub struct Cons { hd: T, tl: U, } +#[derive(Clone, PartialEq, Eq)] +pub struct IterCons { + cons: Cons, + at_head: bool, +} + +impl IntoIterator for Cons { + type Item = Elem; + type IntoIter = IterCons; + + fn into_iter(self) -> Self::IntoIter { + IterCons { + cons: self, + at_head: true, + } + } +} + +impl Iterator for IterCons { + type Item = Elem; + + fn next(&mut self) -> Option { + if self.at_head { + Some(AnElem::is_elem(PhantomData::).to_elem(self.cons.hd.clone())) + } else { + let self_cons = self.cons.clone(); + *self = self_cons.into_iter(); + self.next() + } + } +} + + -pub trait IList {} -impl IList for Nil {} -impl IList for Cons {} +pub trait IList: Clone + IntoIterator { + type Hd: AnElem; + // type N: ArrayLength; + type Tl: IList; + + fn is_empty(&self) -> bool; + fn hd(&self) -> Self::Hd; + fn tl(&self) -> Self::Tl; + fn cons(self, x: T) -> Cons where Self: Sized; + fn pop(x: PhantomData, stack: &mut Stack) -> Result; +} + +impl IList for Nil { + type Hd = (); + type Tl = Nil; + + fn is_empty(&self) -> bool { + true + } + + fn hd(&self) -> Self::Hd { + () + } + + fn tl(&self) -> Self::Tl { + Self {} + } + + fn cons(self, x: T) -> Cons + where + Self: Sized, + { + Cons { + hd: x, + tl: self, + } + } + + fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { + Ok(Nil {}) + } +} + +impl IList for Cons { + type Hd = T; + type Tl = U; + + fn is_empty(&self) -> bool { + false + } + + fn hd(&self) -> Self::Hd { + self.hd.clone() + } + + fn tl(&self) -> Self::Tl { + self.tl.clone() + } + + fn cons(self, x: V) -> Cons + where + Self: Sized, + { + Cons { + hd: x, + tl: self, + } + } + + // TODO: add better errors + fn pop(_x: PhantomData, stack: &mut Stack) -> Result { + let hd_elem = stack.pop()?; + Ok(Cons { + hd: AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { + expected: AnElem::is_elem(PhantomData::).elem_symbol(), + found: hd_elem.clone(), + stack: stack.clone(), + })?, + tl: Self::Tl::pop(PhantomData, stack)?, + }) + } + +} pub trait IOList: IList { - type Return; + type Return: AnElem; } +#[derive(Clone, PartialEq, Eq)] +pub struct ConsOut { + cons: Cons, +} -// #[derive(Clone, PartialEq, Eq)] -pub struct ConsOut { - r: PhantomData, - hd: T, - tl: U, +impl IntoIterator for ConsOut { + type Item = Elem; + type IntoIter = IterCons; + + fn into_iter(self) -> Self::IntoIter { + self.cons.into_iter() + } +} + +impl IList for ConsOut { + type Hd = T; + type Tl = U; + + fn is_empty(&self) -> bool { + self.cons.is_empty() + } + + fn hd(&self) -> Self::Hd { + self.cons.hd() + } + + fn tl(&self) -> Self::Tl { + self.cons.tl() + } + + fn cons(self, x: V) -> Cons + where + Self: Sized, + { + Cons { + hd: x, + tl: self, + } + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result { + Ok(ConsOut { + cons: IList::pop(PhantomData, stack)?, + }) + } } -impl IList for ConsOut {} -impl IOList for ConsOut { - type Return = R; +impl IOList for ConsOut { + type Return = T; } + impl IOList for Cons { type Return = ::Return; } pub trait IsInstructionT: std::fmt::Debug { type In: IOList; - // type Out: AnElem; type Error: AnError; fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; } -// #[derive(Clone, Copy, Debug, PartialEq, Eq)] -// struct Concat { -// t: PhantomData, -// } -// #[derive(Clone, Copy, Debug, PartialEq, Eq)] -// struct ConcatError {} -// impl AnError for ConcatError {} - -// impl::Item>> IsInstruction for Concat { -// type In = (T, T); -// type Out = T; -// type Error = Empty; - -// fn run(&self, x: Self::In) -> Result { -// let (lhs, rhs) = x; -// Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) -// } -// } - - - - - - - - - - -////////////////////////////////////////////////////////////////////////////////////////////// -//#[derive(Clone, PartialEq, Eq)] -//pub struct ConsT, V: TList> { -// t: PhantomData, -// hd: U, -// tl: V, -//} - -//// + IntoIterator -//pub trait TList: Clone { -// type T; -// type Hd: AnElem + Trait; -// type Tl: TList; - -// fn is_empty(&self) -> bool; -// fn hd(&self) -> Self::Hd; -// fn tl(&self) -> Self::Tl; -// fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; -// fn pop(x: PhantomData, stack: &mut Stack) -> Result; -//} - -//impl TList for Nil { -// type T = Nil; -// type Hd = (); -// type Tl = Nil; - -// fn is_empty(&self) -> bool { -// true -// } - -// fn hd(&self) -> Self::Hd { -// () -// } - -// fn tl(&self) -> Self::Tl { -// Self {} -// } - -// fn cons(self, u: PhantomData, x: V) -> ConsT -// where -// V: AnElem + Trait, -// Self: Sized, -// { -// ConsT { -// t: PhantomData, -// hd: x, -// tl: self, -// } -// } - -// fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { -// Ok(Nil {}) -// } -//} - -//impl TList for ConsT -//where -// T: Clone, -// U: AnElem + Trait, -// V: TList, -//{ -// type T = T; -// type Hd = U; -// type Tl = V; - -// fn is_empty(&self) -> bool { -// false -// } - -// fn hd(&self) -> Self::Hd { -// self.hd.clone() -// } - -// fn tl(&self) -> Self::Tl { -// self.tl.clone() -// } - -// fn cons(self, u: PhantomData, x: B) -> ConsT -// where -// B: AnElem + Trait, -// Self: Sized, -// { -// ConsT { -// t: PhantomData, -// hd: x, -// tl: self, -// } -// } - -// // TODO: add better errors -// fn pop(_x: PhantomData, stack: &mut Stack) -> Result { -// let hd_elem = stack.pop()?; - -// // TODO: IMPLEMENT -// Err(StackError::EmptyStack) - -// // Ok(Cons { -// // hd: AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { -// // expected: AnElem::is_elem(PhantomData::).elem_symbol(), -// // found: hd_elem.clone(), -// // stack: stack.clone(), -// // })?, -// // tl: Self::Tl::pop(PhantomData, stack)?, -// // }) -// } -//} - - - - - - - -// impl DecTrait for T { -// fn dec(_t: PhantomData, _s: PhantomData) -> Result>, DecTraitError> { -// Ok(Arc::new(())) -// } -// } - -// // : DecTrait -// // : core::fmt::Debug -// pub trait Trait {} - -// impl Trait for U -// where -// T: AnElem, -// {} - -// impl Trait, V> for W -// where -// T: AnElem, -// U: HList, -// V: AnElem, -// W: Trait, -// W: Trait, -// {} - -// #[derive(Debug)] -// pub struct IterTrait {} -// impl Trait for U -// where -// T: AnElem + IntoIterator + FromIterator<::Item>, -// {} - - -// fn ok(x: T) -> T -// where -// // T: AnElem + IntoIterator + FromIterator<::Item>, -// T: AnElem, -// (): Trait, -// { -// // x -// x.into_iter().collect() -// } - -// TODO: remove Trait entirely, it simply doesn't work! - -// #[derive(Clone)] -// pub struct DynTrait { -// dyn_trait: Arc>, -// } - -// impl std::fmt::Debug for dyn Trait { -// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { -// write!(f, "IterTrait {}", TypeName::type_name(PhantomData::)) -// } -// } - -// impl AnElem for DynTrait { -// fn is_elem(x: PhantomData) -> IsElem where Self: Sized { -// AnElem::is_elem(Ph - -// impl IntoIterator for dyn Trait {} -// impl FromIterator<::Item> for dyn Trait {} - -// #[derive(Debug)] -// pub struct TeqTrait { -// t: PhantomData -// } -// impl Trait, U> for V -// where -// U: AnElem + Teq, -// {} - - -////////////////////////////////////////////////////////////////////////////////////////////// -//pub trait ElemTrait { -// // // sym -// // fn transport_trait(eq: TEq, xs: Arc>) -> Arc>; -// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError>; -//} - -//impl ElemTrait for Nil { -// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError> { -// Ok(Arc::new(())) -// } -//} - -//impl ElemTrait<()> for IterTrait { -// fn dec(s: PhantomData, t: PhantomData<()>) -> Result>, TraitError> { -// Err(TraitError::TODO) -// } -//} - -//impl ElemTrait> for IterTrait { -// fn dec(s: PhantomData, t: PhantomData>) -> Result>>, TraitError> { -// Ok(Arc::new(())) -// } -//} - -// impl dyn Trait { -// fn ok(&self, x: T) -> T { -// self.into_iter().collect() -// } -// } - -// fn transport_trait(eq: TEq, xs: Arc>) -> Arc> { -// Arc::new(()) -// } - -// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError> { -// match AnElem::is_elem(PhantomData) { -// Unit(eq) => self.transport_trait(eq, Arc::new(())), -// Bool(eq) => self.transport_trait(eq, Arc::new(())), -// Number(eq) => self.transport_trait(eq, Arc::new(())), -// Bytes(eq) => self.transport_trait(eq, Arc::new(())), -// String(eq) => self.transport_trait(eq, Arc::new(())), -// Array(eq) => self.transport_trait(eq, Arc::new(())), -// Object(eq) => self.transport_trait(eq, Arc::new(())), -// Json(eq) => self.transport_trait(eq, Arc::new(())), - -// } -// } - - -// impl ElemTrait for IterTrait { -// fn transport_trait(eq: TEq, xs: Arc>) -> Arc> { -// Arc::new(()) -// } - -// fn dec(s: PhantomData, t: PhantomData) -> Result>, TraitError> { -// match AnElem::is_elem(PhantomData) { -// Unit(eq) => self.transport_trait(eq, Arc::new(())), -// Bool(eq) => self.transport_trait(eq, Arc::new(())), -// Number(eq) => self.transport_trait(eq, Arc::new(())), -// Bytes(eq) => self.transport_trait(eq, Arc::new(())), -// String(eq) => self.transport_trait(eq, Arc::new(())), -// Array(eq) => self.transport_trait(eq, Arc::new(())), -// Object(eq) => self.transport_trait(eq, Arc::new(())), -// Json(eq) => self.transport_trait(eq, Arc::new(())), - -// } -// } - -// pub enum TraitError { -// TODO, -// } - -// pub trait AnElem: Clone + std::fmt::Debug { -// fn is_elem(x: PhantomData) -> IsElem where Self: Sized; -// } - - - -// pub trait DynTrait: ElemTrait { -// type Dyn; - -// fn from_trait(t: PhantomData, x: Arc>) -> Self::Dyn; -// } - -// impl DynTrait for Nil { -// type Dyn = Arc; - -// fn from_trait(t: PhantomData, x: Arc>) -> Self::Dyn { -// Arc::new(()) -// } -// } - -// #[derive(Debug)] -// pub struct IterTrait {} - -// impl DynTrait for IterTrait - -// impl Trait for U -// where -// T: AnElem + IntoIterator + FromIterator<::Item>, -// {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Concat { + t: PhantomData, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct ConcatError {} +impl AnError for ConcatError {} + +impl::Item>> IsInstructionT for Concat { + type In = ConsOut; + // type In = (T, T); + // type Out = T; + // type Error = Empty; + type Error = ConcatError; + + fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { + // let (lhs, rhs) = x; + // Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) + Err(ConcatError {}) + } +} -// pub trait TraitArray { -// fn run_trait_array(&self) -> () where (): Trait -// pub trait TraitArray { -// fn run_trait_array(&self) -> () where (): Trait -// pub struct Foo { -// GenericArray -// pub trait TraitArray> { -// // fn ok(&self) -> -// // fn ok(&self) -> (); -// } -// pub trait TraitArray { -// type ElemT; -// type N: ArrayLength; -// fn has_trait(&self) -> Arc>; -// fn array(&self) -> GenericArray; -// } From fd6822d6f93740ea2190bf3fcd97d054c688f60e Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Feb 2022 21:01:20 -0500 Subject: [PATCH 36/77] implemented IsInstructionT for concat --- Cargo.toml | 1 + src/stack.rs | 5 +- src/types_scratch.rs | 113 ++++++++++++++++++++++++------------------- 3 files changed, 67 insertions(+), 52 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2d4b412..1dc18ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ serde_json = { version = "1.0", features = ["arbitrary_precision"] } sha2 = "0.9" sha3 = "0.9" thiserror = "1.0" +typenum = "1.15.0" diff --git a/src/stack.rs b/src/stack.rs index 2e281fd..f0d32dc 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -40,7 +40,7 @@ impl Stack { } } -#[derive(Debug, Error)] +#[derive(Clone, Debug, Error)] pub enum StackError { #[error("Stack::pop: tried to pop from an empty stack")] EmptyStack, @@ -58,6 +58,9 @@ pub enum StackError { error: String, line_no: LineNo, }, + + #[error("Stack::pop_generic_array: unimplemented")] + TODO, } // TODO: relocate LineNo, ArgumentIndex, Location diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 2fe72e1..1303f95 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -9,45 +9,42 @@ use std::iter::{FromIterator}; use std::marker::PhantomData; // use std::sync::Arc; -use generic_array::{GenericArray, ArrayLength}; - +use generic_array::typenum::{U0, U2}; +use generic_array::sequence::GenericSequence; +use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; +use typenum::marker_traits::Unsigned; // fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; #[derive(Clone, PartialEq, Eq)] -pub struct Cons { - hd: T, +pub struct Cons, U: IList> { + hd: GenericArray, tl: U, } -#[derive(Clone, PartialEq, Eq)] -pub struct IterCons { - cons: Cons, - at_head: bool, +pub struct IterCons, U: IList> { + hd: GenericArrayIter, + cons: ::IntoIter, } -impl IntoIterator for Cons { +impl, U: IList> IntoIterator for Cons { type Item = Elem; - type IntoIter = IterCons; + type IntoIter = IterCons; fn into_iter(self) -> Self::IntoIter { IterCons { - cons: self, - at_head: true, + hd: self.hd.into_iter(), + cons: self.tl.into_iter(), } } } -impl Iterator for IterCons { +impl, U: IList> Iterator for IterCons { type Item = Elem; fn next(&mut self) -> Option { - if self.at_head { - Some(AnElem::is_elem(PhantomData::).to_elem(self.cons.hd.clone())) - } else { - let self_cons = self.cons.clone(); - *self = self_cons.into_iter(); - self.next() - } + self.hd.next() + .map(|x| AnElem::is_elem(PhantomData::).to_elem(x)) + .or_else(|| self.cons.next()) } } @@ -56,33 +53,34 @@ impl Iterator for IterCons { pub trait IList: Clone + IntoIterator { type Hd: AnElem; - // type N: ArrayLength; + type N: ArrayLength; type Tl: IList; fn is_empty(&self) -> bool; - fn hd(&self) -> Self::Hd; + fn hd(&self) -> GenericArray; fn tl(&self) -> Self::Tl; - fn cons(self, x: T) -> Cons where Self: Sized; + fn cons>(self, x: GenericArray) -> Cons where Self: Sized; fn pop(x: PhantomData, stack: &mut Stack) -> Result; } impl IList for Nil { type Hd = (); + type N = U0; type Tl = Nil; fn is_empty(&self) -> bool { true } - fn hd(&self) -> Self::Hd { - () + fn hd(&self) -> GenericArray { + GenericArray::generate(|_| ()) } fn tl(&self) -> Self::Tl { Self {} } - fn cons(self, x: T) -> Cons + fn cons>(self, x: GenericArray) -> Cons where Self: Sized, { @@ -97,15 +95,16 @@ impl IList for Nil { } } -impl IList for Cons { +impl, U: IList> IList for Cons { type Hd = T; + type N = N; type Tl = U; fn is_empty(&self) -> bool { false } - fn hd(&self) -> Self::Hd { + fn hd(&self) -> GenericArray { self.hd.clone() } @@ -113,7 +112,7 @@ impl IList for Cons { self.tl.clone() } - fn cons(self, x: V) -> Cons + fn cons>(self, x: GenericArray) -> Cons where Self: Sized, { @@ -125,46 +124,61 @@ impl IList for Cons { // TODO: add better errors fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - let hd_elem = stack.pop()?; + let hd_arr = stack.pop_generic_array(PhantomData, PhantomData)?; Ok(Cons { - hd: AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { - expected: AnElem::is_elem(PhantomData::).elem_symbol(), - found: hd_elem.clone(), - stack: stack.clone(), - })?, + hd: hd_arr, tl: Self::Tl::pop(PhantomData, stack)?, }) } +} +impl Stack { + // TODO: reversed? + pub fn pop_generic_array>(&mut self, + _t: PhantomData, + _n: PhantomData) -> Result, StackError> { + let mut xs = vec![]; + for _current_index in 1..::USIZE { + let hd_elem = self.pop()?; + xs.push(AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { + expected: AnElem::is_elem(PhantomData::).elem_symbol(), + found: hd_elem.clone(), + stack: self.clone(), + })?) + } + GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) + } } + pub trait IOList: IList { type Return: AnElem; } #[derive(Clone, PartialEq, Eq)] -pub struct ConsOut { - cons: Cons, +pub struct ConsOut, U: IList> { + cons: Cons, } -impl IntoIterator for ConsOut { +impl, U: IList> IntoIterator for ConsOut { type Item = Elem; - type IntoIter = IterCons; + type IntoIter = IterCons; fn into_iter(self) -> Self::IntoIter { self.cons.into_iter() } } -impl IList for ConsOut { +impl, U: IList> IList for ConsOut { type Hd = T; + type N = N; type Tl = U; fn is_empty(&self) -> bool { self.cons.is_empty() } - fn hd(&self) -> Self::Hd { + fn hd(&self) -> GenericArray { self.cons.hd() } @@ -172,7 +186,7 @@ impl IList for ConsOut { self.cons.tl() } - fn cons(self, x: V) -> Cons + fn cons>(self, x: GenericArray) -> Cons where Self: Sized, { @@ -189,11 +203,11 @@ impl IList for ConsOut { } } -impl IOList for ConsOut { +impl, U: IList> IOList for ConsOut { type Return = T; } -impl IOList for Cons { +impl, U: IOList> IOList for Cons { type Return = ::Return; } @@ -213,16 +227,13 @@ struct ConcatError {} impl AnError for ConcatError {} impl::Item>> IsInstructionT for Concat { - type In = ConsOut; - // type In = (T, T); - // type Out = T; - // type Error = Empty; + type In = ConsOut; type Error = ConcatError; fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { - // let (lhs, rhs) = x; - // Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) - Err(ConcatError {}) + let lhs = x.hd()[0].clone(); + let rhs = x.hd()[1].clone(); + Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) } } From b9807274ff6ed8d51ac32a25fa7e815fefb7d390 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 1 Mar 2022 13:14:27 -0500 Subject: [PATCH 37/77] add type-level sets --- src/stack.rs | 6 ++ src/types.rs | 13 +++- src/types_scratch.rs | 142 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) diff --git a/src/stack.rs b/src/stack.rs index f0d32dc..8c7c524 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -61,6 +61,12 @@ pub enum StackError { #[error("Stack::pop_generic_array: unimplemented")] TODO, + + #[error("HList::TODO: {e_hd:?}\n{e_tl:?}")] + PopOr { + e_hd: Box, + e_tl: Box, + }, } // TODO: relocate LineNo, ArgumentIndex, Location diff --git a/src/types.rs b/src/types.rs index 5b8bab6..cae622b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -22,7 +22,7 @@ use thiserror::Error; pub enum Empty {} impl Empty { - fn absurd(&self, _p: PhantomData) -> T { + pub fn absurd(&self, _p: PhantomData) -> T { match *self {} } } @@ -275,6 +275,17 @@ impl IsElem { _ => None, } } + + pub fn pop(self, stack: &mut Stack) -> Result { + let hd_elem = stack.pop()?; + let elem_symbol = self.elem_symbol(); + Ok(self.from_elem(hd_elem.clone()) + .ok_or_else(|| StackError::UnexpectedElemType { + expected: elem_symbol, + found: hd_elem.clone(), + stack: stack.clone(), + })?) + } } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 1303f95..11d0637 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -14,6 +14,148 @@ use generic_array::sequence::GenericSequence; use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; use typenum::marker_traits::Unsigned; + +pub trait Elems: Clone + Into { + type Hd: AnElem; + type Tl: Elems; + + fn is_left(&self) -> bool; + fn left(s: PhantomData, x: Self::Hd) -> Self; + fn right(s: PhantomData, x: Self::Tl) -> Self; + fn or T, G: Fn(Self::Tl) -> T>(&self, f: F, g: G) -> T; + fn pop(x: PhantomData, stack: &mut Stack) -> Result; +} + +#[derive(Clone, PartialEq, Eq)] +pub struct Singleton { + t: T, +} + +impl Into for Singleton { + fn into(self) -> Elem { + AnElem::is_elem(PhantomData::).to_elem(self.t) + } +} + +// #[derive(Clone, PartialEq, Eq)] +// pub struct IterSingleton { +// iter: Option, +// } + +// impl IntoIterator for Singleton { +// type Item = Elem; +// type IntoIter = IterSingleton; +// fn into_iter(self) -> Self::IntoIter { +// IterSingleton { +// iter: Some(self.t), +// } +// } +// } + +// impl Iterator for IterSingleton { +// type Item = Elem; +// fn next(&mut self) -> Option { +// match self.iter.clone() { +// None => None, +// Some(x) => { +// self.iter = None; +// Some(AnElem::is_elem(PhantomData::).to_elem(x.clone())) +// }, +// } +// } +// } + +impl Elems for Singleton { + type Hd = T; + type Tl = Singleton; + + fn is_left(&self) -> bool { true } + + fn left(_s: PhantomData, x: Self::Hd) -> Self { + Singleton { + t: x, + } + } + + fn right(_s: PhantomData, x: Self::Tl) -> Self { + x + } + + fn or U, G: Fn(Self::Tl) -> U>(&self, f: F, _g: G) -> U { + f(self.t.clone()) + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result { + Ok(Singleton { + t: AnElem::is_elem(PhantomData::).pop(stack)?, + }) + } +} + + +#[derive(Clone, PartialEq, Eq)] +pub enum Or { + Left(T), + Right(U), +} + +impl Into for Or { + fn into(self) -> Elem { + match self { + Self::Left(x) => AnElem::is_elem(PhantomData).to_elem(x), + Self::Right(x) => x.into(), + } + } +} + +impl Elems for Or { + type Hd = T; + type Tl = U; + + fn is_left(&self) -> bool { + match self { + Self::Left(_) => true, + Self::Right(_) => false, + } + } + + fn left(_s: PhantomData, x: Self::Hd) -> Self { + Self::Left(x) + } + + fn right(_s: PhantomData, x: Self::Tl) -> Self { + Self::Right(x) + } + + fn or V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { + match self { + Self::Left(x) => f(x.clone()), + Self::Right(x) => g(x.clone()), + } + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result { + AnElem::is_elem(PhantomData::) + .pop(stack) + .map(|x| Or::Left(x)) + .or_else(|e_hd| { + Ok(Or::Right(Elems::pop(PhantomData::, stack)?)) + .map_err(|e_tl| { + StackError::PopOr { + e_hd: Box::new(e_hd), + e_tl: Box::new(e_tl), + }}) + }) + } +} + + + + + + + + // fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; #[derive(Clone, PartialEq, Eq)] pub struct Cons, U: IList> { From 575909e890af56a25466bde2fed2c7204eb9cdcc Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 2 Mar 2022 15:57:38 -0500 Subject: [PATCH 38/77] merge IsElem and AnElem --- src/stack.rs | 12 +- src/types.rs | 303 +++++++++++++------------------------------ src/types_scratch.rs | 120 +++++++---------- 3 files changed, 151 insertions(+), 284 deletions(-) diff --git a/src/stack.rs b/src/stack.rs index 8c7c524..310d0ba 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -11,7 +11,7 @@ use crate::elem::{Elem, ElemSymbol}; // use std::marker::PhantomData; // use std::sync::Arc; -// use enumset::{EnumSet, enum_set}; +use enumset::{EnumSet}; use serde::{Deserialize, Serialize}; // use serde_json::{Map, Number, Value}; use thiserror::Error; @@ -45,9 +45,15 @@ pub enum StackError { #[error("Stack::pop: tried to pop from an empty stack")] EmptyStack, - #[error("HList::pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack:?})")] + #[error("AnElem::from_elem: element popped from the stack {found:?} wasn't the expected type {expected:?}")] UnexpectedElemType { - expected: ElemSymbol, + expected: EnumSet, + found: Elem, + }, + + #[error("pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack:?})")] + UnexpectedElemTypeIn { + expected: EnumSet, found: Elem, stack: Stack, }, diff --git a/src/types.rs b/src/types.rs index cae622b..1a16de7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -10,14 +10,13 @@ use std::fmt; use std::fmt::{Display, Formatter}; // use std::alloc::string; use std::marker::PhantomData; -use std::sync::Arc; +// use std::sync::Arc; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; use thiserror::Error; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Empty {} @@ -38,253 +37,141 @@ impl Iterator for Nil { } } +pub trait AnElem: Clone + std::fmt::Debug { + fn elem_symbol(t: PhantomData) -> EnumSet; + fn to_elem(self) -> Elem; + fn from_elem(t: PhantomData, x: Elem) -> Result; -// #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TUnit {} - -// #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TBool { -// get_bool: bool, -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TNumber { -// number: serde_json::Number, -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TBytes { -// bytes: Vec, -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TString { -// string: String, -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TArray { -// array: Vec, -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TObject { -// object: serde_json::Map, -// } - -// #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -// pub struct TJson { -// json: serde_json::Value, -// } - -// Deriving a string from the type without any values allows debugging TEq -pub trait TypeName { - fn type_name(x: PhantomData) -> &'static str; -} - -impl TypeName for () { // TUnit - fn type_name(_: PhantomData) -> &'static str { - "Unit" + fn pop(_t: PhantomData, stack: &mut Stack) -> Result { + let hd_elem = stack.pop()?; + ::from_elem(PhantomData, hd_elem) } } - - - - - - - - - - - - - - -pub trait Teq { - type Other; - - fn transport(&self, x: T) -> Self::Other; - fn transport_sym(&self, x: Self::Other) -> T; -} - -impl Teq for U { - type Other = T; - - fn transport(&self, x: T) -> Self::Other { - x +impl AnElem for () { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Unit) } - fn transport_sym(&self, x: Self::Other) -> T { - x + fn to_elem(self) -> Elem { + Elem::Unit } -} - -#[derive(Clone)] -pub struct TEq -{ - teq: Arc>, -} - -impl PartialEq for TEq { - fn eq(&self, _other: &Self) -> bool { - true - } -} -impl Eq for TEq {} - -impl TEq { - pub fn refl(_x: PhantomData) -> Self { - TEq { - teq: Arc::new(()), + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Unit => Ok(()), + _ => Err(StackError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), } } } -impl TEq { - pub fn transport(&self, x: T) -> U { - (*self.teq).transport(x) +impl AnElem for bool { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Bool) } - pub fn transport_sym(&self, x: U) -> T { - (*self.teq).transport_sym(x) + fn to_elem(self) -> Elem { + Elem::Bool(self) } -} -impl fmt::Debug for TEq { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, - "Teq {{ teq: Teq<{0}, {1}> }}", - TypeName::type_name(PhantomData::), - TypeName::type_name(PhantomData::)) + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Bool(y) => Ok(y), + _ => Err(StackError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } } } -#[derive(Clone, PartialEq, Eq)] -pub enum IsElem { - Unit(TEq), - Bool(TEq), - Number(TEq), - Bytes(TEq>), - String(TEq), - Array(TEq>), - Object(TEq>), - Json(TEq), -} - -impl fmt::Debug for IsElem { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "IsElem {}", TypeName::type_name(PhantomData::)) +impl AnElem for Vec { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Bytes) } -} - -// impl fmt::Debug for TEq { -// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { -// write!(f, "Teq {{ teq: Teq<{0}, {1}> }}", TypeName::type_name(PhantomData), TypeName::type_name(PhantomData)) -// } -// } - - - - - -pub trait AnElem: Clone + std::fmt::Debug { - fn is_elem(x: PhantomData) -> IsElem where Self: Sized; -} - -// impl AnElem for Elem { + fn to_elem(self) -> Elem { + Elem::Bytes(self) + } -impl AnElem for () { - fn is_elem(_x: PhantomData) -> IsElem { - IsElem::Unit(TEq::refl(PhantomData)) + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Bytes(y) => Ok(y), + _ => Err(StackError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } } } -impl AnElem for bool { - fn is_elem(_x: PhantomData) -> IsElem { - IsElem::Bool(TEq::refl(PhantomData)) +impl AnElem for String { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::String) } -} -impl AnElem for Vec { - fn is_elem(_x: PhantomData) -> IsElem { - IsElem::Bytes(TEq::refl(PhantomData)) + fn to_elem(self) -> Elem { + Elem::String(self) } -} -impl AnElem for String { - fn is_elem(_x: PhantomData) -> IsElem { - IsElem::String(TEq::refl(PhantomData)) + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::String(y) => Ok(y), + _ => Err(StackError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } } } impl AnElem for Value { - fn is_elem(_x: PhantomData) -> IsElem { - IsElem::Json(TEq::refl(PhantomData)) + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Json) } -} -impl AnElem for Map { - fn is_elem(_x: PhantomData) -> IsElem { - IsElem::Object(TEq::refl(PhantomData)) + fn to_elem(self) -> Elem { + Elem::Json(self) } -} -impl IsElem { - pub fn elem_symbol(&self) -> ElemSymbol { - match self { - Self::Unit(_) => ElemSymbol::Unit, - Self::Bool(_) => ElemSymbol::Bool, - Self::Number(_) => ElemSymbol::Number, - Self::Bytes(_) => ElemSymbol::Bytes, - Self::String(_) => ElemSymbol::String, - Self::Array(_) => ElemSymbol::Array, - Self::Object(_) => ElemSymbol::Object, - Self::Json(_) => ElemSymbol::Json, + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Json(y) => Ok(y), + _ => Err(StackError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), } } +} - pub fn to_elem(&self, x: T) -> Elem { - match self { - Self::Unit(_) => Elem::Unit, - Self::Bool(eq) => Elem::Bool(eq.transport(x)), - Self::Number(eq) => Elem::Number(eq.transport(x)), - Self::Bytes(eq) => Elem::Bytes(eq.transport(x)), - Self::String(eq) => Elem::String(eq.transport(x)), - Self::Array(eq) => Elem::Array(eq.transport(x)), - Self::Object(eq) => Elem::Object(eq.transport(x)), - Self::Json(eq) => Elem::Json(eq.transport(x)), - } +impl AnElem for Map { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Object) } - // TODO: from_elem - pub fn from_elem(self, x: Elem) -> Option { - match (self, x) { - (Self::Unit(eq), Elem::Unit) => Some(eq.transport_sym(())), - (Self::Bool(eq), Elem::Bool(x)) => Some(eq.transport_sym(x)), - // (Self::Number(eq), Elem::Number(x)) => Some(eq.transport_sym(x)), - // (Self::Bytes(eq), Elem::Bytes(x)) => Some(eq.transport_sym(x)), - // (Self::String(eq), Elem::String(x)) => Some(eq.transport_sym(x)), - // (Self::Array(eq), Elem::Array(x)) => Some(eq.transport_sym(x)), - // (Self::Object(eq), Elem::Object(x)) => Some(eq.transport_sym(x)), - // (Self::Json(eq), Elem::Json(x)) => Some(eq.transport_sym(x)), - _ => None, - } + fn to_elem(self) -> Elem { + Elem::Object(self) } - pub fn pop(self, stack: &mut Stack) -> Result { - let hd_elem = stack.pop()?; - let elem_symbol = self.elem_symbol(); - Ok(self.from_elem(hd_elem.clone()) - .ok_or_else(|| StackError::UnexpectedElemType { + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Object(y) => Ok(y), + _ => Err(StackError::UnexpectedElemType { expected: elem_symbol, - found: hd_elem.clone(), - stack: stack.clone(), - })?) + found: x, + }), + } } } @@ -361,7 +248,7 @@ impl Iterator for IterConsElem { fn next(&mut self) -> Option { if self.at_head { - Some(AnElem::is_elem(PhantomData::).to_elem(self.cons.hd.clone())) + Some(self.cons.clone().hd.to_elem()) } else { let self_cons = self.cons.clone(); *self = self_cons.into_iter(); @@ -400,11 +287,7 @@ impl ElemList for ConsElem { fn pop(_x: PhantomData, stack: &mut Stack) -> Result { let hd_elem = stack.pop()?; Ok(ConsElem { - hd: AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { - expected: AnElem::is_elem(PhantomData::).elem_symbol(), - found: hd_elem.clone(), - stack: stack.clone(), - })?, + hd: ::from_elem(PhantomData, hd_elem)?, tl: Self::Tl::pop(PhantomData, stack)?, }) } @@ -493,7 +376,7 @@ impl Stack { name: format!("{:?}", instr), error: format!("{:?}", e), line_no: line_no})?; - Ok(self.push(AnElem::is_elem(PhantomData::).to_elem(output))) + Ok(self.push(output.to_elem())) } } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 11d0637..4a28d56 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,21 +1,18 @@ -use crate::elem::{Elem}; +use crate::elem::{Elem, ElemSymbol}; use crate::stack::{Stack, StackError}; -use crate::types::{Empty, AnElem, AnError, Nil, Teq, TEq, TypeName}; +use crate::types::{Empty, AnElem, AnError, Nil}; use std::iter::{FromIterator}; - -// use std::fmt; -// use std::fmt::{Display, Formatter}; use std::marker::PhantomData; -// use std::sync::Arc; +use enumset::EnumSet; use generic_array::typenum::{U0, U2}; use generic_array::sequence::GenericSequence; use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; use typenum::marker_traits::Unsigned; -pub trait Elems: Clone + Into { +pub trait Elems: AnElem { type Hd: AnElem; type Tl: Elems; @@ -23,47 +20,30 @@ pub trait Elems: Clone + Into { fn left(s: PhantomData, x: Self::Hd) -> Self; fn right(s: PhantomData, x: Self::Tl) -> Self; fn or T, G: Fn(Self::Tl) -> T>(&self, f: F, g: G) -> T; - fn pop(x: PhantomData, stack: &mut Stack) -> Result; } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Singleton { t: T, } -impl Into for Singleton { - fn into(self) -> Elem { - AnElem::is_elem(PhantomData::).to_elem(self.t) +impl AnElem for Singleton { + fn elem_symbol(_t: PhantomData) -> EnumSet { + ::elem_symbol(PhantomData) + } + + fn to_elem(self) -> Elem { + self.t.to_elem() } -} -// #[derive(Clone, PartialEq, Eq)] -// pub struct IterSingleton { -// iter: Option, -// } - -// impl IntoIterator for Singleton { -// type Item = Elem; -// type IntoIter = IterSingleton; -// fn into_iter(self) -> Self::IntoIter { -// IterSingleton { -// iter: Some(self.t), -// } -// } -// } - -// impl Iterator for IterSingleton { -// type Item = Elem; -// fn next(&mut self) -> Option { -// match self.iter.clone() { -// None => None, -// Some(x) => { -// self.iter = None; -// Some(AnElem::is_elem(PhantomData::).to_elem(x.clone())) -// }, -// } -// } -// } + fn from_elem(_t: PhantomData, x: Elem) -> Result { + ::from_elem(PhantomData, x).map(|y| { + Singleton { + t: y, + } + }) + } +} impl Elems for Singleton { type Hd = T; @@ -84,30 +64,46 @@ impl Elems for Singleton { fn or U, G: Fn(Self::Tl) -> U>(&self, f: F, _g: G) -> U { f(self.t.clone()) } - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - Ok(Singleton { - t: AnElem::is_elem(PhantomData::).pop(stack)?, - }) - } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Or { Left(T), Right(U), } -impl Into for Or { - fn into(self) -> Elem { +impl AnElem for Or { + fn elem_symbol(_t: PhantomData) -> EnumSet { + let t_set = ::elem_symbol(PhantomData); + let u_set = ::elem_symbol(PhantomData); + t_set.union(u_set) + } + + fn to_elem(self) -> Elem { match self { - Self::Left(x) => AnElem::is_elem(PhantomData).to_elem(x), - Self::Right(x) => x.into(), + Self::Left(x) => x.to_elem(), + Self::Right(x) => x.to_elem(), } } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + AnElem::from_elem(PhantomData::, x.clone()) + .map(|y| Or::Left(y)) + .or_else(|e_hd| { + Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) + .map_err(|e_tl| { + StackError::PopOr { + e_hd: Box::new(e_hd), + e_tl: Box::new(e_tl), + }}) + }) + } } + + + impl Elems for Or { type Hd = T; type Tl = U; @@ -133,20 +129,6 @@ impl Elems for Or { Self::Right(x) => g(x.clone()), } } - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - AnElem::is_elem(PhantomData::) - .pop(stack) - .map(|x| Or::Left(x)) - .or_else(|e_hd| { - Ok(Or::Right(Elems::pop(PhantomData::, stack)?)) - .map_err(|e_tl| { - StackError::PopOr { - e_hd: Box::new(e_hd), - e_tl: Box::new(e_tl), - }}) - }) - } } @@ -185,7 +167,7 @@ impl, U: IList> Iterator for IterCons { fn next(&mut self) -> Option { self.hd.next() - .map(|x| AnElem::is_elem(PhantomData::).to_elem(x)) + .map(|x| x.to_elem()) .or_else(|| self.cons.next()) } } @@ -282,11 +264,7 @@ impl Stack { let mut xs = vec![]; for _current_index in 1..::USIZE { let hd_elem = self.pop()?; - xs.push(AnElem::is_elem(PhantomData::).from_elem(hd_elem.clone()).ok_or_else(|| StackError::UnexpectedElemType { - expected: AnElem::is_elem(PhantomData::).elem_symbol(), - found: hd_elem.clone(), - stack: self.clone(), - })?) + xs.push(AnElem::from_elem(PhantomData::, hd_elem)?) } GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) } From b14af3c55b3aafe850ce24e034afd9ac0fb7ad47 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 2 Mar 2022 16:43:21 -0500 Subject: [PATCH 39/77] debugging and applying AnElem --- src/elem.rs | 214 ++++++++++++++++++++++++++++++++- src/stack.rs | 41 +++++-- src/types.rs | 142 +--------------------- src/types_scratch.rs | 275 ++++++++++++++++++++++++++++++++++++------- 4 files changed, 474 insertions(+), 198 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 463a4fd..033eb6e 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -4,6 +4,8 @@ use thiserror::Error; use std::cmp; use std::convert::TryFrom; +use std::marker::PhantomData; +use std::iter::{FromIterator, IntoIterator}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; @@ -234,7 +236,7 @@ impl Elem { Ok(Self::Bool(result)) } - fn concat_generic::Item>>(x: T, y: T) -> T { + fn concat_generic::Item>>(x: T, y: T) -> T { x.into_iter().chain(y.into_iter()).collect() } @@ -257,10 +259,10 @@ impl Elem { } fn slice_generic::Item>>(offset: Number, - length: Number, - iterable: T, - elem_symbol: ElemSymbol) -> + FromIterator<::Item>>(offset: Number, + length: Number, + iterable: T, + elem_symbol: ElemSymbol) -> Result { let u_offset = offset.as_u64() .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) @@ -305,7 +307,7 @@ impl Elem { } fn index_generic::Item>>(index: Number, + FromIterator<::Item>>(index: Number, iterable: T, elem_symbol: ElemSymbol) -> Result<::Item, ElemError> { @@ -531,3 +533,203 @@ impl From for ElemError { } } + + + + + + + + + + +pub trait AnElem: Clone + std::fmt::Debug { + fn elem_symbol(t: PhantomData) -> EnumSet; + fn to_elem(self) -> Elem; + fn from_elem(t: PhantomData, x: Elem) -> Result; +} + +impl AnElem for () { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Unit) + } + + fn to_elem(self) -> Elem { + Elem::Unit + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Unit => Ok(()), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for bool { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Bool) + } + + fn to_elem(self) -> Elem { + Elem::Bool(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Bool(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Number { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Number) + } + + fn to_elem(self) -> Elem { + Elem::Number(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Number(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Vec { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Bytes) + } + + fn to_elem(self) -> Elem { + Elem::Bytes(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Bytes(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for String { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::String) + } + + fn to_elem(self) -> Elem { + Elem::String(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::String(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Vec { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Array) + } + + fn to_elem(self) -> Elem { + Elem::Array(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Array(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Map { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Object) + } + + fn to_elem(self) -> Elem { + Elem::Object(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Object(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Value { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Json) + } + + fn to_elem(self) -> Elem { + Elem::Json(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Json(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + + +#[derive(Clone, Debug, Error)] +pub enum AnElemError { + #[error("AnElem::from_elem: element popped from the stack {found:?} wasn't the expected type {expected:?}")] + UnexpectedElemType { + expected: EnumSet, + found: Elem, + }, + + #[error(" as AnElem>::from_elem: {e_hd:?}\n{e_tl:?}")] + PopOr { + e_hd: Box, + e_tl: Box, + }, +} + diff --git a/src/stack.rs b/src/stack.rs index 310d0ba..acc6208 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,5 +1,5 @@ // use crate::restack::{RestackError}; -use crate::elem::{Elem, ElemSymbol}; +use crate::elem::{Elem, AnElem, AnElemError, ElemSymbol}; // use std::collections::BTreeMap; // use std::cmp; @@ -10,11 +10,14 @@ use crate::elem::{Elem, ElemSymbol}; // // use std::alloc::string; // use std::marker::PhantomData; // use std::sync::Arc; +use std::marker::PhantomData; use enumset::{EnumSet}; use serde::{Deserialize, Serialize}; // use serde_json::{Map, Number, Value}; use thiserror::Error; +use generic_array::{GenericArray, ArrayLength}; +use typenum::marker_traits::Unsigned; // TODO: use for execution @@ -32,24 +35,40 @@ impl Stack { Ok(result.clone()) } + pub fn pop_elem(&mut self, _t: PhantomData) -> Result { + let hd_elem = self.pop()?; + Ok(::from_elem(PhantomData, hd_elem)?) + } + pub fn push(&mut self, elem: Elem) { let mut memo = vec![elem]; // memo.append(&mut self.stack.clone()); memo.append(&mut self.stack); self.stack = memo; } + + // TODO: reversed? + pub fn pop_generic_array>(&mut self, + _t: PhantomData, + _n: PhantomData) -> Result, StackError> { + let mut xs = vec![]; + for _current_index in 1..::USIZE { + let hd_elem = self.pop()?; + xs.push(AnElem::from_elem(PhantomData::, hd_elem)?) + } + GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) + } } + + #[derive(Clone, Debug, Error)] pub enum StackError { #[error("Stack::pop: tried to pop from an empty stack")] EmptyStack, - #[error("AnElem::from_elem: element popped from the stack {found:?} wasn't the expected type {expected:?}")] - UnexpectedElemType { - expected: EnumSet, - found: Elem, - }, + #[error("Stack:pop_elem threw an error from AnElem {0:?}")] + AnElemError(AnElemError), #[error("pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack:?})")] UnexpectedElemTypeIn { @@ -67,12 +86,12 @@ pub enum StackError { #[error("Stack::pop_generic_array: unimplemented")] TODO, +} - #[error("HList::TODO: {e_hd:?}\n{e_tl:?}")] - PopOr { - e_hd: Box, - e_tl: Box, - }, +impl From for StackError { + fn from(x: AnElemError) -> Self { + Self::AnElemError(x) + } } // TODO: relocate LineNo, ArgumentIndex, Location diff --git a/src/types.rs b/src/types.rs index 1a16de7..5265946 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,5 @@ // use crate::restack::{RestackError}; -use crate::elem::{Elem, ElemSymbol}; +use crate::elem::{AnElem, Elem, ElemSymbol}; use crate::stack::{Stack, StackError, LineNo, Location}; use std::collections::BTreeMap; @@ -37,146 +37,6 @@ impl Iterator for Nil { } } -pub trait AnElem: Clone + std::fmt::Debug { - fn elem_symbol(t: PhantomData) -> EnumSet; - fn to_elem(self) -> Elem; - - fn from_elem(t: PhantomData, x: Elem) -> Result; - - fn pop(_t: PhantomData, stack: &mut Stack) -> Result { - let hd_elem = stack.pop()?; - ::from_elem(PhantomData, hd_elem) - } -} - -impl AnElem for () { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Unit) - } - - fn to_elem(self) -> Elem { - Elem::Unit - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Unit => Ok(()), - _ => Err(StackError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for bool { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Bool) - } - - fn to_elem(self) -> Elem { - Elem::Bool(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Bool(y) => Ok(y), - _ => Err(StackError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Vec { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Bytes) - } - - fn to_elem(self) -> Elem { - Elem::Bytes(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Bytes(y) => Ok(y), - _ => Err(StackError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for String { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::String) - } - - fn to_elem(self) -> Elem { - Elem::String(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::String(y) => Ok(y), - _ => Err(StackError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Value { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Json) - } - - fn to_elem(self) -> Elem { - Elem::Json(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Json(y) => Ok(y), - _ => Err(StackError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Map { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Object) - } - - fn to_elem(self) -> Elem { - Elem::Object(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Object(y) => Ok(y), - _ => Err(StackError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - - - pub trait ElemList: Clone + IntoIterator { type Hd: AnElem; type Tl: ElemList; diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 4a28d56..be983d6 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,6 +1,6 @@ -use crate::elem::{Elem, ElemSymbol}; +use crate::elem::{Elem, AnElem, AnElemError, ElemSymbol}; use crate::stack::{Stack, StackError}; -use crate::types::{Empty, AnElem, AnError, Nil}; +use crate::types::{AnError, Nil}; use std::iter::{FromIterator}; use std::marker::PhantomData; @@ -9,8 +9,11 @@ use enumset::EnumSet; use generic_array::typenum::{U0, U2}; use generic_array::sequence::GenericSequence; use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; -use typenum::marker_traits::Unsigned; +// use typenum::marker_traits::Unsigned; +use serde_json::{Map, Value}; +// NEXT: +// - Acheive parity between ElemList -> Elems/IList/IOList pub trait Elems: AnElem { type Hd: AnElem; @@ -36,7 +39,7 @@ impl AnElem for Singleton { self.t.to_elem() } - fn from_elem(_t: PhantomData, x: Elem) -> Result { + fn from_elem(_t: PhantomData, x: Elem) -> Result { ::from_elem(PhantomData, x).map(|y| { Singleton { t: y, @@ -87,13 +90,13 @@ impl AnElem for Or { } } - fn from_elem(_t: PhantomData, x: Elem) -> Result { + fn from_elem(_t: PhantomData, x: Elem) -> Result { AnElem::from_elem(PhantomData::, x.clone()) .map(|y| Or::Left(y)) .or_else(|e_hd| { Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) .map_err(|e_tl| { - StackError::PopOr { + AnElemError::PopOr { e_hd: Box::new(e_hd), e_tl: Box::new(e_tl), }}) @@ -138,6 +141,22 @@ impl Elems for Or { + + + + +pub trait IList: Clone + IntoIterator { + type Hd: AnElem; + type N: ArrayLength; + type Tl: IList; + + fn is_empty(&self) -> bool; + fn hd(&self) -> GenericArray; + fn tl(&self) -> Self::Tl; + fn cons>(self, x: GenericArray) -> Cons where Self: Sized; + fn pop(x: PhantomData, stack: &mut Stack) -> Result; +} + // fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; #[derive(Clone, PartialEq, Eq)] pub struct Cons, U: IList> { @@ -172,21 +191,6 @@ impl, U: IList> Iterator for IterCons { } } - - - -pub trait IList: Clone + IntoIterator { - type Hd: AnElem; - type N: ArrayLength; - type Tl: IList; - - fn is_empty(&self) -> bool; - fn hd(&self) -> GenericArray; - fn tl(&self) -> Self::Tl; - fn cons>(self, x: GenericArray) -> Cons where Self: Sized; - fn pop(x: PhantomData, stack: &mut Stack) -> Result; -} - impl IList for Nil { type Hd = (); type N = U0; @@ -256,21 +260,6 @@ impl, U: IList> IList for Cons { } } -impl Stack { - // TODO: reversed? - pub fn pop_generic_array>(&mut self, - _t: PhantomData, - _n: PhantomData) -> Result, StackError> { - let mut xs = vec![]; - for _current_index in 1..::USIZE { - let hd_elem = self.pop()?; - xs.push(AnElem::from_elem(PhantomData::, hd_elem)?) - } - GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) - } -} - - pub trait IOList: IList { type Return: AnElem; } @@ -338,6 +327,198 @@ pub trait IsInstructionT: std::fmt::Debug { fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; } +pub trait FromIntoIterator: IntoIterator + FromIterator<::Item> {} + +impl IntoIterator for Singleton +where + T: AnElem + IntoIterator, +{ + type Item = ::Item; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.t.into_iter() + } +} + +impl FromIterator<::Item> for Singleton +where + T: AnElem + IntoIterator + FromIterator<::Item>, +{ + fn from_iter(iter: U) -> Self + where + U: IntoIterator::Item>, + { + Singleton { + t: ::Item>>::from_iter(iter), + } + } +} + +pub enum OrIter +where + T: AnElem + IntoIterator, + U: Elems + IntoIterator, +{ + Left(::IntoIter), + Right(::IntoIter), +} + +pub enum OrIterItem +where + T: AnElem + IntoIterator, + U: Elems + IntoIterator, +{ + Left(::Item), + Right(::Item), +} + +impl Iterator for OrIter +where + T: AnElem + IntoIterator, + U: Elems + IntoIterator, +{ + type Item = OrIterItem; + + fn next(&mut self) -> Option { + match self { + Self::Left(x) => x.next().map(|y| OrIterItem::Left(y)), + Self::Right(x) => x.next().map(|y| OrIterItem::Right(y)), + } + } +} + +impl IntoIterator for Or +where + T: AnElem + IntoIterator, + U: Elems + IntoIterator, +{ + type Item = OrIterItem; + type IntoIter = OrIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Left(x) => OrIter::Left(x.into_iter()), + Self::Right(x) => OrIter::Right(x.into_iter()), + } + } +} + + +pub struct ResultOrIterError { + result: Result, +} + +pub enum OrIterError { + FromEmptyIter, + + AnElemError(AnElemError), + + MoreThanSingleton { + // hd_elem: String, + // other_elems: Vec, + }, +} + +impl IntoIterator for ResultOrIterError +where + T: IntoIterator, +{ + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self.result { + Ok(x) => Some(x).into_iter(), + Err(_) => None.into_iter(), + } + } +} + +// impl FromIterator> for ResultOrIterError> +// where +// T: AnElem + IntoIterator + FromIterator<::Item>, +// U: Elems + IntoIterator + FromIterator<::Item>, +// { +// fn from_iter(iter: V) -> Self +// where +// V: IntoIterator>, +// { +// // let mut iter_mut = iter.into_iter(); +// // let opt_hd_elem = iter_mut.next(); +// // let rest = iter_mut.next(); +// // ResultOrIterError { +// // result: match (opt_hd_elem, rest) { +// // (None, _) => Err(OrIterError::FromEmptyIter), +// // (Some(hd_elem), Some(other_elems)) => Err(OrIterError::MoreThanSingleton { +// // // TODO: debug info +// // // hd_elem: hd_elem, +// // // other_elems: other_elems, +// // }), +// // (Some(OrIterItem::Left(hd_elem)), None) => +// // match as AnElem>::from_elem(PhantomData, hd_elem) { +// // Ok(x) => Ok(x), +// // Err(e) => Err(OrIterError::AnElemError(e)), +// // }, +// // (Some(OrIterItem::Right(hd_elem)), None) => +// // match as AnElem>::from_elem(PhantomData, hd_elem) { +// // Ok(x) => Ok(x), +// // Err(e) => Err(OrIterError::AnElemError(e)), +// // }, +// // }, +// // } +// } +// } + +// impl FromIterator> for OrIterFrom +// where +// T: AnElem + IntoIterator + FromIterator<::Item>, +// U: Elems + IntoIterator + FromIterator<::Item>, +// { +// fn from_iter(iter: V) -> Self +// where +// V: IntoIterator>, +// { +// let mut iter_mut = iter.into_iter(); +// let opt_hd_elem = iter_mut.next(); +// let rest = iter_mut.next(); +// match (opt_hd_elem, rest) { +// (None, _) => OrIterFrom::Err(OrIterError::FromEmptyIter), +// (Some(hd_elem), Some(other_elems)) => OrIterFrom::Err(OrIterError::MoreThanSingleton { +// // TODO: debug info +// // hd_elem: hd_elem, +// // other_elems: other_elems, +// }), +// (Some(OrIterItem::Left(hd_elem)), None) => +// match as AnElem>::from_elem(PhantomData, hd_elem) { +// Ok(x) => OrIterFrom::Or(x), +// Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), +// }, +// (Some(OrIterItem::Right(hd_elem)), None) => +// match as AnElem>::from_elem(PhantomData, hd_elem) { +// Ok(x) => OrIterFrom::Or(x), +// Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), +// }, +// } +// } +// } + + + +// impl FromIterator<::Item> for Or +// where +// T: AnElem + IntoIterator + FromIterator<::Item>, +// U: Elems + IntoIterator::Item> + FromIterator<::Item>, +// { +// fn from_iter(iter: V) -> Self +// where +// V: IntoIterator::Item>, +// { +// _ +// } +// } + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Concat { t: PhantomData, @@ -346,18 +527,32 @@ struct Concat { struct ConcatError {} impl AnError for ConcatError {} -impl::Item>> IsInstructionT for Concat { - type In = ConsOut; + +// bytes, string, array, object +impl IsInstructionT for Concat<()> { + type In = ConsOut, Singleton>>, U2, Nil>; type Error = ConcatError; fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { - let lhs = x.hd()[0].clone(); - let rhs = x.hd()[1].clone(); + let lhs: Or, Singleton>> = x.hd()[0].clone(); + let rhs: Or, Singleton>> = x.hd()[1].clone(); Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) } } +// impl::Item>> IsInstructionT for Concat { +// type In = ConsOut; +// type Error = ConcatError; + +// fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { +// let lhs = x.hd()[0].clone(); +// let rhs = x.hd()[1].clone(); +// Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) +// } +// } + + From d112edcc5bd6b47787a6f60e8cbc067fa6809a3e Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 3 Mar 2022 15:27:13 -0500 Subject: [PATCH 40/77] iterator impls, debugging return type API --- src/types_scratch.rs | 821 +++++++++++++++++++++++++------------------ 1 file changed, 472 insertions(+), 349 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index be983d6..9c9f21b 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -4,57 +4,95 @@ use crate::types::{AnError, Nil}; use std::iter::{FromIterator}; use std::marker::PhantomData; +use std::fmt::Debug; +use std::sync::Arc; use enumset::EnumSet; use generic_array::typenum::{U0, U2}; use generic_array::sequence::GenericSequence; -use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; +use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; // use typenum::marker_traits::Unsigned; use serde_json::{Map, Value}; // NEXT: // - Acheive parity between ElemList -> Elems/IList/IOList -pub trait Elems: AnElem { +// pub trait IList: Clone + IntoIterator { +// type Hd: AnElem; +// type N: ArrayLength; +// type Tl: IList; + +// fn is_empty(&self) -> bool; +// fn hd(&self) -> GenericArray; +// fn tl(&self) -> Self::Tl; +// fn cons>(self, x: GenericArray) -> Cons where Self: Sized; +// fn pop(x: PhantomData, stack: &mut Stack) -> Result; + + +// pub trait AnElem: Clone + std::fmt::Debug { +// fn elem_symbol(t: PhantomData) -> ElemSymbol; +// fn elem_symbol(t: PhantomData) -> EnumSet; + +// fn to_elem(self) -> Elem; + +// fn from_elem(t: PhantomData, x: Elem) -> Result; +// } + + +pub trait Elems: Clone + Debug { type Hd: AnElem; - type Tl: Elems; + type N: ArrayLength; + type Tl: Elems; fn is_left(&self) -> bool; - fn left(s: PhantomData, x: Self::Hd) -> Self; + fn left(s: PhantomData, x: GenericArray) -> Self; fn right(s: PhantomData, x: Self::Tl) -> Self; - fn or T, G: Fn(Self::Tl) -> T>(&self, f: F, g: G) -> T; + fn or) -> T, G: Fn(Self::Tl) -> T>(&self, f: F, g: G) -> T; + + // fn elem_symbols(t: PhantomData) -> EnumSet; + // fn to_elems(self) -> Elem; + // fn from_elems(t: PhantomData, x: &mut Stack) -> Result; } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Singleton { - t: T, +pub struct Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + t: GenericArray, } -impl AnElem for Singleton { - fn elem_symbol(_t: PhantomData) -> EnumSet { - ::elem_symbol(PhantomData) - } +// impl AnElem for Singleton { +// fn elem_symbol(_t: PhantomData) -> EnumSet { +// ::elem_symbol(PhantomData) +// } - fn to_elem(self) -> Elem { - self.t.to_elem() - } +// fn to_elem(self) -> Elem { +// self.t.to_elem() +// } - fn from_elem(_t: PhantomData, x: Elem) -> Result { - ::from_elem(PhantomData, x).map(|y| { - Singleton { - t: y, - } - }) - } -} +// fn from_elem(_t: PhantomData, x: Elem) -> Result { +// ::from_elem(PhantomData, x).map(|y| { +// Singleton { +// t: y, +// } +// }) +// } +// } -impl Elems for Singleton { +impl Elems for Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ type Hd = T; - type Tl = Singleton; + type N = N; + type Tl = Singleton; fn is_left(&self) -> bool { true } - fn left(_s: PhantomData, x: Self::Hd) -> Self { + fn left(_s: PhantomData, x: GenericArray) -> Self { Singleton { t: x, } @@ -64,51 +102,59 @@ impl Elems for Singleton { x } - fn or U, G: Fn(Self::Tl) -> U>(&self, f: F, _g: G) -> U { + fn or) -> U, G: Fn(Self::Tl) -> U>(&self, f: F, _g: G) -> U { f(self.t.clone()) } } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum Or { - Left(T), +pub enum Or +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + Left(GenericArray), Right(U), } -impl AnElem for Or { - fn elem_symbol(_t: PhantomData) -> EnumSet { - let t_set = ::elem_symbol(PhantomData); - let u_set = ::elem_symbol(PhantomData); - t_set.union(u_set) - } - - fn to_elem(self) -> Elem { - match self { - Self::Left(x) => x.to_elem(), - Self::Right(x) => x.to_elem(), - } - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - AnElem::from_elem(PhantomData::, x.clone()) - .map(|y| Or::Left(y)) - .or_else(|e_hd| { - Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) - .map_err(|e_tl| { - AnElemError::PopOr { - e_hd: Box::new(e_hd), - e_tl: Box::new(e_tl), - }}) - }) - } -} - +// impl AnElem for Or { +// fn elem_symbol(_t: PhantomData) -> EnumSet { +// let t_set = ::elem_symbol(PhantomData); +// let u_set = ::elem_symbol(PhantomData); +// t_set.union(u_set) +// } +// fn to_elem(self) -> Elem { +// match self { +// Self::Left(x) => x.to_elem(), +// Self::Right(x) => x.to_elem(), +// } +// } +// fn from_elem(_t: PhantomData, x: Elem) -> Result { +// AnElem::from_elem(PhantomData::, x.clone()) +// .map(|y| Or::Left(y)) +// .or_else(|e_hd| { +// Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) +// .map_err(|e_tl| { +// AnElemError::PopOr { +// e_hd: Box::new(e_hd), +// e_tl: Box::new(e_tl), +// }}) +// }) +// } +// } -impl Elems for Or { +impl Elems for Or +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ type Hd = T; + type N = N; type Tl = U; fn is_left(&self) -> bool { @@ -118,7 +164,7 @@ impl Elems for Or { } } - fn left(_s: PhantomData, x: Self::Hd) -> Self { + fn left(_s: PhantomData, x: GenericArray) -> Self { Self::Left(x) } @@ -126,7 +172,7 @@ impl Elems for Or { Self::Right(x) } - fn or V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { + fn or) -> V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { match self { Self::Left(x) => f(x.clone()), Self::Right(x) => g(x.clone()), @@ -140,384 +186,270 @@ impl Elems for Or { - - - - - -pub trait IList: Clone + IntoIterator { - type Hd: AnElem; - type N: ArrayLength; +// + IntoIterator +pub trait IList: Clone { + type Hd: Elems; + // type N: ArrayLength; type Tl: IList; fn is_empty(&self) -> bool; - fn hd(&self) -> GenericArray; - fn tl(&self) -> Self::Tl; - fn cons>(self, x: GenericArray) -> Cons where Self: Sized; - fn pop(x: PhantomData, stack: &mut Stack) -> Result; + // fn hd(&self) -> GenericArray; + fn hd(self) -> Self::Hd; + fn tl(self) -> Self::Tl; + // fn cons>(self, x: GenericArray) -> Cons where Self: Sized; + fn cons(self, x: T) -> Cons { + Cons { + hd: x, + tl: self, + } + } + // where Self: Sized; + + // fn pop(x: PhantomData, stack: &mut Stack) -> Result; } // fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; #[derive(Clone, PartialEq, Eq)] -pub struct Cons, U: IList> { - hd: GenericArray, +// pub struct Cons, U: IList> { +// hd: GenericArray, +// tl: U, +// } +pub struct Cons { + hd: T, tl: U, } -pub struct IterCons, U: IList> { - hd: GenericArrayIter, - cons: ::IntoIter, -} +// pub struct IterCons, U: IList> { +// hd: GenericArrayIter, +// cons: ::IntoIter, +// } -impl, U: IList> IntoIterator for Cons { - type Item = Elem; - type IntoIter = IterCons; +// impl, U: IList> IntoIterator for Cons { +// type Item = Elem; +// type IntoIter = IterCons; - fn into_iter(self) -> Self::IntoIter { - IterCons { - hd: self.hd.into_iter(), - cons: self.tl.into_iter(), - } - } -} +// fn into_iter(self) -> Self::IntoIter { +// IterCons { +// hd: self.hd.into_iter(), +// cons: self.tl.into_iter(), +// } +// } +// } -impl, U: IList> Iterator for IterCons { - type Item = Elem; +// impl, U: IList> Iterator for IterCons { +// type Item = Elem; - fn next(&mut self) -> Option { - self.hd.next() - .map(|x| x.to_elem()) - .or_else(|| self.cons.next()) - } -} +// fn next(&mut self) -> Option { +// self.hd.next() +// .map(|x| x.to_elem()) +// .or_else(|| self.cons.next()) +// } +// } impl IList for Nil { - type Hd = (); - type N = U0; + type Hd = Singleton<(), U0>; type Tl = Nil; fn is_empty(&self) -> bool { true } - fn hd(&self) -> GenericArray { - GenericArray::generate(|_| ()) + fn hd(self) -> Self::Hd { + Singleton { + t: GenericArray::generate(|_| ()), + } } - fn tl(&self) -> Self::Tl { + fn tl(self) -> Self::Tl { Self {} } - fn cons>(self, x: GenericArray) -> Cons - where - Self: Sized, - { - Cons { - hd: x, - tl: self, - } - } - - fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { - Ok(Nil {}) - } + // fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { + // Ok(Nil {}) + // } } -impl, U: IList> IList for Cons { +impl IList for Cons { type Hd = T; - type N = N; type Tl = U; fn is_empty(&self) -> bool { false } - fn hd(&self) -> GenericArray { - self.hd.clone() - } - - fn tl(&self) -> Self::Tl { - self.tl.clone() + fn hd(self) -> Self::Hd { + self.hd } - fn cons>(self, x: GenericArray) -> Cons - where - Self: Sized, - { - Cons { - hd: x, - tl: self, - } + fn tl(self) -> Self::Tl { + self.tl } - // TODO: add better errors - fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - let hd_arr = stack.pop_generic_array(PhantomData, PhantomData)?; - Ok(Cons { - hd: hd_arr, - tl: Self::Tl::pop(PhantomData, stack)?, - }) - } + // // TODO: add better errors + // fn pop(_x: PhantomData, stack: &mut Stack) -> Result { + // let hd_arr = stack.pop_generic_array(PhantomData, PhantomData)?; + // Ok(Cons { + // hd: hd_arr, + // tl: Self::Tl::pop(PhantomData, stack)?, + // }) + // } } -pub trait IOList: IList { - type Return: AnElem; -} -#[derive(Clone, PartialEq, Eq)] -pub struct ConsOut, U: IList> { - cons: Cons, -} -impl, U: IList> IntoIterator for ConsOut { - type Item = Elem; - type IntoIter = IterCons; - fn into_iter(self) -> Self::IntoIter { - self.cons.into_iter() - } -} -impl, U: IList> IList for ConsOut { - type Hd = T; - type N = N; - type Tl = U; - fn is_empty(&self) -> bool { - self.cons.is_empty() - } - fn hd(&self) -> GenericArray { - self.cons.hd() - } - fn tl(&self) -> Self::Tl { - self.cons.tl() - } - fn cons>(self, x: GenericArray) -> Cons - where - Self: Sized, - { - Cons { - hd: x, - tl: self, - } - } - fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - Ok(ConsOut { - cons: IList::pop(PhantomData, stack)?, - }) - } -} -impl, U: IList> IOList for ConsOut { - type Return = T; -} -impl, U: IOList> IOList for Cons { - type Return = ::Return; -} -pub trait IsInstructionT: std::fmt::Debug { - type In: IOList; - type Error: AnError; - fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; +/// return_value is private, but get_return_value can be used to extract it +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Return { + return_value: T, } -pub trait FromIntoIterator: IntoIterator + FromIterator<::Item> {} - -impl IntoIterator for Singleton -where - T: AnElem + IntoIterator, -{ - type Item = ::Item; - type IntoIter = ::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.t.into_iter() +impl Return { + pub fn get_return_value(self) -> T { + self.return_value } } -impl FromIterator<::Item> for Singleton -where - T: AnElem + IntoIterator + FromIterator<::Item>, -{ - fn from_iter(iter: U) -> Self - where - U: IntoIterator::Item>, - { - Singleton { - t: ::Item>>::from_iter(iter), - } - } +pub trait IOList: IList { + type Return: AnElem; } -pub enum OrIter +#[derive(Clone)] +pub enum ConsOut where - T: AnElem + IntoIterator, - U: Elems + IntoIterator, + T: AnElem, + N: ArrayLength + Debug, + U: Elems, + V: IList, { - Left(::IntoIter), - Right(::IntoIter), + Left { + return_fn: Arc Return>, + hd: Singleton, + tl: V, + }, + Right { + hd: U, + tl: V, + } } -pub enum OrIterItem +impl IList for ConsOut where - T: AnElem + IntoIterator, - U: Elems + IntoIterator, + T: AnElem, + N: ArrayLength + Debug, + U: Elems, + V: IList, { - Left(::Item), - Right(::Item), -} + type Hd = Or; + type Tl = V; -impl Iterator for OrIter -where - T: AnElem + IntoIterator, - U: Elems + IntoIterator, -{ - type Item = OrIterItem; + fn is_empty(&self) -> bool { + false + } - fn next(&mut self) -> Option { + fn hd(self) -> Self::Hd { match self { - Self::Left(x) => x.next().map(|y| OrIterItem::Left(y)), - Self::Right(x) => x.next().map(|y| OrIterItem::Right(y)), + Self::Left { hd, .. } => Or::Left(hd.t), + Self::Right { hd, .. } => Or::Right(hd), } } -} - -impl IntoIterator for Or -where - T: AnElem + IntoIterator, - U: Elems + IntoIterator, -{ - type Item = OrIterItem; - type IntoIter = OrIter; - fn into_iter(self) -> Self::IntoIter { + fn tl(self) -> Self::Tl { match self { - Self::Left(x) => OrIter::Left(x.into_iter()), - Self::Right(x) => OrIter::Right(x.into_iter()), + Self::Left { tl, .. } => tl, + Self::Right { tl, .. } => tl, } } } +impl IOList for ConsOut +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, + V: IList, +{ + type Return = T; -pub struct ResultOrIterError { - result: Result, + fn returning(&self) -> Arc Return>; } -pub enum OrIterError { - FromEmptyIter, - - AnElemError(AnElemError), - - MoreThanSingleton { - // hd_elem: String, - // other_elems: Vec, - }, -} - -impl IntoIterator for ResultOrIterError +impl IOList for Cons where - T: IntoIterator, + T: Elems, + U: IOList, { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - match self.result { - Ok(x) => Some(x).into_iter(), - Err(_) => None.into_iter(), - } - } + type Return = U::Return; } -// impl FromIterator> for ResultOrIterError> -// where -// T: AnElem + IntoIterator + FromIterator<::Item>, -// U: Elems + IntoIterator + FromIterator<::Item>, -// { -// fn from_iter(iter: V) -> Self -// where -// V: IntoIterator>, -// { -// // let mut iter_mut = iter.into_iter(); -// // let opt_hd_elem = iter_mut.next(); -// // let rest = iter_mut.next(); -// // ResultOrIterError { -// // result: match (opt_hd_elem, rest) { -// // (None, _) => Err(OrIterError::FromEmptyIter), -// // (Some(hd_elem), Some(other_elems)) => Err(OrIterError::MoreThanSingleton { -// // // TODO: debug info -// // // hd_elem: hd_elem, -// // // other_elems: other_elems, -// // }), -// // (Some(OrIterItem::Left(hd_elem)), None) => -// // match as AnElem>::from_elem(PhantomData, hd_elem) { -// // Ok(x) => Ok(x), -// // Err(e) => Err(OrIterError::AnElemError(e)), -// // }, -// // (Some(OrIterItem::Right(hd_elem)), None) => -// // match as AnElem>::from_elem(PhantomData, hd_elem) { -// // Ok(x) => Ok(x), -// // Err(e) => Err(OrIterError::AnElemError(e)), -// // }, -// // }, -// // } +// impl, U: IList> IntoIterator for ConsOut { +// type Item = Elem; +// type IntoIter = IterCons; + +// fn into_iter(self) -> Self::IntoIter { +// self.cons.into_iter() // } // } -// impl FromIterator> for OrIterFrom -// where -// T: AnElem + IntoIterator + FromIterator<::Item>, -// U: Elems + IntoIterator + FromIterator<::Item>, -// { -// fn from_iter(iter: V) -> Self -// where -// V: IntoIterator>, -// { -// let mut iter_mut = iter.into_iter(); -// let opt_hd_elem = iter_mut.next(); -// let rest = iter_mut.next(); -// match (opt_hd_elem, rest) { -// (None, _) => OrIterFrom::Err(OrIterError::FromEmptyIter), -// (Some(hd_elem), Some(other_elems)) => OrIterFrom::Err(OrIterError::MoreThanSingleton { -// // TODO: debug info -// // hd_elem: hd_elem, -// // other_elems: other_elems, -// }), -// (Some(OrIterItem::Left(hd_elem)), None) => -// match as AnElem>::from_elem(PhantomData, hd_elem) { -// Ok(x) => OrIterFrom::Or(x), -// Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), -// }, -// (Some(OrIterItem::Right(hd_elem)), None) => -// match as AnElem>::from_elem(PhantomData, hd_elem) { -// Ok(x) => OrIterFrom::Or(x), -// Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), -// }, -// } +// impl, U: IList> IList for ConsOut { +// type Hd = T; +// type N = N; +// type Tl = U; + +// fn is_empty(&self) -> bool { +// self.cons.is_empty() // } -// } +// fn hd(&self) -> GenericArray { +// self.cons.hd() +// } +// fn tl(&self) -> Self::Tl { +// self.cons.tl() +// } -// impl FromIterator<::Item> for Or -// where -// T: AnElem + IntoIterator + FromIterator<::Item>, -// U: Elems + IntoIterator::Item> + FromIterator<::Item>, -// { -// fn from_iter(iter: V) -> Self +// fn cons>(self, x: GenericArray) -> Cons // where -// V: IntoIterator::Item>, +// Self: Sized, // { -// _ +// Cons { +// hd: x, +// tl: self, +// } +// } + +// fn pop(_x: PhantomData, stack: &mut Stack) -> Result { +// Ok(ConsOut { +// cons: IList::pop(PhantomData, stack)?, +// }) // } // } +// impl, U: IList> IOList for ConsOut { +// type Return = T; +// } + +// impl, U: IOList> IOList for Cons { +// type Return = ::Return; +// } + +pub trait IsInstructionT: std::fmt::Debug { + type In: IOList; + type Error: AnError; + + fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Concat { @@ -528,17 +460,17 @@ struct ConcatError {} impl AnError for ConcatError {} -// bytes, string, array, object -impl IsInstructionT for Concat<()> { - type In = ConsOut, Singleton>>, U2, Nil>; - type Error = ConcatError; +//// bytes, string, array, object +// impl IsInstructionT for Concat<()> { +// type In = ConsOut, Singleton>>, U2, Nil>; +// type Error = ConcatError; - fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { - let lhs: Or, Singleton>> = x.hd()[0].clone(); - let rhs: Or, Singleton>> = x.hd()[1].clone(); - Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) - } -} +// fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { +// let lhs: Or, Singleton>> = x.hd()[0].clone(); +// let rhs: Or, Singleton>> = x.hd()[1].clone(); +// Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) +// } +// } // impl::Item>> IsInstructionT for Concat { @@ -552,6 +484,197 @@ impl IsInstructionT for Concat<()> { // } // } +// pub trait FromIntoIterator: IntoIterator + FromIterator<::Item> {} + +// impl IntoIterator for Singleton +// where +// T: AnElem + IntoIterator, +// { +// type Item = ::Item; +// type IntoIter = ::IntoIter; + +// fn into_iter(self) -> Self::IntoIter { +// self.t.into_iter() +// } +// } + +// impl FromIterator<::Item> for Singleton +// where +// T: AnElem + IntoIterator + FromIterator<::Item>, +// { +// fn from_iter(iter: U) -> Self +// where +// U: IntoIterator::Item>, +// { +// Singleton { +// t: ::Item>>::from_iter(iter), +// } +// } +// } + +// pub enum OrIter +// where +// T: AnElem + IntoIterator, +// U: Elems + IntoIterator, +// { +// Left(::IntoIter), +// Right(::IntoIter), +// } + +// pub enum OrIterItem +// where +// T: AnElem + IntoIterator, +// U: Elems + IntoIterator, +// { +// Left(::Item), +// Right(::Item), +// } + +// impl Iterator for OrIter +// where +// T: AnElem + IntoIterator, +// U: Elems + IntoIterator, +// { +// type Item = OrIterItem; + +// fn next(&mut self) -> Option { +// match self { +// Self::Left(x) => x.next().map(|y| OrIterItem::Left(y)), +// Self::Right(x) => x.next().map(|y| OrIterItem::Right(y)), +// } +// } +// } + +// impl IntoIterator for Or +// where +// T: AnElem + IntoIterator, +// U: Elems + IntoIterator, +// { +// type Item = OrIterItem; +// type IntoIter = OrIter; + +// fn into_iter(self) -> Self::IntoIter { +// match self { +// Self::Left(x) => OrIter::Left(x.into_iter()), +// Self::Right(x) => OrIter::Right(x.into_iter()), +// } +// } +// } + + +// pub struct ResultOrIterError { +// result: Result, +// } + +// pub enum OrIterError { +// FromEmptyIter, + +// AnElemError(AnElemError), + +// MoreThanSingleton { +// // hd_elem: String, +// // other_elems: Vec, +// }, +// } + +// impl IntoIterator for ResultOrIterError +// where +// T: IntoIterator, +// { +// type Item = as IntoIterator>::Item; +// type IntoIter = as IntoIterator>::IntoIter; + +// fn into_iter(self) -> Self::IntoIter { +// match self.result { +// Ok(x) => Some(x).into_iter(), +// Err(_) => None.into_iter(), +// } +// } +// } + +// // impl fromiterator> for resultoritererror> +// // where +// // T: AnElem + IntoIterator + FromIterator<::Item>, +// // U: Elems + IntoIterator + FromIterator<::Item>, +// // { +// // fn from_iter(iter: V) -> Self +// // where +// // V: IntoIterator>, +// // { +// // // let mut iter_mut = iter.into_iter(); +// // // let opt_hd_elem = iter_mut.next(); +// // // let rest = iter_mut.next(); +// // // ResultOrIterError { +// // // result: match (opt_hd_elem, rest) { +// // // (None, _) => Err(OrIterError::FromEmptyIter), +// // // (Some(hd_elem), Some(other_elems)) => Err(OrIterError::MoreThanSingleton { +// // // // TODO: debug info +// // // // hd_elem: hd_elem, +// // // // other_elems: other_elems, +// // // }), +// // // (Some(OrIterItem::Left(hd_elem)), None) => +// // // match as AnElem>::from_elem(PhantomData, hd_elem) { +// // // Ok(x) => Ok(x), +// // // Err(e) => Err(OrIterError::AnElemError(e)), +// // // }, +// // // (Some(OrIterItem::Right(hd_elem)), None) => +// // // match as AnElem>::from_elem(PhantomData, hd_elem) { +// // // Ok(x) => Ok(x), +// // // Err(e) => Err(OrIterError::AnElemError(e)), +// // // }, +// // // }, +// // // } +// // } +// // } + +// // impl FromIterator> for OrIterFrom +// // where +// // T: AnElem + IntoIterator + FromIterator<::Item>, +// // U: Elems + IntoIterator + FromIterator<::Item>, +// // { +// // fn from_iter(iter: V) -> Self +// // where +// // V: IntoIterator>, +// // { +// // let mut iter_mut = iter.into_iter(); +// // let opt_hd_elem = iter_mut.next(); +// // let rest = iter_mut.next(); +// // match (opt_hd_elem, rest) { +// // (None, _) => OrIterFrom::Err(OrIterError::FromEmptyIter), +// // (Some(hd_elem), Some(other_elems)) => OrIterFrom::Err(OrIterError::MoreThanSingleton { +// // // TODO: debug info +// // // hd_elem: hd_elem, +// // // other_elems: other_elems, +// // }), +// // (Some(OrIterItem::Left(hd_elem)), None) => +// // match as AnElem>::from_elem(PhantomData, hd_elem) { +// // Ok(x) => OrIterFrom::Or(x), +// // Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), +// // }, +// // (Some(OrIterItem::Right(hd_elem)), None) => +// // match as AnElem>::from_elem(PhantomData, hd_elem) { +// // Ok(x) => OrIterFrom::Or(x), +// // Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), +// // }, +// // } +// // } +// // } + + + +// // impl FromIterator<::Item> for Or +// // where +// // T: AnElem + IntoIterator + FromIterator<::Item>, +// // U: Elems + IntoIterator::Item> + FromIterator<::Item>, +// // { +// // fn from_iter(iter: V) -> Self +// // where +// // V: IntoIterator::Item>, +// // { +// // _ +// // } +// // } + From bdb83b194a2f85d665b148f05ef4727903caa59f Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 3 Mar 2022 16:47:38 -0500 Subject: [PATCH 41/77] fix returning too many values --- src/types_scratch.rs | 339 +++++-------------------------------------- 1 file changed, 36 insertions(+), 303 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 9c9f21b..ee9df43 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -185,68 +185,25 @@ where - // + IntoIterator -pub trait IList: Clone { +pub trait IsList: Clone { type Hd: Elems; - // type N: ArrayLength; - type Tl: IList; + type Tl: IsList; fn is_empty(&self) -> bool; - // fn hd(&self) -> GenericArray; fn hd(self) -> Self::Hd; fn tl(self) -> Self::Tl; - // fn cons>(self, x: GenericArray) -> Cons where Self: Sized; fn cons(self, x: T) -> Cons { Cons { hd: x, tl: self, } } - // where Self: Sized; // fn pop(x: PhantomData, stack: &mut Stack) -> Result; } -// fn cons>(self, u: PhantomData, x: V) -> ConsT where Self: Sized; -#[derive(Clone, PartialEq, Eq)] -// pub struct Cons, U: IList> { -// hd: GenericArray, -// tl: U, -// } -pub struct Cons { - hd: T, - tl: U, -} - -// pub struct IterCons, U: IList> { -// hd: GenericArrayIter, -// cons: ::IntoIter, -// } - -// impl, U: IList> IntoIterator for Cons { -// type Item = Elem; -// type IntoIter = IterCons; - -// fn into_iter(self) -> Self::IntoIter { -// IterCons { -// hd: self.hd.into_iter(), -// cons: self.tl.into_iter(), -// } -// } -// } - -// impl, U: IList> Iterator for IterCons { -// type Item = Elem; - -// fn next(&mut self) -> Option { -// self.hd.next() -// .map(|x| x.to_elem()) -// .or_else(|| self.cons.next()) -// } -// } - -impl IList for Nil { +impl IsList for Nil { type Hd = Singleton<(), U0>; type Tl = Nil; @@ -263,13 +220,15 @@ impl IList for Nil { fn tl(self) -> Self::Tl { Self {} } +} - // fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { - // Ok(Nil {}) - // } +#[derive(Clone, PartialEq, Eq)] +pub struct Cons { + hd: T, + tl: U, } -impl IList for Cons { +impl IsList for Cons { type Hd = T; type Tl = U; @@ -300,28 +259,35 @@ impl IList for Cons { +pub trait IList: IsList { +} +impl IList for Nil { +} - - - +impl IList for Cons { +} /// return_value is private, but get_return_value can be used to extract it #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Return { - return_value: T, +pub struct Return { + for_instruction: PhantomData, + return_value: U, } -impl Return { - pub fn get_return_value(self) -> T { +impl Return { + pub fn get_return_value(self) -> U { self.return_value } } -pub trait IOList: IList { + +pub trait IOList: IsList { type Return: AnElem; + + fn returning(self) -> Option Return<(), Self::Return>>>; } #[derive(Clone)] @@ -333,7 +299,7 @@ where V: IList, { Left { - return_fn: Arc Return>, + return_fn: Arc Return<(), T>>, hd: Singleton, tl: V, }, @@ -343,7 +309,7 @@ where } } -impl IList for ConsOut +impl IsList for ConsOut where T: AnElem, N: ArrayLength + Debug, @@ -381,7 +347,12 @@ where { type Return = T; - fn returning(&self) -> Arc Return>; + fn returning(self) -> Option Return<(), T>>> { + match self { + Self::Left { return_fn, .. } => Some(return_fn), + Self::Right { .. } => None, + } + } } impl IOList for Cons @@ -390,58 +361,18 @@ where U: IOList, { type Return = U::Return; -} - -// impl, U: IList> IntoIterator for ConsOut { -// type Item = Elem; -// type IntoIter = IterCons; - -// fn into_iter(self) -> Self::IntoIter { -// self.cons.into_iter() -// } -// } -// impl, U: IList> IList for ConsOut { -// type Hd = T; -// type N = N; -// type Tl = U; + fn returning(self) -> Option Return<(), U::Return>>> { + self.tl.returning() + } +} -// fn is_empty(&self) -> bool { -// self.cons.is_empty() -// } -// fn hd(&self) -> GenericArray { -// self.cons.hd() -// } -// fn tl(&self) -> Self::Tl { -// self.cons.tl() -// } -// fn cons>(self, x: GenericArray) -> Cons -// where -// Self: Sized, -// { -// Cons { -// hd: x, -// tl: self, -// } -// } -// fn pop(_x: PhantomData, stack: &mut Stack) -> Result { -// Ok(ConsOut { -// cons: IList::pop(PhantomData, stack)?, -// }) -// } -// } -// impl, U: IList> IOList for ConsOut { -// type Return = T; -// } -// impl, U: IOList> IOList for Cons { -// type Return = ::Return; -// } pub trait IsInstructionT: std::fmt::Debug { type In: IOList; @@ -484,201 +415,3 @@ impl AnError for ConcatError {} // } // } -// pub trait FromIntoIterator: IntoIterator + FromIterator<::Item> {} - -// impl IntoIterator for Singleton -// where -// T: AnElem + IntoIterator, -// { -// type Item = ::Item; -// type IntoIter = ::IntoIter; - -// fn into_iter(self) -> Self::IntoIter { -// self.t.into_iter() -// } -// } - -// impl FromIterator<::Item> for Singleton -// where -// T: AnElem + IntoIterator + FromIterator<::Item>, -// { -// fn from_iter(iter: U) -> Self -// where -// U: IntoIterator::Item>, -// { -// Singleton { -// t: ::Item>>::from_iter(iter), -// } -// } -// } - -// pub enum OrIter -// where -// T: AnElem + IntoIterator, -// U: Elems + IntoIterator, -// { -// Left(::IntoIter), -// Right(::IntoIter), -// } - -// pub enum OrIterItem -// where -// T: AnElem + IntoIterator, -// U: Elems + IntoIterator, -// { -// Left(::Item), -// Right(::Item), -// } - -// impl Iterator for OrIter -// where -// T: AnElem + IntoIterator, -// U: Elems + IntoIterator, -// { -// type Item = OrIterItem; - -// fn next(&mut self) -> Option { -// match self { -// Self::Left(x) => x.next().map(|y| OrIterItem::Left(y)), -// Self::Right(x) => x.next().map(|y| OrIterItem::Right(y)), -// } -// } -// } - -// impl IntoIterator for Or -// where -// T: AnElem + IntoIterator, -// U: Elems + IntoIterator, -// { -// type Item = OrIterItem; -// type IntoIter = OrIter; - -// fn into_iter(self) -> Self::IntoIter { -// match self { -// Self::Left(x) => OrIter::Left(x.into_iter()), -// Self::Right(x) => OrIter::Right(x.into_iter()), -// } -// } -// } - - -// pub struct ResultOrIterError { -// result: Result, -// } - -// pub enum OrIterError { -// FromEmptyIter, - -// AnElemError(AnElemError), - -// MoreThanSingleton { -// // hd_elem: String, -// // other_elems: Vec, -// }, -// } - -// impl IntoIterator for ResultOrIterError -// where -// T: IntoIterator, -// { -// type Item = as IntoIterator>::Item; -// type IntoIter = as IntoIterator>::IntoIter; - -// fn into_iter(self) -> Self::IntoIter { -// match self.result { -// Ok(x) => Some(x).into_iter(), -// Err(_) => None.into_iter(), -// } -// } -// } - -// // impl fromiterator> for resultoritererror> -// // where -// // T: AnElem + IntoIterator + FromIterator<::Item>, -// // U: Elems + IntoIterator + FromIterator<::Item>, -// // { -// // fn from_iter(iter: V) -> Self -// // where -// // V: IntoIterator>, -// // { -// // // let mut iter_mut = iter.into_iter(); -// // // let opt_hd_elem = iter_mut.next(); -// // // let rest = iter_mut.next(); -// // // ResultOrIterError { -// // // result: match (opt_hd_elem, rest) { -// // // (None, _) => Err(OrIterError::FromEmptyIter), -// // // (Some(hd_elem), Some(other_elems)) => Err(OrIterError::MoreThanSingleton { -// // // // TODO: debug info -// // // // hd_elem: hd_elem, -// // // // other_elems: other_elems, -// // // }), -// // // (Some(OrIterItem::Left(hd_elem)), None) => -// // // match as AnElem>::from_elem(PhantomData, hd_elem) { -// // // Ok(x) => Ok(x), -// // // Err(e) => Err(OrIterError::AnElemError(e)), -// // // }, -// // // (Some(OrIterItem::Right(hd_elem)), None) => -// // // match as AnElem>::from_elem(PhantomData, hd_elem) { -// // // Ok(x) => Ok(x), -// // // Err(e) => Err(OrIterError::AnElemError(e)), -// // // }, -// // // }, -// // // } -// // } -// // } - -// // impl FromIterator> for OrIterFrom -// // where -// // T: AnElem + IntoIterator + FromIterator<::Item>, -// // U: Elems + IntoIterator + FromIterator<::Item>, -// // { -// // fn from_iter(iter: V) -> Self -// // where -// // V: IntoIterator>, -// // { -// // let mut iter_mut = iter.into_iter(); -// // let opt_hd_elem = iter_mut.next(); -// // let rest = iter_mut.next(); -// // match (opt_hd_elem, rest) { -// // (None, _) => OrIterFrom::Err(OrIterError::FromEmptyIter), -// // (Some(hd_elem), Some(other_elems)) => OrIterFrom::Err(OrIterError::MoreThanSingleton { -// // // TODO: debug info -// // // hd_elem: hd_elem, -// // // other_elems: other_elems, -// // }), -// // (Some(OrIterItem::Left(hd_elem)), None) => -// // match as AnElem>::from_elem(PhantomData, hd_elem) { -// // Ok(x) => OrIterFrom::Or(x), -// // Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), -// // }, -// // (Some(OrIterItem::Right(hd_elem)), None) => -// // match as AnElem>::from_elem(PhantomData, hd_elem) { -// // Ok(x) => OrIterFrom::Or(x), -// // Err(e) => OrIterFrom::Err(OrIterError::AnElemError(e)), -// // }, -// // } -// // } -// // } - - - -// // impl FromIterator<::Item> for Or -// // where -// // T: AnElem + IntoIterator + FromIterator<::Item>, -// // U: Elems + IntoIterator::Item> + FromIterator<::Item>, -// // { -// // fn from_iter(iter: V) -> Self -// // where -// // V: IntoIterator::Item>, -// // { -// // _ -// // } -// // } - - - - - - - - From 0e645c26d18889bf56f3ae4bfb04f59e38be6fd7 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Mar 2022 17:45:20 -0500 Subject: [PATCH 42/77] WIP: checkpoint broken mutable references --- src/types_scratch.rs | 613 ++++++++++++++++++++++++++++++------------- 1 file changed, 427 insertions(+), 186 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index ee9df43..bfe194a 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -5,48 +5,39 @@ use crate::types::{AnError, Nil}; use std::iter::{FromIterator}; use std::marker::PhantomData; use std::fmt::Debug; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use enumset::EnumSet; use generic_array::typenum::{U0, U2}; use generic_array::sequence::GenericSequence; use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; -// use typenum::marker_traits::Unsigned; use serde_json::{Map, Value}; // NEXT: // - Acheive parity between ElemList -> Elems/IList/IOList -// pub trait IList: Clone + IntoIterator { -// type Hd: AnElem; -// type N: ArrayLength; -// type Tl: IList; - -// fn is_empty(&self) -> bool; -// fn hd(&self) -> GenericArray; -// fn tl(&self) -> Self::Tl; -// fn cons>(self, x: GenericArray) -> Cons where Self: Sized; -// fn pop(x: PhantomData, stack: &mut Stack) -> Result; - - -// pub trait AnElem: Clone + std::fmt::Debug { -// fn elem_symbol(t: PhantomData) -> ElemSymbol; -// fn elem_symbol(t: PhantomData) -> EnumSet; - -// fn to_elem(self) -> Elem; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + array: GenericArray, +} -// fn from_elem(t: PhantomData, x: Elem) -> Result; +// impl AnElem for Singleton { +// fn elem_symbol(_t: PhantomData) -> EnumSet { ::elem_symbol(PhantomData) } +// fn to_elem(self) -> Elem { self.t.to_elem() } +// fn from_elem(_t: PhantomData, x: Elem) -> Result { ::from_elem(PhantomData, x).map(|y| { Singleton { t: y, } }) } // } - pub trait Elems: Clone + Debug { type Hd: AnElem; type N: ArrayLength; type Tl: Elems; - fn is_left(&self) -> bool; - fn left(s: PhantomData, x: GenericArray) -> Self; - fn right(s: PhantomData, x: Self::Tl) -> Self; + // fn left(s: PhantomData, x: GenericArray) -> Self; + // fn right(s: PhantomData, x: Self::Tl) -> Self; fn or) -> T, G: Fn(Self::Tl) -> T>(&self, f: F, g: G) -> T; // fn elem_symbols(t: PhantomData) -> EnumSet; @@ -54,32 +45,16 @@ pub trait Elems: Clone + Debug { // fn from_elems(t: PhantomData, x: &mut Stack) -> Result; } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Singleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - t: GenericArray, -} +pub trait IElems: Elems {} -// impl AnElem for Singleton { -// fn elem_symbol(_t: PhantomData) -> EnumSet { -// ::elem_symbol(PhantomData) -// } +pub trait IOElems<'a>: Elems { + fn or_return(&'a self, f: &mut F, g: &mut G) -> T + where + F: Fn(GenericArray, &'a mut Option) -> T, + G: Fn(Self::Tl) -> T; +} -// fn to_elem(self) -> Elem { -// self.t.to_elem() -// } -// fn from_elem(_t: PhantomData, x: Elem) -> Result { -// ::from_elem(PhantomData, x).map(|y| { -// Singleton { -// t: y, -// } -// }) -// } -// } impl Elems for Singleton where @@ -90,23 +65,20 @@ where type N = N; type Tl = Singleton; - fn is_left(&self) -> bool { true } - - fn left(_s: PhantomData, x: GenericArray) -> Self { - Singleton { - t: x, - } - } - - fn right(_s: PhantomData, x: Self::Tl) -> Self { - x - } - + // fn left(_s: PhantomData, x: GenericArray) -> Self { Singleton { t: x, } } + // fn right(_s: PhantomData, x: Self::Tl) -> Self { x } fn or) -> U, G: Fn(Self::Tl) -> U>(&self, f: F, _g: G) -> U { - f(self.t.clone()) + f(self.array.clone()) } } +impl IElems for Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{} + + #[derive(Clone, Debug, PartialEq, Eq)] pub enum Or @@ -119,34 +91,6 @@ where Right(U), } -// impl AnElem for Or { -// fn elem_symbol(_t: PhantomData) -> EnumSet { -// let t_set = ::elem_symbol(PhantomData); -// let u_set = ::elem_symbol(PhantomData); -// t_set.union(u_set) -// } - -// fn to_elem(self) -> Elem { -// match self { -// Self::Left(x) => x.to_elem(), -// Self::Right(x) => x.to_elem(), -// } -// } - -// fn from_elem(_t: PhantomData, x: Elem) -> Result { -// AnElem::from_elem(PhantomData::, x.clone()) -// .map(|y| Or::Left(y)) -// .or_else(|e_hd| { -// Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) -// .map_err(|e_tl| { -// AnElemError::PopOr { -// e_hd: Box::new(e_hd), -// e_tl: Box::new(e_tl), -// }}) -// }) -// } -// } - impl Elems for Or where T: AnElem, @@ -157,25 +101,129 @@ where type N = N; type Tl = U; - fn is_left(&self) -> bool { + // fn left(_s: PhantomData, x: GenericArray) -> Self { Self::Left(x) } + // fn right(_s: PhantomData, x: Self::Tl) -> Self { Self::Right(x) } + fn or) -> V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { match self { - Self::Left(_) => true, - Self::Right(_) => false, + Self::Left(x) => f(x.clone()), + Self::Right(x) => g(x.clone()), } } +} + +impl IElems for Or +where + T: AnElem, + N: ArrayLength + Debug, + U: IElems, +{} + + + +#[derive(Clone, Debug)] +pub struct ReturnSingleton<'a, T, N> +where + T: AnElem, + N: ArrayLength + Debug, +{ + singleton: Singleton, + returning: Arc>>, +} + +impl<'a, T, N> Elems for ReturnSingleton<'a, T, N> +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Hd = T; + type N = N; + type Tl = Singleton; - fn left(_s: PhantomData, x: GenericArray) -> Self { - Self::Left(x) + // fn left(_s: PhantomData, x: GenericArray) -> Self { Elems::left(PhantomData::>, x) } + // fn right(_s: PhantomData, x: Self::Tl) -> Self { Elems::left(PhantomData::>, x) } + fn or) -> U, G: Fn(Self::Tl) -> U>(&self, f: F, g: G) -> U { + self.singleton.or(f, g) } +} + +impl<'a, T, N> IOElems<'a> for ReturnSingleton<'a, T, N> +where + T: AnElem, + N: ArrayLength + Debug, +{ + fn or_return(&'a self, f: &mut F, _g: &mut G) -> U + where + F: Fn(GenericArray, &'a mut Option) -> U, + G: Fn(Self::Tl) -> U, + { + let mut lock = (*self.returning).try_lock(); + if let Ok(ref mut mutex) = lock { + f(self.singleton.array.clone(), mutex) + } else { + panic!("or_return ReturnSingleton: TODO") + } - fn right(_s: PhantomData, x: Self::Tl) -> Self { - Self::Right(x) + // f(self.singleton.array.clone(), *self.returning) } +} + + +#[derive(Clone, Debug)] +pub enum ReturnOr<'a, T, N, U> +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + Left { + array: GenericArray, + returning: Arc>>, + }, + Right(U), +} + +impl<'a, T, N, U> Elems for ReturnOr<'a, T, N, U> +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Hd = T; + type N = N; + type Tl = U; fn or) -> V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { match self { - Self::Left(x) => f(x.clone()), - Self::Right(x) => g(x.clone()), + Self::Left { array, .. } => f(array.clone()), + Self::Right(x) => g(*x), + } + } +} + +impl<'a, T, N, U> IOElems<'a> for ReturnOr<'a, T, N, U> +where + T: AnElem, + N: ArrayLength + Debug, + U: IOElems<'a, N = N> +{ + fn or_return(&'a self, f: &mut F, g: &mut G) -> V + where + F: Fn(GenericArray, &'a mut Option) -> V, + G: Fn(Self::Tl) -> V, + { + match self { + Self::Left { array, returning } => { + let mut lock = (*returning).try_lock(); + if let Ok(ref mut mutex) = lock { + f(array.clone(), mutex) + } else { + panic!("or_return: TODO") + } + + // let mut_returning = *returning.lock()?; + // f(array.clone(), mut_returning) + }, + Self::Right(x) => g(*x), } } } @@ -185,21 +233,24 @@ where + // + IntoIterator -pub trait IsList: Clone { +pub trait IsList: Debug { type Hd: Elems; type Tl: IsList; fn is_empty(&self) -> bool; fn hd(self) -> Self::Hd; fn tl(self) -> Self::Tl; - fn cons(self, x: T) -> Cons { + fn cons(self, x: T) -> Cons + where + Self: Sized, + { Cons { hd: x, tl: self, } } - // fn pop(x: PhantomData, stack: &mut Stack) -> Result; } @@ -213,7 +264,7 @@ impl IsList for Nil { fn hd(self) -> Self::Hd { Singleton { - t: GenericArray::generate(|_| ()), + array: GenericArray::generate(|_| ()), } } @@ -222,7 +273,7 @@ impl IsList for Nil { } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Cons { hd: T, tl: U, @@ -244,7 +295,7 @@ impl IsList for Cons { self.tl } - // // TODO: add better errors + // // add better errors // fn pop(_x: PhantomData, stack: &mut Stack) -> Result { // let hd_arr = stack.pop_generic_array(PhantomData, PhantomData)?; // Ok(Cons { @@ -265,145 +316,157 @@ pub trait IList: IsList { impl IList for Nil { } -impl IList for Cons { +impl IList for Cons +where + T: IElems, + U: IList, +{ } - -/// return_value is private, but get_return_value can be used to extract it -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Return { - for_instruction: PhantomData, - return_value: U, +pub trait IOList<'a>: IsList { + type Return: IOElems<'a>; } -impl Return { - pub fn get_return_value(self) -> U { - self.return_value - } +impl<'a, T, U> IOList<'a> for Cons +where + T: IElems, + U: IOList<'a>, +{ + type Return = U::Return; } -pub trait IOList: IsList { - type Return: AnElem; - - fn returning(self) -> Option Return<(), Self::Return>>>; -} - -#[derive(Clone)] -pub enum ConsOut +#[derive(Clone, Debug)] +pub struct ConsOut<'a, T, U> where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, - V: IList, + T: IOElems<'a>, + U: IList, { - Left { - return_fn: Arc Return<(), T>>, - hd: Singleton, - tl: V, - }, - Right { - hd: U, - tl: V, - } + a: PhantomData<&'a ()>, + cons: Cons, } -impl IsList for ConsOut +impl<'a, T, U> IsList for ConsOut<'a, T, U> where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, - V: IList, + T: IOElems<'a>, + U: IList { - type Hd = Or; - type Tl = V; + type Hd = T; + type Tl = U; fn is_empty(&self) -> bool { - false + self.cons.is_empty() } fn hd(self) -> Self::Hd { - match self { - Self::Left { hd, .. } => Or::Left(hd.t), - Self::Right { hd, .. } => Or::Right(hd), - } + self.cons.hd() } fn tl(self) -> Self::Tl { - match self { - Self::Left { tl, .. } => tl, - Self::Right { tl, .. } => tl, - } + self.cons.tl() } } -impl IOList for ConsOut +impl<'a, T, U> IOList<'a> for ConsOut<'a, T, U> where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, - V: IList, + T: IOElems<'a>, + U: IList, { type Return = T; - - fn returning(self) -> Option Return<(), T>>> { - match self { - Self::Left { return_fn, .. } => Some(return_fn), - Self::Right { .. } => None, - } - } } -impl IOList for Cons -where - T: Elems, - U: IOList, -{ - type Return = U::Return; - fn returning(self) -> Option Return<(), U::Return>>> { - self.tl.returning() - } -} - - - -pub trait IsInstructionT: std::fmt::Debug { - type In: IOList; +pub trait IsInstructionT<'a>: std::fmt::Debug { + type In: IOList<'a>; type Error: AnError; - fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; + // fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; + fn run(&'a self, x: Self::In) -> Result<(), Self::Error>; } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Concat { - t: PhantomData, +// struct Concat { +struct Concat<'a> { + a: PhantomData<&'a ()>, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct ConcatError {} impl AnError for ConcatError {} -//// bytes, string, array, object +// // bytes, string, array, object +// impl<'a> IsInstructionT<'a> for Concat<'a> { +// type In = ConsOut<'a, ReturnOr<'a, Vec, U2, ReturnSingleton<'a, Map, U2>>, Nil>; +// type Error = ConcatError; + +// // fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { +// fn run(&'a self, x: Self::In) -> Result<(), Self::Error> { +// let y = x.hd(); + +// y.or_return(&mut |z, arc_return| { +// let lhs = &z[0]; +// let rhs = &z[1]; +// *arc_return = Some(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); +// Ok(()) +// }, +// &mut |z| { +// z.or_return(&mut |w, arc_return| { +// let lhs = &w[0]; +// let rhs = &w[1]; +// *arc_return = Some(lhs.into_iter().chain(rhs.into_iter()).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); +// Ok(()) +// }, +// &mut |_w| { +// // let lhs = w.singleton.array[0]; +// // let rhs = w.singleton.array[1]; +// // **w.returning = Some(lhs.into_iter().chain(rhs.into_iter()).collect()); +// Ok(()) +// }) +// }) + +// // match y { +// // Left([z1, z2]) => concat(z1, z2), + +// // .. +// // } + +// // let lhs: Or, Singleton>> = x.hd()[0].clone(); +// // let rhs: Or, Singleton>> = x.hd()[1].clone(); +// // Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) +// } +// } + + +// // bytes, string, array, object // impl IsInstructionT for Concat<()> { -// type In = ConsOut, Singleton>>, U2, Nil>; +// type In = ConsOut, U2, Singleton, U2>>, U2, Singleton<(), U0>, Nil>; // type Error = ConcatError; // fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { -// let lhs: Or, Singleton>> = x.hd()[0].clone(); -// let rhs: Or, Singleton>> = x.hd()[1].clone(); -// Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) +// let y = x.hd(); +// // match y { +// // Left([z1, z2]) => concat(z1, z2), + +// // .. +// // } + +// // let lhs: Or, Singleton>> = x.hd()[0].clone(); +// // let rhs: Or, Singleton>> = x.hd()[1].clone(); +// // Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) // } // } + + + // impl::Item>> IsInstructionT for Concat { // type In = ConsOut; // type Error = ConcatError; @@ -415,3 +478,181 @@ impl AnError for ConcatError {} // } // } + +// #[derive(Clone)] +// pub enum ConsOut +// where +// T: AnElem, +// N: ArrayLength + Debug, +// U: Elems, +// V: IList, +// { +// Left { +// return_fn: Arc Return<(), T>>, +// hd: Singleton, +// tl: V, +// }, +// Right { +// hd: U, +// tl: V, +// } +// } + +// impl IsList for ConsOut +// where +// T: AnElem, +// N: ArrayLength + Debug, +// U: Elems, +// V: IList, +// { +// type Hd = Or; +// type Tl = V; + +// fn is_empty(&self) -> bool { +// false +// } + +// fn hd(self) -> Self::Hd { +// match self { +// Self::Left { hd, .. } => Or::Left(hd.t), +// Self::Right { hd, .. } => Or::Right(hd), +// } +// } + +// fn tl(self) -> Self::Tl { +// match self { +// Self::Left { tl, .. } => tl, +// Self::Right { tl, .. } => tl, +// } +// } +// } + +// impl IOList for ConsOut +// where +// T: AnElem, +// N: ArrayLength + Debug, +// U: Elems, +// V: IList, +// { +// type Return = T; + +// // fn returning(self) -> Option Return<(), T>>> { +// // match self { +// // Self::Left { return_fn, .. } => Some(return_fn), +// // Self::Right { .. } => None, +// // } +// // } +// } + +// impl IOList for Cons +// where +// T: Elems, +// U: IOList, +// { +// type Return = U::Return; + +// // fn returning(self) -> Option Return<(), U::Return>>> { +// // self.tl.returning() +// // } +// } + + + + + + + + + + +// /// return_value is private, but get_return_value can be used to extract it +// #[derive(Clone, Debug, PartialEq, Eq)] +// pub struct Return { +// for_instruction: PhantomData, +// return_value: U, +// } + +// impl Return { +// pub fn get_return_value(self) -> U { +// self.return_value +// } +// } + + + + + + + +// Cons>, Nil> + +// ( {U, T} ) + +// Cons>>, Nil> + +// ( {U, T} ) -> {U, T} + + +// forall x, .. z. IsIn {A, B, C} x, .. => [x, x, y, Bool, y] -> [x, Bool] + +// Or < Singleton +// ReturningOr< ReturningSingleton + + +// +// Instruction +// Instruction +// Instruction +// Instruction + + +// [A, B, C] +// Instruction +// [A, B, C] + + + +// Or> + +// Or<(), Singleton<()>> + +// Or> + +// IsNot + +// Dict> -> Empty + +// IsEq +// type IsEqBool: const bool; + + + + +// impl AnElem for Or { +// fn elem_symbol(_t: PhantomData) -> EnumSet { +// let t_set = ::elem_symbol(PhantomData); +// let u_set = ::elem_symbol(PhantomData); +// t_set.union(u_set) +// } + +// fn to_elem(self) -> Elem { +// match self { +// Self::Left(x) => x.to_elem(), +// Self::Right(x) => x.to_elem(), +// } +// } + +// fn from_elem(_t: PhantomData, x: Elem) -> Result { +// AnElem::from_elem(PhantomData::, x.clone()) +// .map(|y| Or::Left(y)) +// .or_else(|e_hd| { +// Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) +// .map_err(|e_tl| { +// AnElemError::PopOr { +// e_hd: Box::new(e_hd), +// e_tl: Box::new(e_tl), +// }}) +// }) +// } +// } + From d5981bf135d6e2885efdb7baf20ed6bf8ed6e729 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Mar 2022 18:34:24 -0500 Subject: [PATCH 43/77] fixed mutex and got concat IsInstructionT up to parity w/ IsInstruction --- src/types_scratch.rs | 354 +++++++++++++------------------------------ 1 file changed, 103 insertions(+), 251 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index bfe194a..6901c6b 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -13,9 +13,6 @@ use generic_array::sequence::GenericSequence; use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; use serde_json::{Map, Value}; -// NEXT: -// - Acheive parity between ElemList -> Elems/IList/IOList - #[derive(Clone, Debug, PartialEq, Eq)] pub struct Singleton where @@ -38,7 +35,7 @@ pub trait Elems: Clone + Debug { // fn left(s: PhantomData, x: GenericArray) -> Self; // fn right(s: PhantomData, x: Self::Tl) -> Self; - fn or) -> T, G: Fn(Self::Tl) -> T>(&self, f: F, g: G) -> T; + fn or) -> T, G: Fn(&Self::Tl) -> T>(&self, f: F, g: G) -> T; // fn elem_symbols(t: PhantomData) -> EnumSet; // fn to_elems(self) -> Elem; @@ -47,11 +44,37 @@ pub trait Elems: Clone + Debug { pub trait IElems: Elems {} -pub trait IOElems<'a>: Elems { - fn or_return(&'a self, f: &mut F, g: &mut G) -> T + +#[derive(Clone, Debug)] +pub struct Return { + return_value: Arc>>, +} + +impl Return { + pub fn returning(&self, return_value: T) { + let mut lock = (*self.return_value).try_lock(); + if let Ok(ref mut mutex) = lock { + **mutex = Some(return_value) + } else { + panic!("returning: TODO") + } + } + + pub fn returned(&self) -> Option { + let mut lock = (*self.return_value).try_lock(); + if let Ok(ref mut mutex) = lock { + (**mutex).clone() + } else { + panic!("returned: TODO") + } + } +} + +pub trait IOElems: Elems { + fn or_return(&self, f: F, g: G) -> T where - F: Fn(GenericArray, &'a mut Option) -> T, - G: Fn(Self::Tl) -> T; + F: Fn(&GenericArray, &Return) -> T, + G: Fn(&Self::Tl) -> T; } @@ -67,8 +90,8 @@ where // fn left(_s: PhantomData, x: GenericArray) -> Self { Singleton { t: x, } } // fn right(_s: PhantomData, x: Self::Tl) -> Self { x } - fn or) -> U, G: Fn(Self::Tl) -> U>(&self, f: F, _g: G) -> U { - f(self.array.clone()) + fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, _g: G) -> U { + f(&self.array) } } @@ -103,10 +126,10 @@ where // fn left(_s: PhantomData, x: GenericArray) -> Self { Self::Left(x) } // fn right(_s: PhantomData, x: Self::Tl) -> Self { Self::Right(x) } - fn or) -> V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { + fn or) -> V, G: Fn(&Self::Tl) -> V>(&self, f: F, g: G) -> V { match self { - Self::Left(x) => f(x.clone()), - Self::Right(x) => g(x.clone()), + Self::Left(x) => f(x), + Self::Right(x) => g(x), } } } @@ -121,16 +144,16 @@ where #[derive(Clone, Debug)] -pub struct ReturnSingleton<'a, T, N> +pub struct ReturnSingleton where T: AnElem, N: ArrayLength + Debug, { singleton: Singleton, - returning: Arc>>, + returning: Return, } -impl<'a, T, N> Elems for ReturnSingleton<'a, T, N> +impl Elems for ReturnSingleton where T: AnElem, N: ArrayLength + Debug, @@ -141,35 +164,28 @@ where // fn left(_s: PhantomData, x: GenericArray) -> Self { Elems::left(PhantomData::>, x) } // fn right(_s: PhantomData, x: Self::Tl) -> Self { Elems::left(PhantomData::>, x) } - fn or) -> U, G: Fn(Self::Tl) -> U>(&self, f: F, g: G) -> U { + fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, g: G) -> U { self.singleton.or(f, g) } } -impl<'a, T, N> IOElems<'a> for ReturnSingleton<'a, T, N> +impl IOElems for ReturnSingleton where T: AnElem, N: ArrayLength + Debug, { - fn or_return(&'a self, f: &mut F, _g: &mut G) -> U + fn or_return(&self, f: F, _g: G) -> U where - F: Fn(GenericArray, &'a mut Option) -> U, - G: Fn(Self::Tl) -> U, + F: Fn(&GenericArray, &Return) -> U, + G: Fn(&Self::Tl) -> U, { - let mut lock = (*self.returning).try_lock(); - if let Ok(ref mut mutex) = lock { - f(self.singleton.array.clone(), mutex) - } else { - panic!("or_return ReturnSingleton: TODO") - } - - // f(self.singleton.array.clone(), *self.returning) + f(&self.singleton.array, &self.returning) } } #[derive(Clone, Debug)] -pub enum ReturnOr<'a, T, N, U> +pub enum ReturnOr where T: AnElem, N: ArrayLength + Debug, @@ -177,12 +193,12 @@ where { Left { array: GenericArray, - returning: Arc>>, + returning: Return, }, Right(U), } -impl<'a, T, N, U> Elems for ReturnOr<'a, T, N, U> +impl Elems for ReturnOr where T: AnElem, N: ArrayLength + Debug, @@ -192,38 +208,30 @@ where type N = N; type Tl = U; - fn or) -> V, G: Fn(Self::Tl) -> V>(&self, f: F, g: G) -> V { + fn or) -> V, G: Fn(&Self::Tl) -> V>(&self, f: F, g: G) -> V { match self { - Self::Left { array, .. } => f(array.clone()), - Self::Right(x) => g(*x), + Self::Left { array, .. } => f(array), + Self::Right(x) => g(x), } } } -impl<'a, T, N, U> IOElems<'a> for ReturnOr<'a, T, N, U> +impl IOElems for ReturnOr where T: AnElem, N: ArrayLength + Debug, - U: IOElems<'a, N = N> + U: IOElems { - fn or_return(&'a self, f: &mut F, g: &mut G) -> V + fn or_return(&self, f: F, g: G) -> V where - F: Fn(GenericArray, &'a mut Option) -> V, - G: Fn(Self::Tl) -> V, + F: Fn(&GenericArray, &Return) -> V, + G: Fn(&Self::Tl) -> V, { match self { Self::Left { array, returning } => { - let mut lock = (*returning).try_lock(); - if let Ok(ref mut mutex) = lock { - f(array.clone(), mutex) - } else { - panic!("or_return: TODO") - } - - // let mut_returning = *returning.lock()?; - // f(array.clone(), mut_returning) + f(array, returning) }, - Self::Right(x) => g(*x), + Self::Right(x) => g(x), } } } @@ -324,32 +332,31 @@ where } -pub trait IOList<'a>: IsList { - type Return: IOElems<'a>; +pub trait IOList: IsList { + type Return: IOElems; } -impl<'a, T, U> IOList<'a> for Cons +impl IOList for Cons where T: IElems, - U: IOList<'a>, + U: IOList, { type Return = U::Return; } #[derive(Clone, Debug)] -pub struct ConsOut<'a, T, U> +pub struct ConsOut where - T: IOElems<'a>, + T: IOElems, U: IList, { - a: PhantomData<&'a ()>, cons: Cons, } -impl<'a, T, U> IsList for ConsOut<'a, T, U> +impl IsList for ConsOut where - T: IOElems<'a>, + T: IOElems, U: IList { type Hd = T; @@ -368,9 +375,9 @@ where } } -impl<'a, T, U> IOList<'a> for ConsOut<'a, T, U> +impl IOList for ConsOut where - T: IOElems<'a>, + T: IOElems, U: IList, { type Return = T; @@ -382,207 +389,52 @@ where -pub trait IsInstructionT<'a>: std::fmt::Debug { - type In: IOList<'a>; +pub trait IsInstructionT: std::fmt::Debug { + type In: IOList; type Error: AnError; - // fn run(&self, x: Self::In) -> Result<::Return, Self::Error>; - fn run(&'a self, x: Self::In) -> Result<(), Self::Error>; + fn run(&self, x: Self::In) -> Result<(), Self::Error>; } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -// struct Concat { -struct Concat<'a> { - a: PhantomData<&'a ()>, +struct Concat { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct ConcatError {} impl AnError for ConcatError {} - -// // bytes, string, array, object -// impl<'a> IsInstructionT<'a> for Concat<'a> { -// type In = ConsOut<'a, ReturnOr<'a, Vec, U2, ReturnSingleton<'a, Map, U2>>, Nil>; -// type Error = ConcatError; - -// // fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { -// fn run(&'a self, x: Self::In) -> Result<(), Self::Error> { -// let y = x.hd(); - -// y.or_return(&mut |z, arc_return| { -// let lhs = &z[0]; -// let rhs = &z[1]; -// *arc_return = Some(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); -// Ok(()) -// }, -// &mut |z| { -// z.or_return(&mut |w, arc_return| { -// let lhs = &w[0]; -// let rhs = &w[1]; -// *arc_return = Some(lhs.into_iter().chain(rhs.into_iter()).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); -// Ok(()) -// }, -// &mut |_w| { -// // let lhs = w.singleton.array[0]; -// // let rhs = w.singleton.array[1]; -// // **w.returning = Some(lhs.into_iter().chain(rhs.into_iter()).collect()); -// Ok(()) -// }) -// }) - -// // match y { -// // Left([z1, z2]) => concat(z1, z2), - -// // .. -// // } - -// // let lhs: Or, Singleton>> = x.hd()[0].clone(); -// // let rhs: Or, Singleton>> = x.hd()[1].clone(); -// // Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) -// } -// } - - -// // bytes, string, array, object -// impl IsInstructionT for Concat<()> { -// type In = ConsOut, U2, Singleton, U2>>, U2, Singleton<(), U0>, Nil>; -// type Error = ConcatError; - -// fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { -// let y = x.hd(); -// // match y { -// // Left([z1, z2]) => concat(z1, z2), - -// // .. -// // } - -// // let lhs: Or, Singleton>> = x.hd()[0].clone(); -// // let rhs: Or, Singleton>> = x.hd()[1].clone(); -// // Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) -// } -// } - - - - - -// impl::Item>> IsInstructionT for Concat { -// type In = ConsOut; -// type Error = ConcatError; - -// fn run(&self, x: Self::In) -> Result<::Return, Self::Error> { -// let lhs = x.hd()[0].clone(); -// let rhs = x.hd()[1].clone(); -// Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) -// } -// } - - -// #[derive(Clone)] -// pub enum ConsOut -// where -// T: AnElem, -// N: ArrayLength + Debug, -// U: Elems, -// V: IList, -// { -// Left { -// return_fn: Arc Return<(), T>>, -// hd: Singleton, -// tl: V, -// }, -// Right { -// hd: U, -// tl: V, -// } -// } - -// impl IsList for ConsOut -// where -// T: AnElem, -// N: ArrayLength + Debug, -// U: Elems, -// V: IList, -// { -// type Hd = Or; -// type Tl = V; - -// fn is_empty(&self) -> bool { -// false -// } - -// fn hd(self) -> Self::Hd { -// match self { -// Self::Left { hd, .. } => Or::Left(hd.t), -// Self::Right { hd, .. } => Or::Right(hd), -// } -// } - -// fn tl(self) -> Self::Tl { -// match self { -// Self::Left { tl, .. } => tl, -// Self::Right { tl, .. } => tl, -// } -// } -// } - -// impl IOList for ConsOut -// where -// T: AnElem, -// N: ArrayLength + Debug, -// U: Elems, -// V: IList, -// { -// type Return = T; - -// // fn returning(self) -> Option Return<(), T>>> { -// // match self { -// // Self::Left { return_fn, .. } => Some(return_fn), -// // Self::Right { .. } => None, -// // } -// // } -// } - -// impl IOList for Cons -// where -// T: Elems, -// U: IOList, -// { -// type Return = U::Return; - -// // fn returning(self) -> Option Return<(), U::Return>>> { -// // self.tl.returning() -// // } -// } - - - - - - - - - - -// /// return_value is private, but get_return_value can be used to extract it -// #[derive(Clone, Debug, PartialEq, Eq)] -// pub struct Return { -// for_instruction: PhantomData, -// return_value: U, -// } - -// impl Return { -// pub fn get_return_value(self) -> U { -// self.return_value -// } -// } - - - - - - +// bytes, array, object +impl IsInstructionT for Concat { + type In = ConsOut, U2, + ReturnOr, U2, + ReturnSingleton, U2>>>, Nil>; + type Error = ConcatError; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let y = x.hd(); + match y { + ReturnOr::Left { array, returning } => { + let lhs = &array[0]; + let rhs = &array[1]; + returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); + Ok(()) + }, + ReturnOr::Right(ReturnOr::Left { array, returning }) => { + let lhs = &array[0]; + let rhs = &array[1]; + returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); + Ok(()) + }, + ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton, returning })) => { + let lhs = &singleton.array[0]; + let rhs = &singleton.array[1]; + returning.returning(lhs.into_iter().chain(rhs.into_iter()).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); + Ok(()) + }, + } + } +} // Cons>, Nil> From f44126bec048f50c2dc38dd223f5039fe871f058 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 7 Mar 2022 18:56:02 -0500 Subject: [PATCH 44/77] IsInstructionT up to parity with InstructionT --- src/types.rs | 1 - src/types_scratch.rs | 339 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 337 insertions(+), 3 deletions(-) diff --git a/src/types.rs b/src/types.rs index 5265946..f199303 100644 --- a/src/types.rs +++ b/src/types.rs @@ -297,7 +297,6 @@ impl IsInstruction for HashSha256 { } } - #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Concat { t: PhantomData, diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 6901c6b..0c58537 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,6 +1,6 @@ use crate::elem::{Elem, AnElem, AnElemError, ElemSymbol}; use crate::stack::{Stack, StackError}; -use crate::types::{AnError, Nil}; +use crate::types::{Empty, AnError, Nil}; use std::iter::{FromIterator}; use std::marker::PhantomData; @@ -8,7 +8,7 @@ use std::fmt::Debug; use std::sync::{Arc, Mutex}; use enumset::EnumSet; -use generic_array::typenum::{U0, U2}; +use generic_array::typenum::{U0, U1, U2}; use generic_array::sequence::GenericSequence; use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; use serde_json::{Map, Value}; @@ -397,6 +397,23 @@ pub trait IsInstructionT: std::fmt::Debug { } + +// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +// pub enum Instruction { +// Restack(Restack), +// +// CheckLe, +// CheckLt, +// CheckEq, +// Slice, +// Index, +// ToJson, +// UnpackJson(ElemSymbol), +// StringToBytes, +// } + + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Concat { } @@ -435,6 +452,324 @@ impl IsInstructionT for Concat { } } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct AssertTrue {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct AssertTrueError {} +impl AnError for AssertTrueError {} + +impl IsInstructionT for AssertTrue { + type In = ConsOut, Nil>; + type Error = AssertTrueError; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let array = x.clone().hd().singleton.array; + let returning = x.hd().returning; + if array[0] { + returning.returning(true); + Ok(()) + } else { + Err(AssertTrueError {}) + } + } +} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Push { + push: T, +} + +impl IsInstructionT for Push { + type In = ConsOut, Nil>; + type Error = Empty; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + x.hd().returning.returning(self.push.clone()); + Ok(()) + } +} + + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct HashSha256 {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct HashSha256Error {} +impl AnError for HashSha256Error {} + +impl IsInstructionT for HashSha256 { + type In = ConsOut, U1>, Nil>; + type Error = Empty; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let array = x.clone().hd().singleton.array; + let returning = x.hd().returning; + returning.returning(super::sha256(&array[0])); + Ok(()) + } +} + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct Slice {} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct SliceError {} +//impl AnError for SliceError {} + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct Index {} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct IndexError {} +//impl AnError for IndexError {} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Lookup {} +#[derive(Clone, Debug, PartialEq, Eq)] +struct LookupError { + key: String, + map: Map, +} +impl AnError for LookupError {} + +impl IsInstructionT for Lookup { + type In = ConsOut, Cons, Cons, U1>, Nil>>>; + type Error = LookupError; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let key = &x.clone().tl().hd().array[0]; + let map = &x.tl().tl().hd().array[0]; + returning.returning(map.get(key) + .ok_or_else(|| LookupError { + key: key.clone(), + map: map.clone(), + })?.clone()); + Ok(()) + } +} + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct UnpackJson { +// t: PhantomData, +//} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct UnpackJsonError {} +//impl AnError for UnpackJsonError {} + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct StringToBytes {} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct StringToBytesError {} +//impl AnError for StringToBytesError {} + + + +//// TODO: POLYMORPHIC W/ ANY: PERHAPS Elem: AnElem ?? +//// +//// ideas: +//// 1. use macros when it's a trait +//// 2. gradual typing: allow Elem to be AnElem + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct CheckLe {} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct CheckLeError {} +//impl AnError for CheckLeError {} + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct CheckLt {} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct CheckLtError {} +//impl AnError for CheckLtError {} + + +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct CheckEq {} +//#[derive(Clone, Copy, Debug, PartialEq, Eq)] +//struct CheckEqError {} +//impl AnError for CheckEqError {} + + + + + + // pub fn check_le(self, other: Self) -> Result { + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckLeIncomparableTypes { + // lhs: self.symbol_str(), + // rhs: other.symbol_str() })? { + // cmp::Ordering::Less => true, + // cmp::Ordering::Equal => true, + // cmp::Ordering::Greater => false, + // }; + // Ok(Self::Bool(result)) + // } + + // pub fn check_lt(self, other: Self) -> Result { + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckLtIncomparableTypes { + // lhs: self.symbol_str(), + // rhs: other.symbol_str() })? { + // cmp::Ordering::Less => true, + // _ => false, + // }; + // Ok(Self::Bool(result)) + // } + + // pub fn check_eq(self, other: Self) -> Result { + // let result = match self.partial_cmp(&other) + // .ok_or_else(|| ElemError::CheckEqIncomparableTypes { + // lhs: self.symbol_str(), + // rhs: other.symbol_str() })? { + // cmp::Ordering::Equal => true, + // _ => false, + // }; + // Ok(Self::Bool(result)) + // } + + // fn slice_generic::Item>>(offset: Number, + // length: Number, + // iterable: T, + // elem_symbol: ElemSymbol) -> + // Result { + // let u_offset = offset.as_u64() + // .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) + // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + // let u_length = length.as_u64() + // .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) + // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; + // let u_offset_plus_length = u_offset.checked_add(u_length) + // .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; + // if iterable.clone().into_iter().count() < u_offset_plus_length { + // Err(ElemError::SliceTooShort { + // offset: u_offset, + // length: u_length, + // iterable: From::from(elem_symbol), + // }) + // } else { + // Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) + // } + // } + + // pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { + // match (maybe_offset, maybe_length, maybe_iterable) { + // (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => + // Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), + // (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { + // let iterator_vec = Vec::from(iterator.clone()); + // Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) + // .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) + // }, + // (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => + // Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), + // (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => + // Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), + // (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { + // Err(ElemError::SliceUnsupportedTypes { + // maybe_not_offset: maybe_not_offset.symbol_str(), + // maybe_not_length: maybe_not_length.symbol_str(), + // maybe_not_iterable: maybe_not_iterable.symbol_str(), + // }) + // } + // } + // } + + // fn index_generic::Item>>(index: Number, + // iterable: T, + // elem_symbol: ElemSymbol) -> + // Result<::Item, ElemError> { + // let u_index: usize = index.as_u64() + // .ok_or_else(|| ElemError::IndexNotU64(index.clone())) + // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; + // if iterable.clone().into_iter().count() <= u_index { + // return Err(ElemError::IndexTooShort { + // index: u_index, + // iterable: From::from(elem_symbol), + // }) + // } else { + // match iterable.into_iter().skip(u_index).next() { + // None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), + // Some(x) => Ok(x), + // } + // } + // } + + // pub fn index(self, maybe_iterable: Self) -> Result { + // match (self, maybe_iterable) { + // // (Self::Number(index), Self::Bytes(iterator)) => + // // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), + // (Self::Number(index), Self::Array(iterator)) => + // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), + // (Self::Number(index), Self::Object(iterator)) => + // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), + // (maybe_not_index, maybe_not_iterable) => { + // Err(ElemError::IndexUnsupportedTypes { + // maybe_not_index: maybe_not_index.symbol_str(), + // maybe_not_iterable: maybe_not_iterable.symbol_str(), + // }) + // } + // } + // } + + // pub fn to_json(self) -> Result { + // Ok(Self::Json(serde_json::to_value(self)?)) + // } + + // pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { + // match (self, elem_symbol) { + // (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), + // (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), + // (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), + // (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), + // (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), + // (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), + // (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { + // json: json, + // elem_symbol: From::from(elem_symbol), + // }), + // (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { + // non_json: non_json.symbol_str(), + // elem_symbol: From::from(elem_symbol), + // }), + // } + // } + + // pub fn string_to_bytes(self) -> Result { + // match self { + // Self::String(x) => Ok(Self::Bytes(x.into_bytes())), + // other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), + // } + // } + + + + + + + + + + + + + + + + + + + + // Cons>, Nil> From 3e9c1745e3687bf3803995184d567f8f8c2d366e Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Mar 2022 14:55:55 -0500 Subject: [PATCH 45/77] add alias/handler for untyped inputs, use for le/lt/eq --- src/types_scratch.rs | 257 ++++++++++++++++++++++++++++++------------- 1 file changed, 179 insertions(+), 78 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 0c58537..0850776 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -2,6 +2,7 @@ use crate::elem::{Elem, AnElem, AnElemError, ElemSymbol}; use crate::stack::{Stack, StackError}; use crate::types::{Empty, AnError, Nil}; +use std::cmp; use std::iter::{FromIterator}; use std::marker::PhantomData; use std::fmt::Debug; @@ -10,8 +11,15 @@ use std::sync::{Arc, Mutex}; use enumset::EnumSet; use generic_array::typenum::{U0, U1, U2}; use generic_array::sequence::GenericSequence; +use generic_array::functional::FunctionalSequence; use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; -use serde_json::{Map, Value}; +use serde_json::{Map, Number, Value}; + +// NEXT: +// - finish migrating instruction implementations from elem to IsInstructionT +// - migrate pop-stack from IsInstruction +// - delete old IsInstruction +// - add typing info as with pop-stack #[derive(Clone, Debug, PartialEq, Eq)] pub struct Singleton @@ -51,6 +59,7 @@ pub struct Return { } impl Return { + // TODO: throw error if try_lock fails pub fn returning(&self, return_value: T) { let mut lock = (*self.return_value).try_lock(); if let Ok(ref mut mutex) = lock { @@ -60,6 +69,7 @@ impl Return { } } + // TODO: throw error if try_lock fails pub fn returned(&self) -> Option { let mut lock = (*self.return_value).try_lock(); if let Ok(ref mut mutex) = lock { @@ -141,6 +151,60 @@ where U: IElems, {} +// TODO: AnElem: &self -> AllElems +type AllElems = + Or<(), N, + Or, N, + Or, N, + Or, N, + Singleton>>>>>>>; + +fn all_elems_untyped(x: &AllElems) -> GenericArray +where + N: Debug + + ArrayLength<()> + + ArrayLength + + ArrayLength + + ArrayLength> + + ArrayLength + + ArrayLength> + + ArrayLength> + + ArrayLength + + ArrayLength, +{ + match x { + Or::Left(array) => { + array.map(|_x| Elem::Unit) + }, + Or::Right(Or::Left(array)) => { + array.map(|&x| Elem::Bool(x)) + }, + Or::Right(Or::Right(Or::Left(array))) => { + array.map(|x| Elem::Number(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Left(array)))) => { + array.map(|x| Elem::Bytes(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))) => { + array.map(|x| Elem::String(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array)))))) => { + array.map(|x| Elem::Array(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))))) => { + array.map(|x| Elem::Object(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Singleton { array }))))))) => { + array.map(|x| Elem::Json(x.clone())) + }, + } +} + + + #[derive(Clone, Debug)] @@ -435,21 +499,19 @@ impl IsInstructionT for Concat { let lhs = &array[0]; let rhs = &array[1]; returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); - Ok(()) }, ReturnOr::Right(ReturnOr::Left { array, returning }) => { let lhs = &array[0]; let rhs = &array[1]; returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); - Ok(()) }, ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton, returning })) => { let lhs = &singleton.array[0]; let rhs = &singleton.array[1]; returning.returning(lhs.into_iter().chain(rhs.into_iter()).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); - Ok(()) }, } + Ok(()) } } @@ -553,87 +615,133 @@ impl IsInstructionT for Lookup { } -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct UnpackJson { -// t: PhantomData, -//} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct UnpackJsonError {} -//impl AnError for UnpackJsonError {} - - -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct StringToBytes {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct StringToBytesError {} -//impl AnError for StringToBytesError {} - - +// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +// struct UnpackJson { +// t: PhantomData, +// } +// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +// struct UnpackJsonError {} +// impl AnError for UnpackJsonError {} -//// TODO: POLYMORPHIC W/ ANY: PERHAPS Elem: AnElem ?? -//// -//// ideas: -//// 1. use macros when it's a trait -//// 2. gradual typing: allow Elem to be AnElem +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct StringToBytes {} +// #[derive(Clone, Copy, Debug, PartialEq, Eq)] +// struct StringToBytesError {} +// impl AnError for StringToBytesError {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct CheckLe {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct CheckLeError {} -//impl AnError for CheckLeError {} +impl IsInstructionT for StringToBytes { + type In = ConsOut, U0>, Cons, Nil>>; + type Error = Empty; + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let in_str = &x.clone().tl().hd().array[0]; + returning.returning(in_str.clone().into_bytes()); + Ok(()) + } +} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct CheckLt {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct CheckLtError {} -//impl AnError for CheckLtError {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckLe {} +#[derive(Clone, Debug, PartialEq, Eq)] +struct CheckLeError { + lhs: Elem, + rhs: Elem, +} +impl AnError for CheckLeError {} +impl IsInstructionT for CheckLe { + type In = ConsOut, Cons, Nil>>; + type Error = CheckLeError; -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct CheckEq {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct CheckEqError {} -//impl AnError for CheckEqError {} + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| CheckLeError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Less => true, + cmp::Ordering::Equal => true, + cmp::Ordering::Greater => false, + }; + returning.returning(result); + Ok(()) + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckLt {} +#[derive(Clone, Debug, PartialEq, Eq)] +struct CheckLtError { + lhs: Elem, + rhs: Elem, +} +impl AnError for CheckLtError {} +impl IsInstructionT for CheckLt { + type In = ConsOut, Cons, Nil>>; + type Error = CheckLtError; + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| CheckLtError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Less => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} - // pub fn check_le(self, other: Self) -> Result { - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckLeIncomparableTypes { - // lhs: self.symbol_str(), - // rhs: other.symbol_str() })? { - // cmp::Ordering::Less => true, - // cmp::Ordering::Equal => true, - // cmp::Ordering::Greater => false, - // }; - // Ok(Self::Bool(result)) - // } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct CheckEq {} +#[derive(Clone, Debug, PartialEq, Eq)] +struct CheckEqError { + lhs: Elem, + rhs: Elem, +} +impl AnError for CheckEqError {} - // pub fn check_lt(self, other: Self) -> Result { - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckLtIncomparableTypes { - // lhs: self.symbol_str(), - // rhs: other.symbol_str() })? { - // cmp::Ordering::Less => true, - // _ => false, - // }; - // Ok(Self::Bool(result)) - // } +impl IsInstructionT for CheckEq { + type In = ConsOut, Cons, Nil>>; + type Error = CheckEqError; - // pub fn check_eq(self, other: Self) -> Result { - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckEqIncomparableTypes { - // lhs: self.symbol_str(), - // rhs: other.symbol_str() })? { - // cmp::Ordering::Equal => true, - // _ => false, - // }; - // Ok(Self::Bool(result)) - // } + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| CheckEqError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Equal => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} // fn slice_generic::Item>>(offset: Number, @@ -744,13 +852,6 @@ impl IsInstructionT for Lookup { // } // } - // pub fn string_to_bytes(self) -> Result { - // match self { - // Self::String(x) => Ok(Self::Bytes(x.into_bytes())), - // other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), - // } - // } - From 18692687970ac5ad92fbac6594b6698cc27afab9 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 8 Mar 2022 18:38:49 -0500 Subject: [PATCH 46/77] added InstructionT impl for all but Restack --- src/types_scratch.rs | 430 +++++++++++++++++++++++++++---------------- 1 file changed, 274 insertions(+), 156 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 0850776..971cef5 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -3,10 +3,12 @@ use crate::stack::{Stack, StackError}; use crate::types::{Empty, AnError, Nil}; use std::cmp; -use std::iter::{FromIterator}; +use std::convert::TryFrom; +use std::iter::FromIterator; use std::marker::PhantomData; use std::fmt::Debug; use std::sync::{Arc, Mutex}; +use std::string::FromUtf8Error; use enumset::EnumSet; use generic_array::typenum::{U0, U1, U2}; @@ -14,6 +16,7 @@ use generic_array::sequence::GenericSequence; use generic_array::functional::FunctionalSequence; use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; use serde_json::{Map, Number, Value}; +use thiserror::Error; // NEXT: // - finish migrating instruction implementations from elem to IsInstructionT @@ -465,29 +468,26 @@ pub trait IsInstructionT: std::fmt::Debug { // #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] // pub enum Instruction { // Restack(Restack), -// -// CheckLe, -// CheckLt, -// CheckEq, -// Slice, -// Index, -// ToJson, -// UnpackJson(ElemSymbol), -// StringToBytes, // } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Concat { +pub struct Concat { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct ConcatError {} +pub struct ConcatError {} impl AnError for ConcatError {} +// TODO: add string! +// (Self::String(x), Self::String(y)) => { +// Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) +// .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) +// }, +// // bytes, array, object impl IsInstructionT for Concat { - type In = ConsOut, U2, + type In = ConsOut, U2, ReturnOr, U2, ReturnSingleton, U2>>>, Nil>; type Error = ConcatError; @@ -516,9 +516,9 @@ impl IsInstructionT for Concat { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct AssertTrue {} +pub struct AssertTrue {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct AssertTrueError {} +pub struct AssertTrueError {} impl AnError for AssertTrueError {} impl IsInstructionT for AssertTrue { @@ -539,7 +539,7 @@ impl IsInstructionT for AssertTrue { #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Push { +pub struct Push { push: T, } @@ -556,9 +556,9 @@ impl IsInstructionT for Push { #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct HashSha256 {} +pub struct HashSha256 {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct HashSha256Error {} +pub struct HashSha256Error {} impl AnError for HashSha256Error {} impl IsInstructionT for HashSha256 { @@ -574,24 +574,200 @@ impl IsInstructionT for HashSha256 { } -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct Slice {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct SliceError {} -//impl AnError for SliceError {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Slice {} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SliceError { + OffsetNotU64(Number), + + LengthNotU64(Number), + + Overflow { + offset: Number, + length: Number, + }, + + TooShort { + offset: usize, + length: usize, + iterable: String, + }, + + FromUtf8Error(FromUtf8Error), +} + +impl From for SliceError { + fn from(error: FromUtf8Error) -> Self { + Self::FromUtf8Error(error) + } +} + +impl AnError for SliceError {} +// bytes, string, array, object +impl IsInstructionT for Slice { + type In = ConsOut, U1, + ReturnOr, U1, + ReturnSingleton, U1>>>>, + Cons, Nil>>; + type Error = SliceError; -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct Index {} -//#[derive(Clone, Copy, Debug, PartialEq, Eq)] -//struct IndexError {} -//impl AnError for IndexError {} + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let y = x.clone().hd(); + let offset_length = x.clone().tl().hd().array; + let offset = &offset_length[0]; + let length = &offset_length[1]; + let u_offset = offset.as_u64() + .ok_or_else(|| SliceError::OffsetNotU64(offset.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| SliceError::Overflow { offset: offset.clone(), length: length.clone() }))?; + let u_length = length.as_u64() + .ok_or_else(|| SliceError::LengthNotU64(length.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| SliceError::Overflow { offset: offset.clone(), length: length.clone() }))?; + let u_offset_plus_length = u_offset.checked_add(u_length) + .ok_or_else(|| SliceError::Overflow { offset: offset.clone(), length: length.clone() })?; + match y.clone() { + ReturnOr::Left { array, returning } => { + let iterable = &array[0]; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(()) + } else { + returning.returning(iterable.into_iter().skip(u_offset).take(u_length).copied().collect()); + Ok(()) + } + }, + ReturnOr::Right(ReturnOr::Left { array, returning }) => { + let iterable = &array[0]; + if iterable.len() < u_offset_plus_length { + Err(()) + } else { + returning.returning(String::from_utf8(Vec::from(iterable.clone()).into_iter().skip(u_offset).take(u_length).collect())?); + Ok(()) + } + }, + ReturnOr::Right(ReturnOr::Right(ReturnOr::Left { array, returning })) => { + let iterable = &array[0]; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(()) + } else { + returning.returning(iterable.into_iter().skip(u_offset).take(u_length).cloned().collect()); + Ok(()) + } + }, + ReturnOr::Right(ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton: Singleton { array }, returning }))) => { + let iterable = &array[0]; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(()) + } else { + returning.returning(iterable.into_iter().skip(u_offset).take(u_length).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); + Ok(()) + } + }, + }.map_err(|_e| { + SliceError::TooShort { + offset: u_offset, + length: u_length, + // TODO: better error + iterable: format!("{:?}", y), + } + }) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Lookup {} +pub struct Index {} +#[derive(Clone, Debug, PartialEq, Eq, Error)] +pub enum IndexError { + #[error("Index: index not valid u64: {0:?}")] + IndexNotU64(Number), + + #[error("Index: index not valid usize: {0:?}")] + Overflow(Number), + + #[error("Index: iterable: {iterable:?}\nis too short for index: {index:?}")] + TooShort { + index: usize, + iterable: String, + }, +} +impl AnError for IndexError {} + +// bytes, array, object +impl IsInstructionT for Index { + type In = ConsOut, + Cons, U2, + Singleton, U2>>, + Cons, Nil>>>; + type Error = IndexError; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = x.clone().tl().hd(); + let index = &x.clone().tl().tl().hd().array[0]; + let u_index = index.as_u64() + .ok_or_else(|| IndexError::IndexNotU64(index.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| IndexError::Overflow(index.clone())))?; + let result = match y.clone() { + Or::Left(array) => { + array[0] + .clone() + .into_iter() + .skip(u_index) + .next() + }, + Or::Right(Singleton { array }) => { + array[0] + .clone() + .into_iter() + .skip(u_index) + .next() + .map(|(_x, y)| y) + }, + }.ok_or_else(|| { + IndexError::TooShort { + index: u_index, + // TODO: better error + iterable: format!("{:?}", y), + } + })?; + returning.returning(result); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ToJson {} +#[derive(Clone, Debug)] +pub struct ToJsonError { + input: Elem, + error: Arc, +} +impl AnError for ToJsonError {} + +impl IsInstructionT for ToJson { + type In = ConsOut, Cons, Nil>>; + type Error = ToJsonError; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let z = array[0].clone(); + returning.returning(serde_json::to_value(z.clone()) + .map_err(move |e| ToJsonError { + input: z, + error: Arc::new(e), + })?); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Lookup {} #[derive(Clone, Debug, PartialEq, Eq)] -struct LookupError { +pub struct LookupError { key: String, map: Map, } @@ -615,20 +791,71 @@ impl IsInstructionT for Lookup { } -// #[derive(Clone, Copy, Debug, PartialEq, Eq)] -// struct UnpackJson { -// t: PhantomData, -// } -// #[derive(Clone, Copy, Debug, PartialEq, Eq)] -// struct UnpackJsonError {} -// impl AnError for UnpackJsonError {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct UnpackJson { + t: PhantomData, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct UnpackJsonError {} +impl AnError for UnpackJsonError {} + +// TODO: implement for rest of types +trait AJsonElem: AnElem { + fn to_value(&self) -> Value; + fn from_value(t: PhantomData, x: Value) -> Option; +} + +impl AJsonElem for () { + fn to_value(&self) -> Value { + Value::Null + } + + fn from_value(_t: PhantomData, x: Value) -> Option { + match x { + Value::Null => Some(()), + _ => None, + } + } +} + + // pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { + // match (self, elem_symbol) { + // (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), + // (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), + // (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), + // (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), + // (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), + // (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), + // (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { + // json: json, + // elem_symbol: From::from(elem_symbol), + // }), + // (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { + // non_json: non_json.symbol_str(), + // elem_symbol: From::from(elem_symbol), + // }), + // } + // } + +impl IsInstructionT for UnpackJson { + type In = ConsOut, + Cons, Nil>>; + type Error = UnpackJsonError; + + fn run(&self, x: Self::In) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let json = &x.clone().tl().hd().array[0]; + let result = + AJsonElem::from_value(PhantomData::, json.clone()) + .ok_or_else(|| UnpackJsonError {})?; + returning.returning(result); + Ok(()) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct StringToBytes {} -// #[derive(Clone, Copy, Debug, PartialEq, Eq)] -// struct StringToBytesError {} -// impl AnError for StringToBytesError {} +pub struct StringToBytes {} impl IsInstructionT for StringToBytes { type In = ConsOut, U0>, Cons, Nil>>; @@ -643,9 +870,9 @@ impl IsInstructionT for StringToBytes { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckLe {} +pub struct CheckLe {} #[derive(Clone, Debug, PartialEq, Eq)] -struct CheckLeError { +pub struct CheckLeError { lhs: Elem, rhs: Elem, } @@ -677,9 +904,9 @@ impl IsInstructionT for CheckLe { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckLt {} +pub struct CheckLt {} #[derive(Clone, Debug, PartialEq, Eq)] -struct CheckLtError { +pub struct CheckLtError { lhs: Elem, rhs: Elem, } @@ -711,9 +938,9 @@ impl IsInstructionT for CheckLt { #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckEq {} +pub struct CheckEq {} #[derive(Clone, Debug, PartialEq, Eq)] -struct CheckEqError { +pub struct CheckEqError { lhs: Elem, rhs: Elem, } @@ -743,115 +970,6 @@ impl IsInstructionT for CheckEq { } } - // fn slice_generic::Item>>(offset: Number, - // length: Number, - // iterable: T, - // elem_symbol: ElemSymbol) -> - // Result { - // let u_offset = offset.as_u64() - // .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) - // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - // let u_length = length.as_u64() - // .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) - // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - // let u_offset_plus_length = u_offset.checked_add(u_length) - // .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; - // if iterable.clone().into_iter().count() < u_offset_plus_length { - // Err(ElemError::SliceTooShort { - // offset: u_offset, - // length: u_length, - // iterable: From::from(elem_symbol), - // }) - // } else { - // Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) - // } - // } - - // pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { - // match (maybe_offset, maybe_length, maybe_iterable) { - // (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => - // Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), - // (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { - // let iterator_vec = Vec::from(iterator.clone()); - // Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) - // .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) - // }, - // (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => - // Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), - // (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => - // Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), - // (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { - // Err(ElemError::SliceUnsupportedTypes { - // maybe_not_offset: maybe_not_offset.symbol_str(), - // maybe_not_length: maybe_not_length.symbol_str(), - // maybe_not_iterable: maybe_not_iterable.symbol_str(), - // }) - // } - // } - // } - - // fn index_generic::Item>>(index: Number, - // iterable: T, - // elem_symbol: ElemSymbol) -> - // Result<::Item, ElemError> { - // let u_index: usize = index.as_u64() - // .ok_or_else(|| ElemError::IndexNotU64(index.clone())) - // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; - // if iterable.clone().into_iter().count() <= u_index { - // return Err(ElemError::IndexTooShort { - // index: u_index, - // iterable: From::from(elem_symbol), - // }) - // } else { - // match iterable.into_iter().skip(u_index).next() { - // None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), - // Some(x) => Ok(x), - // } - // } - // } - - // pub fn index(self, maybe_iterable: Self) -> Result { - // match (self, maybe_iterable) { - // // (Self::Number(index), Self::Bytes(iterator)) => - // // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), - // (Self::Number(index), Self::Array(iterator)) => - // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), - // (Self::Number(index), Self::Object(iterator)) => - // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), - // (maybe_not_index, maybe_not_iterable) => { - // Err(ElemError::IndexUnsupportedTypes { - // maybe_not_index: maybe_not_index.symbol_str(), - // maybe_not_iterable: maybe_not_iterable.symbol_str(), - // }) - // } - // } - // } - - // pub fn to_json(self) -> Result { - // Ok(Self::Json(serde_json::to_value(self)?)) - // } - - // pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { - // match (self, elem_symbol) { - // (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), - // (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), - // (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), - // (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), - // (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), - // (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), - // (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { - // json: json, - // elem_symbol: From::from(elem_symbol), - // }), - // (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { - // non_json: non_json.symbol_str(), - // elem_symbol: From::from(elem_symbol), - // }), - // } - // } - From 39af1acb8f48538168660d289797c9672dc7b900 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 10 Mar 2022 12:57:06 -0500 Subject: [PATCH 47/77] WIP: cleanup --- src/types.rs | 182 ------------------------------------------- src/types_scratch.rs | 77 +++++++++--------- 2 files changed, 37 insertions(+), 222 deletions(-) diff --git a/src/types.rs b/src/types.rs index f199303..dfcc9a8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -419,188 +419,6 @@ struct CheckEq {} struct CheckEqError {} impl AnError for CheckEqError {} - - - - - // pub fn check_le(self, other: Self) -> Result { - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckLeIncomparableTypes { - // lhs: self.symbol_str(), - // rhs: other.symbol_str() })? { - // cmp::Ordering::Less => true, - // cmp::Ordering::Equal => true, - // cmp::Ordering::Greater => false, - // }; - // Ok(Self::Bool(result)) - // } - - // pub fn check_lt(self, other: Self) -> Result { - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckLtIncomparableTypes { - // lhs: self.symbol_str(), - // rhs: other.symbol_str() })? { - // cmp::Ordering::Less => true, - // _ => false, - // }; - // Ok(Self::Bool(result)) - // } - - // pub fn check_eq(self, other: Self) -> Result { - // let result = match self.partial_cmp(&other) - // .ok_or_else(|| ElemError::CheckEqIncomparableTypes { - // lhs: self.symbol_str(), - // rhs: other.symbol_str() })? { - // cmp::Ordering::Equal => true, - // _ => false, - // }; - // Ok(Self::Bool(result)) - // } - - // fn slice_generic::Item>>(offset: Number, - // length: Number, - // iterable: T, - // elem_symbol: ElemSymbol) -> - // Result { - // let u_offset = offset.as_u64() - // .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) - // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - // let u_length = length.as_u64() - // .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) - // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - // let u_offset_plus_length = u_offset.checked_add(u_length) - // .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; - // if iterable.clone().into_iter().count() < u_offset_plus_length { - // Err(ElemError::SliceTooShort { - // offset: u_offset, - // length: u_length, - // iterable: From::from(elem_symbol), - // }) - // } else { - // Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) - // } - // } - - // pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { - // match (maybe_offset, maybe_length, maybe_iterable) { - // (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => - // Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), - // (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { - // let iterator_vec = Vec::from(iterator.clone()); - // Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) - // .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) - // }, - // (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => - // Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), - // (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => - // Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), - // (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { - // Err(ElemError::SliceUnsupportedTypes { - // maybe_not_offset: maybe_not_offset.symbol_str(), - // maybe_not_length: maybe_not_length.symbol_str(), - // maybe_not_iterable: maybe_not_iterable.symbol_str(), - // }) - // } - // } - // } - - // fn index_generic::Item>>(index: Number, - // iterable: T, - // elem_symbol: ElemSymbol) -> - // Result<::Item, ElemError> { - // let u_index: usize = index.as_u64() - // .ok_or_else(|| ElemError::IndexNotU64(index.clone())) - // .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; - // if iterable.clone().into_iter().count() <= u_index { - // return Err(ElemError::IndexTooShort { - // index: u_index, - // iterable: From::from(elem_symbol), - // }) - // } else { - // match iterable.into_iter().skip(u_index).next() { - // None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), - // Some(x) => Ok(x), - // } - // } - // } - - // pub fn index(self, maybe_iterable: Self) -> Result { - // match (self, maybe_iterable) { - // // (Self::Number(index), Self::Bytes(iterator)) => - // // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), - // (Self::Number(index), Self::Array(iterator)) => - // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), - // (Self::Number(index), Self::Object(iterator)) => - // Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), - // (maybe_not_index, maybe_not_iterable) => { - // Err(ElemError::IndexUnsupportedTypes { - // maybe_not_index: maybe_not_index.symbol_str(), - // maybe_not_iterable: maybe_not_iterable.symbol_str(), - // }) - // } - // } - // } - - // pub fn to_json(self) -> Result { - // Ok(Self::Json(serde_json::to_value(self)?)) - // } - - // pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { - // match (self, elem_symbol) { - // (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), - // (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), - // (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), - // (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), - // (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), - // (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), - // (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { - // json: json, - // elem_symbol: From::from(elem_symbol), - // }), - // (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { - // non_json: non_json.symbol_str(), - // elem_symbol: From::from(elem_symbol), - // }), - // } - // } - - // pub fn string_to_bytes(self) -> Result { - // match self { - // Self::String(x) => Ok(Self::Bytes(x.into_bytes())), - // other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), - // } - // } - - - - - - -// ToJson, -// Restack(Restack), - -// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -// pub enum Instruction { -// Push(Elem), -// Restack(Restack), -// HashSha256, -// CheckLe, -// CheckLt, -// CheckEq, -// Concat, -// Slice, -// Index, -// Lookup, -// AssertTrue, -// ToJson, -// UnpackJson(ElemSymbol), -// StringToBytes, -// } - - - // pub fn demo_triple() -> ConsElem<(), TBool, ConsElem<(), TUnit, ConsElem<(), TBool, Nil<()>>>> { // Nil { t: PhantomData } // .cons(TBool { get_bool: true }) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 971cef5..9df402f 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -18,9 +18,14 @@ use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; use serde_json::{Map, Number, Value}; use thiserror::Error; +// use generic_array::typenum::{B1}; +// use typenum::marker_traits::Unsigned; +// use typenum::type_operators::IsLess; + // NEXT: -// - finish migrating instruction implementations from elem to IsInstructionT -// - migrate pop-stack from IsInstruction +// - migrate pop from ElemList +// fn pop(x: PhantomData, stack: &mut Stack) -> Result; + // - delete old IsInstruction // - add typing info as with pop-stack @@ -210,6 +215,7 @@ where + #[derive(Clone, Debug)] pub struct ReturnSingleton where @@ -457,24 +463,15 @@ where pub trait IsInstructionT: std::fmt::Debug { - type In: IOList; + type IO: IOList; type Error: AnError; - fn run(&self, x: Self::In) -> Result<(), Self::Error>; + fn run(&self, x: Self::IO) -> Result<(), Self::Error>; } - -// #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -// pub enum Instruction { -// Restack(Restack), -// } - - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Concat { -} +pub struct Concat {} #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ConcatError {} impl AnError for ConcatError {} @@ -487,12 +484,12 @@ impl AnError for ConcatError {} // // bytes, array, object impl IsInstructionT for Concat { - type In = ConsOut, U2, + type IO = ConsOut, U2, ReturnOr, U2, ReturnSingleton, U2>>>, Nil>; type Error = ConcatError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let y = x.hd(); match y { ReturnOr::Left { array, returning } => { @@ -522,10 +519,10 @@ pub struct AssertTrueError {} impl AnError for AssertTrueError {} impl IsInstructionT for AssertTrue { - type In = ConsOut, Nil>; + type IO = ConsOut, Nil>; type Error = AssertTrueError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let array = x.clone().hd().singleton.array; let returning = x.hd().returning; if array[0] { @@ -544,10 +541,10 @@ pub struct Push { } impl IsInstructionT for Push { - type In = ConsOut, Nil>; + type IO = ConsOut, Nil>; type Error = Empty; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { x.hd().returning.returning(self.push.clone()); Ok(()) } @@ -562,10 +559,10 @@ pub struct HashSha256Error {} impl AnError for HashSha256Error {} impl IsInstructionT for HashSha256 { - type In = ConsOut, U1>, Nil>; + type IO = ConsOut, U1>, Nil>; type Error = Empty; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let array = x.clone().hd().singleton.array; let returning = x.hd().returning; returning.returning(super::sha256(&array[0])); @@ -607,14 +604,14 @@ impl AnError for SliceError {} // bytes, string, array, object impl IsInstructionT for Slice { - type In = ConsOut, U1, + type IO = ConsOut, U1, ReturnOr, U1, ReturnSingleton, U1>>>>, Cons, Nil>>; type Error = SliceError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let y = x.clone().hd(); let offset_length = x.clone().tl().hd().array; let offset = &offset_length[0]; @@ -696,13 +693,13 @@ impl AnError for IndexError {} // bytes, array, object impl IsInstructionT for Index { - type In = ConsOut, + type IO = ConsOut, Cons, U2, Singleton, U2>>, Cons, Nil>>>; type Error = IndexError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = x.clone().tl().hd(); let index = &x.clone().tl().tl().hd().array[0]; @@ -747,10 +744,10 @@ pub struct ToJsonError { impl AnError for ToJsonError {} impl IsInstructionT for ToJson { - type In = ConsOut, Cons, Nil>>; + type IO = ConsOut, Cons, Nil>>; type Error = ToJsonError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); @@ -774,10 +771,10 @@ pub struct LookupError { impl AnError for LookupError {} impl IsInstructionT for Lookup { - type In = ConsOut, Cons, Cons, U1>, Nil>>>; + type IO = ConsOut, Cons, Cons, U1>, Nil>>>; type Error = LookupError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let key = &x.clone().tl().hd().array[0]; let map = &x.tl().tl().hd().array[0]; @@ -838,11 +835,11 @@ impl AJsonElem for () { // } impl IsInstructionT for UnpackJson { - type In = ConsOut, + type IO = ConsOut, Cons, Nil>>; type Error = UnpackJsonError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let json = &x.clone().tl().hd().array[0]; let result = @@ -858,10 +855,10 @@ impl IsInstructionT for UnpackJson { pub struct StringToBytes {} impl IsInstructionT for StringToBytes { - type In = ConsOut, U0>, Cons, Nil>>; + type IO = ConsOut, U0>, Cons, Nil>>; type Error = Empty; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let in_str = &x.clone().tl().hd().array[0]; returning.returning(in_str.clone().into_bytes()); @@ -879,10 +876,10 @@ pub struct CheckLeError { impl AnError for CheckLeError {} impl IsInstructionT for CheckLe { - type In = ConsOut, Cons, Nil>>; + type IO = ConsOut, Cons, Nil>>; type Error = CheckLeError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); @@ -913,10 +910,10 @@ pub struct CheckLtError { impl AnError for CheckLtError {} impl IsInstructionT for CheckLt { - type In = ConsOut, Cons, Nil>>; + type IO = ConsOut, Cons, Nil>>; type Error = CheckLtError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); @@ -947,10 +944,10 @@ pub struct CheckEqError { impl AnError for CheckEqError {} impl IsInstructionT for CheckEq { - type In = ConsOut, Cons, Nil>>; + type IO = ConsOut, Cons, Nil>>; type Error = CheckEqError; - fn run(&self, x: Self::In) -> Result<(), Self::Error> { + fn run(&self, x: Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); From 4a41781227aaf99aaaa27cd1ff1140209b93d818 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 10 Mar 2022 17:13:14 -0500 Subject: [PATCH 48/77] implement typed popping from stack for instructions --- src/types_scratch.rs | 163 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 155 insertions(+), 8 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 9df402f..e1ae9c7 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,22 +1,23 @@ -use crate::elem::{Elem, AnElem, AnElemError, ElemSymbol}; +use crate::elem::{Elem, AnElem}; use crate::stack::{Stack, StackError}; use crate::types::{Empty, AnError, Nil}; use std::cmp; use std::convert::TryFrom; -use std::iter::FromIterator; +// use std::iter::FromIterator; use std::marker::PhantomData; use std::fmt::Debug; use std::sync::{Arc, Mutex}; use std::string::FromUtf8Error; -use enumset::EnumSet; -use generic_array::typenum::{U0, U1, U2}; -use generic_array::sequence::GenericSequence; +// use enumset::EnumSet; use generic_array::functional::FunctionalSequence; -use generic_array::{arr, GenericArray, GenericArrayIter, ArrayLength}; +use generic_array::sequence::GenericSequence; +use generic_array::typenum::{U0, U1, U2}; +use generic_array::{GenericArray, ArrayLength}; use serde_json::{Map, Number, Value}; use thiserror::Error; +use typenum::marker_traits::Unsigned; // use generic_array::typenum::{B1}; // use typenum::marker_traits::Unsigned; @@ -44,6 +45,32 @@ where // fn from_elem(_t: PhantomData, x: Elem) -> Result { ::from_elem(PhantomData, x).map(|y| { Singleton { t: y, } }) } // } +#[derive(Clone, Debug, Error)] +pub enum ElemsPopError { + #[error("Elems::pop singleton: tried to pop an Elem that was not found: {error:?}")] + PopSingleton { + error: StackError, + }, + + #[error("Elems::pop: tried to pop a set of Elem's that were not found: {hd_error:?}\n{tl_errors:?}")] + Pop { + hd_error: Arc, + tl_errors: Arc, + }, + + // TODO: add detail + #[error("Elems::pop: generic_array internal error")] + GenericArray, +} + +impl From for ElemsPopError { + fn from(error: StackError) -> Self { + Self::PopSingleton { + error: error, + } + } +} + pub trait Elems: Clone + Debug { type Hd: AnElem; type N: ArrayLength; @@ -56,6 +83,10 @@ pub trait Elems: Clone + Debug { // fn elem_symbols(t: PhantomData) -> EnumSet; // fn to_elems(self) -> Elem; // fn from_elems(t: PhantomData, x: &mut Stack) -> Result; + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized; } pub trait IElems: Elems {} @@ -111,6 +142,23 @@ where fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, _g: G) -> U { f(&self.array) } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + let vec = (1..::to_usize()).map(|_array_ix| { + stack + .pop_elem(PhantomData::) + .map_err(|e| >::from(e)) + }).collect::, ElemsPopError>>()?; + let array = GenericArray::from_exact_iter(vec).ok_or_else(|| { + ElemsPopError::GenericArray + })?; + Ok(Singleton { + array: array, + }) + } } impl IElems for Singleton @@ -150,6 +198,25 @@ where Self::Right(x) => g(x), } } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + match as Elems>::pop(PhantomData, stack) { + Ok(Singleton { array }) => Ok(Self::Left(array)), + Err(hd_error) => { + Elems::pop(PhantomData::, stack) + .map(|x| Self::Right(x)) + .map_err(|tl_errors| { + ElemsPopError::Pop { + hd_error: Arc::new(hd_error), + tl_errors: Arc::new(tl_errors), + } + }) + }, + } + } } impl IElems for Or @@ -240,6 +307,18 @@ where fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, g: G) -> U { self.singleton.or(f, g) } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + Ok(ReturnSingleton { + singleton: Elems::pop(PhantomData::>, stack)?, + returning: Return { + return_value: Arc::new(Mutex::new(None)), + }, + }) + } } impl IOElems for ReturnSingleton @@ -287,6 +366,26 @@ where Self::Right(x) => g(x), } } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + as Elems>::pop(PhantomData, stack) + .map(|x| { + match x { + Or::Left(array) => Self::Left { + array: array, + returning: Return { + return_value: Arc::new(Mutex::new(None)), + }, + }, + Or::Right(y) => Self::Right(y), + } + }) + + } + } impl IOElems for ReturnOr @@ -314,12 +413,14 @@ where - // + IntoIterator pub trait IsList: Debug { type Hd: Elems; type Tl: IsList; + fn empty_list() -> Option where Self: Sized; + fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self; + fn is_empty(&self) -> bool; fn hd(self) -> Self::Hd; fn tl(self) -> Self::Tl; @@ -332,13 +433,35 @@ pub trait IsList: Debug { tl: self, } } - // fn pop(x: PhantomData, stack: &mut Stack) -> Result; + + // TODO: wrap ElemsError w/ whole stack, position in stack, etc + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + match ::empty_list() { + Some(x) => Ok(x), + None => { + let x = ::pop(PhantomData, stack)?; + let xs = ::pop(PhantomData, stack)?; + Ok(::cons_list(x, xs)) + } + } + } } impl IsList for Nil { type Hd = Singleton<(), U0>; type Tl = Nil; + fn empty_list() -> Option where Self: Sized { + Some(Self {}) + } + + fn cons_list(_x: Self::Hd, _xs: Self::Tl) -> Self { + Self {} + } + fn is_empty(&self) -> bool { true } @@ -364,6 +487,17 @@ impl IsList for Cons { type Hd = T; type Tl = U; + fn empty_list() -> Option where Self: Sized { + None + } + + fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self { + Cons { + hd: x, + tl: xs, + } + } + fn is_empty(&self) -> bool { false } @@ -435,6 +569,19 @@ where type Hd = T; type Tl = U; + fn empty_list() -> Option where Self: Sized { + None + } + + fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self { + ConsOut { + cons: Cons { + hd: x, + tl: xs, + }, + } + } + fn is_empty(&self) -> bool { self.cons.is_empty() } From fd63fcb4b3ad3e3fe4bb6c6f3b4425485ce6c902 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 14 Mar 2022 16:13:22 -0400 Subject: [PATCH 49/77] make Elems and IsList Iterable, dynamic instruction dispatch --- src/types_scratch.rs | 236 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 219 insertions(+), 17 deletions(-) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index e1ae9c7..073c1dc 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -14,7 +14,7 @@ use std::string::FromUtf8Error; use generic_array::functional::FunctionalSequence; use generic_array::sequence::GenericSequence; use generic_array::typenum::{U0, U1, U2}; -use generic_array::{GenericArray, ArrayLength}; +use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; use serde_json::{Map, Number, Value}; use thiserror::Error; use typenum::marker_traits::Unsigned; @@ -24,11 +24,14 @@ use typenum::marker_traits::Unsigned; // use typenum::type_operators::IsLess; // NEXT: -// - migrate pop from ElemList -// fn pop(x: PhantomData, stack: &mut Stack) -> Result; - // - delete old IsInstruction +// // - add typing info as with pop-stack +// + get typing up to parity +// + add special-case unifier for restack + IsInstructionT for testing? +// +// - random type -> ~random inhabitant of the type +// - random typed program!? #[derive(Clone, Debug, PartialEq, Eq)] pub struct Singleton @@ -39,6 +42,20 @@ where array: GenericArray, } +impl IntoIterator for Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Item = Elem; + type IntoIter = std::iter::Map, impl Fn(T) -> Elem>; + + fn into_iter(self) -> Self::IntoIter { + self.array.into_iter().map(AnElem::to_elem) + } +} + + // impl AnElem for Singleton { // fn elem_symbol(_t: PhantomData) -> EnumSet { ::elem_symbol(PhantomData) } // fn to_elem(self) -> Elem { self.t.to_elem() } @@ -71,7 +88,7 @@ impl From for ElemsPopError { } } -pub trait Elems: Clone + Debug { +pub trait Elems: Clone + Debug + IntoIterator { type Hd: AnElem; type N: ArrayLength; type Tl: Elems; @@ -124,6 +141,9 @@ pub trait IOElems: Elems { where F: Fn(&GenericArray, &Return) -> T, G: Fn(&Self::Tl) -> T; + + // TODO: rename to 'returned' to match Return + fn returning(&self) -> Option; } @@ -180,6 +200,54 @@ where Right(U), } +// #[derive(Clone, Debug, PartialEq, Eq)] +pub enum IterOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + Left( as IntoIterator>::IntoIter), + Right(::IntoIter), +} + +impl Iterator for IterOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Item = Elem; + + fn next(&mut self) -> Option { + match self { + Self::Left(x) => x.next(), + Self::Right(x) => x.next(), + } + } +} + +impl IntoIterator for Or +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Item = Elem; + type IntoIter = IterOr; + + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Left(array) => IterOr::Left( + Singleton { + array: array, + }.into_iter() + ), + Self::Right(xs) => IterOr::Right(xs.into_iter()), + } + } +} + impl Elems for Or where T: AnElem, @@ -293,6 +361,19 @@ where returning: Return, } +impl IntoIterator for ReturnSingleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Item = Elem; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.singleton.into_iter() + } +} + impl Elems for ReturnSingleton where T: AnElem, @@ -333,6 +414,10 @@ where { f(&self.singleton.array, &self.returning) } + + fn returning(&self) -> Option { + self.returning.returned().map(|x| x.to_elem()) + } } @@ -350,6 +435,23 @@ where Right(U), } +impl IntoIterator for ReturnOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Item = Elem; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Left { array, .. } => Or::::Left(array).into_iter(), + Self::Right(xs) => Or::Right(xs).into_iter(), + } + } +} + impl Elems for ReturnOr where T: AnElem, @@ -406,6 +508,15 @@ where Self::Right(x) => g(x), } } + + fn returning(&self) -> Option { + match self { + Self::Left { returning, .. } => { + returning.returned().map(|x| x.to_elem()) + }, + Self::Right(x) => x.returning(), + } + } } @@ -413,8 +524,8 @@ where -// + IntoIterator -pub trait IsList: Debug { + +pub trait IsList: Clone + Debug + IntoIterator { type Hd: Elems; type Tl: IsList; @@ -483,6 +594,31 @@ pub struct Cons { tl: U, } +pub struct IterCons { + hd: ::IntoIter, + tl: ::IntoIter, +} + +impl IntoIterator for Cons { + type Item = Elem; + type IntoIter = IterCons; + + fn into_iter(self) -> Self::IntoIter { + IterCons { + hd: self.hd.into_iter(), + tl: self.tl.into_iter(), + } + } +} + +impl Iterator for IterCons { + type Item = Elem; + + fn next(&mut self) -> Option { + self.hd.next().or_else(|| self.tl.next()) + } +} + impl IsList for Cons { type Hd = T; type Tl = U; @@ -509,15 +645,6 @@ impl IsList for Cons { fn tl(self) -> Self::Tl { self.tl } - - // // add better errors - // fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - // let hd_arr = stack.pop_generic_array(PhantomData, PhantomData)?; - // Ok(Cons { - // hd: hd_arr, - // tl: Self::Tl::pop(PhantomData, stack)?, - // }) - // } } @@ -541,6 +668,8 @@ where pub trait IOList: IsList { type Return: IOElems; + + fn returning(&self) -> Option; } impl IOList for Cons @@ -549,6 +678,10 @@ where U: IOList, { type Return = U::Return; + + fn returning(&self) -> Option { + self.tl.returning() + } } @@ -561,6 +694,19 @@ where cons: Cons, } +impl IntoIterator for ConsOut { + type Item = Elem; + type IntoIter = IterCons; + + fn into_iter(self) -> Self::IntoIter { + self.cons.into_iter() + // IterCons { + // cons: self.cons, + // at_head: true, + // } + } +} + impl IsList for ConsOut where T: IOElems, @@ -601,6 +747,10 @@ where U: IList, { type Return = T; + + fn returning(&self) -> Option { + self.cons.hd.returning() + } } @@ -609,13 +759,65 @@ where -pub trait IsInstructionT: std::fmt::Debug { +pub trait IsInstructionT: Clone + Debug { type IO: IOList; type Error: AnError; fn run(&self, x: Self::IO) -> Result<(), Self::Error>; } +pub enum InstructionError { + ElemsPopError(ElemsPopError), + + RawInstructionError(String), + + // TODO: more granular typing + MissingOutput { + instruction: String, + stack_input: String, + }, +} + +pub trait IsInstructionU: Debug { + fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError>; + + // type IO: IOList; + // type Error: AnError; + + // fn run(&self, x: Self::IO) -> Result<(), Self::Error>; +} + +impl IsInstructionU for T +where + T: IsInstructionT, +{ + fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError> { + let stack_input = IsList::pop(PhantomData::<::IO>, stack) + .map_err(|e| InstructionError::ElemsPopError(e))?; + self.run(stack_input) + .map_err(|e| InstructionError::RawInstructionError(format!("{:?}", e)))?; + let output_value = stack_input + .returning() + .ok_or_else(|| InstructionError::MissingOutput { + instruction: format!("{:?}", self), + stack_input: format!("{:?}", stack_input), + })?; + stack.push(output_value); + Ok(()) + } +} + + +#[derive(Clone, Debug)] +pub struct Instrs { + instrs: Vec>, +} + + + + + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Concat {} From e59508bb6550470ae34757da92cda27ff09d0786 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 14 Mar 2022 18:29:20 -0400 Subject: [PATCH 50/77] example program working with new instruction types --- src/elem.rs | 2 +- src/executor.rs | 2 +- src/instruction.rs | 6 +- src/lib.rs | 1 + src/main.rs | 28 ++++++- src/restack.rs | 6 +- src/stack.rs | 13 +++- src/types_scratch.rs | 172 ++++++++++++++++++++++++++++++++++--------- 8 files changed, 188 insertions(+), 42 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 033eb6e..4e103ac 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -543,7 +543,7 @@ impl From for ElemError { -pub trait AnElem: Clone + std::fmt::Debug { +pub trait AnElem: Clone + std::fmt::Debug + PartialEq { fn elem_symbol(t: PhantomData) -> EnumSet; fn to_elem(self) -> Elem; fn from_elem(t: PhantomData, x: Elem) -> Result; diff --git a/src/executor.rs b/src/executor.rs index dc08c7a..4c7037e 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -50,7 +50,7 @@ impl Executor { } fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { - self.stack = restack.run(&mut self.stack)?; + restack.run(&mut self.stack)?; Ok(()) } diff --git a/src/instruction.rs b/src/instruction.rs index 2a69315..0378b0c 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -71,10 +71,12 @@ impl Restack { let mut restack_type: Vec = (0..self.restack_depth) .map(|x| context.push(ElemType::any(vec![line_no.in_at(x)]))) .collect(); + let i_type = restack_type.clone(); + self.run(&mut restack_type)?; Ok(Type { context: context, - i_type: restack_type.clone(), - o_type: self.run(&mut restack_type)?, + i_type: i_type, + o_type: restack_type, }) } } diff --git a/src/lib.rs b/src/lib.rs index 7b968ec..c7aa8b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ mod stack; pub use stack::{Stack, StackError}; mod types; mod types_scratch; +pub use types_scratch::{Instrs, AssertTrue, Push}; // pub use types::{Instruction, Instructions}; // , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList mod instruction; diff --git a/src/main.rs b/src/main.rs index 0665065..49a5ac0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; + +use cryptoscript::{Stack, Instrs, AssertTrue, Push}; // use cryptoscript::{demo_triple, demo_triple_with_tl_handles_intermediate_types, HList}; #[cfg(test)] @@ -26,7 +28,7 @@ mod tests { fn main() { - let _input_json = r#" + let input_json = r#" { "queries": [ { @@ -69,6 +71,30 @@ fn main() { } "#; + let mut instructions_vec_t_1 = Instrs::new(); + instructions_vec_t_1.instr(Push { push: true }); + instructions_vec_t_1.restack(Restack::id()); + instructions_vec_t_1.instr(AssertTrue {}); + + // : Instrs = Instrs { instrs: vec![ + // TEST #1 + // Arc::new(Push { push: true }), + + // Instruction::Push(Elem::Bool(true)), + // Instruction::Restack(Restack::id()), + // Instruction::AssertTrue, + + // ]}; + + let mut stack = Stack::new(); + let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); + stack.push_elem(input_json_value); + + format!("{:?}", instructions_vec_t_1.run(&mut stack)); + println!("FINAL STACK"); + println!("{:?}", stack); + + // let json_instructions = parse_json()" let instructions_vec: Vec = vec![ // TEST #1 diff --git a/src/restack.rs b/src/restack.rs index 8fa3046..fc9f1d7 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -98,7 +98,7 @@ impl Restack { } // restack a Stack - pub fn run(&self, stack: &mut Vec) -> Result, RestackError> { + pub fn run(&self, stack: &mut Vec) -> Result<(), RestackError> { if self.restack_depth <= stack.len() { let result = self.restack_vec.iter().map(|&restack_index| match stack.get(restack_index) { @@ -109,7 +109,9 @@ impl Restack { match result { Ok(mut result_ok) => { result_ok.extend(stack.drain(self.restack_depth..)); - Ok(result_ok) }, + *stack = result_ok; + Ok(()) + }, Err(e) => Err(e) } diff --git a/src/stack.rs b/src/stack.rs index acc6208..acd8481 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -21,12 +21,19 @@ use typenum::marker_traits::Unsigned; // TODO: use for execution +// TODO: pub field needed? #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Stack { - stack: Vec, + pub stack: Vec, } impl Stack { + pub fn new() -> Self { + Stack { + stack: vec![], + } + } + // TODO: since pop can fail, require passing debug info to it // (so we know what we were expecting) pub fn pop(&mut self) -> Result { @@ -47,6 +54,10 @@ impl Stack { self.stack = memo; } + pub fn push_elem(&mut self, elem: impl AnElem) { + self.push(elem.to_elem()) + } + // TODO: reversed? pub fn pop_generic_array>(&mut self, _t: PhantomData, diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 073c1dc..8699260 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,5 +1,6 @@ use crate::elem::{Elem, AnElem}; use crate::stack::{Stack, StackError}; +use crate::restack::{Restack, RestackError}; use crate::types::{Empty, AnError, Nil}; use std::cmp; @@ -48,7 +49,7 @@ where N: ArrayLength + Debug, { type Item = Elem; - type IntoIter = std::iter::Map, impl Fn(T) -> Elem>; + type IntoIter = std::iter::Map, fn(T) -> Elem>; fn into_iter(self) -> Self::IntoIter { self.array.into_iter().map(AnElem::to_elem) @@ -759,40 +760,44 @@ where -pub trait IsInstructionT: Clone + Debug { +pub trait IsInstructionT: Clone + Debug + PartialEq { type IO: IOList; type Error: AnError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error>; + fn name(x: PhantomData) -> String; + fn run(&self, x: &Self::IO) -> Result<(), Self::Error>; } +#[derive(Debug)] pub enum InstructionError { ElemsPopError(ElemsPopError), RawInstructionError(String), - // TODO: more granular typing + // TODO: more granular error typing MissingOutput { instruction: String, stack_input: String, }, + + RestackError(RestackError), } -pub trait IsInstructionU: Debug { +pub trait IsStackInstruction: Debug { + fn name(&self) -> String; fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError>; - - // type IO: IOList; - // type Error: AnError; - - // fn run(&self, x: Self::IO) -> Result<(), Self::Error>; } -impl IsInstructionU for T +impl IsStackInstruction for T where T: IsInstructionT, { + fn name(&self) -> String { + IsInstructionT::name(PhantomData::) + } + fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError> { - let stack_input = IsList::pop(PhantomData::<::IO>, stack) + let stack_input = &IsList::pop(PhantomData::<::IO>, stack) .map_err(|e| InstructionError::ElemsPopError(e))?; self.run(stack_input) .map_err(|e| InstructionError::RawInstructionError(format!("{:?}", e)))?; @@ -808,13 +813,60 @@ where } + #[derive(Clone, Debug)] pub struct Instrs { - instrs: Vec>, -} + // TODO: replace Result with Either? + pub instrs: Vec, Restack>>, +} + +// fn example_instrs() -> Instrs { +// Instrs { +// instrs: vec![ +// Arc::new(Concat {}), +// Arc::new(AssertTrue {}), +// Arc::new(Push { push: () }), +// Arc::new(HashSha256 {}), +// Arc::new(Slice {}), +// Arc::new(Index {}), +// Arc::new(ToJson {}), +// Arc::new(Lookup {}), +// Arc::new(UnpackJson { t: PhantomData::<()> }), +// Arc::new(StringToBytes {}), +// Arc::new(CheckLe {}), +// Arc::new(CheckLt {}), +// Arc::new(CheckEq {}) +// ], +// } +// } +impl Instrs { + pub fn new() -> Self { + Instrs { + instrs: vec![], + } + } + pub fn run(&self, stack: &mut Stack) -> Result<(), InstructionError> { + for instr_or_restack in &self.instrs { + match instr_or_restack { + Ok(instr) => instr.stack_run(stack)?, + Err(restack) => restack.run(&mut stack.stack) + .map_err(|e| InstructionError::RestackError(e))?, + } + } + Ok(()) + } + + pub fn instr(&mut self, instr: impl IsStackInstruction + 'static) -> () { + self.instrs.push(Ok(Arc::new(instr))) + } + + pub fn restack(&mut self, restack: Restack) -> () { + self.instrs.push(Err(restack)) + } +} @@ -838,8 +890,12 @@ impl IsInstructionT for Concat { ReturnSingleton, U2>>>, Nil>; type Error = ConcatError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { - let y = x.hd(); + fn name(_x: PhantomData) -> String { + "concat".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let y = x.clone().hd(); match y { ReturnOr::Left { array, returning } => { let lhs = &array[0]; @@ -871,9 +927,13 @@ impl IsInstructionT for AssertTrue { type IO = ConsOut, Nil>; type Error = AssertTrueError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "assert_true".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let array = x.clone().hd().singleton.array; - let returning = x.hd().returning; + let returning = x.clone().hd().returning; if array[0] { returning.returning(true); Ok(()) @@ -884,17 +944,21 @@ impl IsInstructionT for AssertTrue { } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Push { - push: T, + pub push: T, } impl IsInstructionT for Push { type IO = ConsOut, Nil>; type Error = Empty; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { - x.hd().returning.returning(self.push.clone()); + fn name(_x: PhantomData) -> String { + format!("push_{:?}", AnElem::elem_symbol(PhantomData::)) + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + x.clone().hd().returning.returning(self.push.clone()); Ok(()) } } @@ -911,9 +975,13 @@ impl IsInstructionT for HashSha256 { type IO = ConsOut, U1>, Nil>; type Error = Empty; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "sha256".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let array = x.clone().hd().singleton.array; - let returning = x.hd().returning; + let returning = x.clone().hd().returning; returning.returning(super::sha256(&array[0])); Ok(()) } @@ -960,7 +1028,11 @@ impl IsInstructionT for Slice { Cons, Nil>>; type Error = SliceError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "slice".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let y = x.clone().hd(); let offset_length = x.clone().tl().hd().array; let offset = &offset_length[0]; @@ -1048,7 +1120,11 @@ impl IsInstructionT for Index { Cons, Nil>>>; type Error = IndexError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "index".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = x.clone().tl().hd(); let index = &x.clone().tl().tl().hd().array[0]; @@ -1096,7 +1172,11 @@ impl IsInstructionT for ToJson { type IO = ConsOut, Cons, Nil>>; type Error = ToJsonError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "to_json".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); @@ -1123,10 +1203,14 @@ impl IsInstructionT for Lookup { type IO = ConsOut, Cons, Cons, U1>, Nil>>>; type Error = LookupError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "lookup".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let key = &x.clone().tl().hd().array[0]; - let map = &x.tl().tl().hd().array[0]; + let map = &x.clone().tl().tl().hd().array[0]; returning.returning(map.get(key) .ok_or_else(|| LookupError { key: key.clone(), @@ -1188,7 +1272,11 @@ impl IsInstructionT for UnpackJson { Cons, Nil>>; type Error = UnpackJsonError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "unpack_json".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let json = &x.clone().tl().hd().array[0]; let result = @@ -1207,7 +1295,11 @@ impl IsInstructionT for StringToBytes { type IO = ConsOut, U0>, Cons, Nil>>; type Error = Empty; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "string_to_bytes".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let in_str = &x.clone().tl().hd().array[0]; returning.returning(in_str.clone().into_bytes()); @@ -1228,7 +1320,11 @@ impl IsInstructionT for CheckLe { type IO = ConsOut, Cons, Nil>>; type Error = CheckLeError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "check_le".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); @@ -1262,7 +1358,11 @@ impl IsInstructionT for CheckLt { type IO = ConsOut, Cons, Nil>>; type Error = CheckLtError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "check_lt".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); @@ -1296,7 +1396,11 @@ impl IsInstructionT for CheckEq { type IO = ConsOut, Cons, Nil>>; type Error = CheckEqError; - fn run(&self, x: Self::IO) -> Result<(), Self::Error> { + fn name(_x: PhantomData) -> String { + "check_eq".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); let array = all_elems_untyped(y); From 7b018e90c4e7fd2681765c71c97d306b8f9a6545 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 14 Mar 2022 18:36:38 -0400 Subject: [PATCH 51/77] cleanup unused versions of instructions/executor --- src/elem.rs | 342 ------------------------------------- src/executor.rs | 172 +++++++++---------- src/main.rs | 37 ++-- src/types.rs | 396 +------------------------------------------ src/types_scratch.rs | 7 - 5 files changed, 108 insertions(+), 846 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 4e103ac..5d28f37 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -190,350 +190,8 @@ impl Elem { pub fn symbol_str(&self) -> &'static str { From::from(self.symbol()) } - - pub fn assert_true(&self) -> Result<(), ElemError> { - match self { - Self::Bool(x) => if *x { - Ok(()) - } else { - Err(ElemError::AssertTrueFailed()) - }, - found => Err(ElemError::AssertTrueUnsupportedType(found.clone())), - } - } - - pub fn check_le(self, other: Self) -> Result { - let result = match self.partial_cmp(&other) - .ok_or_else(|| ElemError::CheckLeIncomparableTypes { - lhs: self.symbol_str(), - rhs: other.symbol_str() })? { - cmp::Ordering::Less => true, - cmp::Ordering::Equal => true, - cmp::Ordering::Greater => false, - }; - Ok(Self::Bool(result)) - } - - pub fn check_lt(self, other: Self) -> Result { - let result = match self.partial_cmp(&other) - .ok_or_else(|| ElemError::CheckLtIncomparableTypes { - lhs: self.symbol_str(), - rhs: other.symbol_str() })? { - cmp::Ordering::Less => true, - _ => false, - }; - Ok(Self::Bool(result)) - } - - pub fn check_eq(self, other: Self) -> Result { - let result = match self.partial_cmp(&other) - .ok_or_else(|| ElemError::CheckEqIncomparableTypes { - lhs: self.symbol_str(), - rhs: other.symbol_str() })? { - cmp::Ordering::Equal => true, - _ => false, - }; - Ok(Self::Bool(result)) - } - - fn concat_generic::Item>>(x: T, y: T) -> T { - x.into_iter().chain(y.into_iter()).collect() - } - - pub fn concat(self, other: Self) -> Result { - match (self, other) { - (Self::Bytes(x), Self::Bytes(y)) => Ok(Self::Bytes(Self::concat_generic(x, y))), - (Self::String(x), Self::String(y)) => { - Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) - .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) - }, - (Self::Array(x), Self::Array(y)) => Ok(Self::Array(Self::concat_generic(x, y))), - (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), - (some_x, some_y) => { - Err(ElemError::ConcatUnsupportedTypes { - lhs: some_x.symbol_str(), - rhs: some_y.symbol_str() - }) - }, - } - } - - fn slice_generic::Item>>(offset: Number, - length: Number, - iterable: T, - elem_symbol: ElemSymbol) -> - Result { - let u_offset = offset.as_u64() - .ok_or_else(|| ElemError::SliceOffsetNotU64(offset.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - let u_length = length.as_u64() - .ok_or_else(|| ElemError::SliceLengthNotU64(length.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() }))?; - let u_offset_plus_length = u_offset.checked_add(u_length) - .ok_or_else(|| ElemError::SliceOverflow { offset: offset.clone(), length: length.clone() })?; - if iterable.clone().into_iter().count() < u_offset_plus_length { - Err(ElemError::SliceTooShort { - offset: u_offset, - length: u_length, - iterable: From::from(elem_symbol), - }) - } else { - Ok(iterable.into_iter().skip(u_offset).take(u_length).collect()) - } - } - - pub fn slice(maybe_offset: Self, maybe_length: Self, maybe_iterable: Self) -> Result { - match (maybe_offset, maybe_length, maybe_iterable) { - (Self::Number(offset), Self::Number(length), Self::Bytes(iterator)) => - Ok(Self::Bytes(Self::slice_generic(offset, length, iterator, ElemSymbol::Bytes)?)), - (Self::Number(offset), Self::Number(length), Self::String(iterator)) => { - let iterator_vec = Vec::from(iterator.clone()); - Ok(Self::String(String::from_utf8(Self::slice_generic(offset.clone(), length.clone(), iterator_vec, ElemSymbol::String)?) - .map_err(|_| ElemError::SliceInvalidUTF8 { offset: offset, length: length, iterator: iterator })?)) - }, - (Self::Number(offset), Self::Number(length), Self::Array(iterator)) => - Ok(Self::Array(Self::slice_generic(offset, length, iterator, ElemSymbol::Number)?)), - (Self::Number(offset), Self::Number(length), Self::Object(iterator)) => - Ok(Self::Object(Self::slice_generic(offset, length, iterator, ElemSymbol::Object)?)), - (maybe_not_offset, maybe_not_length, maybe_not_iterable) => { - Err(ElemError::SliceUnsupportedTypes { - maybe_not_offset: maybe_not_offset.symbol_str(), - maybe_not_length: maybe_not_length.symbol_str(), - maybe_not_iterable: maybe_not_iterable.symbol_str(), - }) - } - } - } - - fn index_generic::Item>>(index: Number, - iterable: T, - elem_symbol: ElemSymbol) -> - Result<::Item, ElemError> { - let u_index: usize = index.as_u64() - .ok_or_else(|| ElemError::IndexNotU64(index.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| ElemError::IndexOverflow(index.clone())))?; - if iterable.clone().into_iter().count() <= u_index { - return Err(ElemError::IndexTooShort { - index: u_index, - iterable: From::from(elem_symbol), - }) - } else { - match iterable.into_iter().skip(u_index).next() { - None => Err(ElemError::IndexTooShort { index: u_index, iterable: From::from(elem_symbol) }), - Some(x) => Ok(x), - } - } - } - - pub fn index(self, maybe_iterable: Self) -> Result { - match (self, maybe_iterable) { - // (Self::Number(index), Self::Bytes(iterator)) => - // Ok(Self::Bytes(vec![Self::index_generic(index, iterator, ElemSymbol::Bytes)?])), - (Self::Number(index), Self::Array(iterator)) => - Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Json)?)), - (Self::Number(index), Self::Object(iterator)) => - Ok(Self::Json(Self::index_generic(index, iterator, ElemSymbol::Object)?.1)), - (maybe_not_index, maybe_not_iterable) => { - Err(ElemError::IndexUnsupportedTypes { - maybe_not_index: maybe_not_index.symbol_str(), - maybe_not_iterable: maybe_not_iterable.symbol_str(), - }) - } - } - } - - // you can lookup a key in a Map (or fail, no recovery) - pub fn lookup(self, maybe_map: Self) -> Result { - match (self, maybe_map) { - (Self::String(key), Self::Object(map)) => { - Ok(Self::Json(map.get(&key) - .ok_or_else(|| ElemError::LookupKeyMissing { - key: key, - map: map.clone(), - }) - .map(|x|x.clone())?)) - }, - (maybe_not_key, maybe_not_map) => Err(ElemError::LookupUnsupportedTypes { - maybe_not_key: maybe_not_key.symbol_str(), - maybe_not_map: maybe_not_map.symbol_str(), - }), - } - } - - pub fn sha256(self) -> Result { - match self { - Self::Bytes(bytes) => { - Ok(Self::Bytes(super::sha256(&bytes))) - } - elem => Err(ElemError::HashUnsupportedType(elem.symbol_str())), - } - } - - pub fn to_json(self) -> Result { - Ok(Self::Json(serde_json::to_value(self)?)) - } - - pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { - match (self, elem_symbol) { - (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), - (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), - (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), - (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), - (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), - (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), - (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { - json: json, - elem_symbol: From::from(elem_symbol), - }), - (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { - non_json: non_json.symbol_str(), - elem_symbol: From::from(elem_symbol), - }), - } - } - - pub fn string_to_bytes(self) -> Result { - match self { - Self::String(x) => Ok(Self::Bytes(x.into_bytes())), - other => Err(ElemError::StringToBytesUnsupportedType(other.symbol_str())), - } - } } -#[derive(Debug, PartialEq, Error)] -pub enum ElemError { - #[error("expected Elem::Bool(true), found {0:?}")] - AssertTrueUnsupportedType(Elem), - #[error("expected true, but found false")] - AssertTrueFailed(), - #[error("check_le: incomparable types: {lhs:?}; {rhs:?}")] - CheckLeIncomparableTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("check_lt: incomparable types: {lhs:?}; {rhs:?}")] - CheckLtIncomparableTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("check_eq: incomparable types: {lhs:?}; {rhs:?}")] - CheckEqIncomparableTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("concat applied to unsupported types: lhs: {lhs:?}; rhs: {rhs:?}")] - ConcatUnsupportedTypes { - lhs: &'static str, - rhs: &'static str, - }, - #[error("concat applied to strings that concatentate to invalid UTF8: lhs: {lhs:?}; rhs: {rhs:?}")] - ConcatInvalidUTF8 { - lhs: String, - rhs: String, - }, - - #[error("slice applied to unsupported types: maybe_not_offset: {maybe_not_offset:?}; maybe_not_length: {maybe_not_length:?}; maybe_not_iterable: {maybe_not_iterable:?}")] - SliceUnsupportedTypes { - maybe_not_offset: &'static str, - maybe_not_length: &'static str, - maybe_not_iterable: &'static str, - }, - #[error("slice applied to an 'offset' that can't be unpacked to u64: offset: {0:?}")] - SliceOffsetNotU64(Number), - #[error("slice applied to a 'length' that can't be unpacked to u64: length: {0:?}")] - SliceLengthNotU64(Number), - #[error("slice applied to an iterable that's too short for the given offset: offset: {offset:?} and length: {length:?}: iterable: {iterable:?}")] - SliceTooShort { - offset: usize, - length: usize, - iterable: &'static str, - }, - #[error("slice applied to offset and length whose sum overflows usize: offset: {offset:?} and length: {length:?}")] - SliceOverflow { - offset: Number, - length: Number, - }, - #[error("slice applied to arguments that produce invalid UTF8: offset: {offset:?}; length: {length:?}, iterator: {iterator:?}")] - SliceInvalidUTF8 { - offset: Number, - length: Number, - iterator: String, - }, - - #[error("index applied to unsupported types: maybe_not_index: {maybe_not_index:?}; maybe_not_iterable: {maybe_not_iterable:?}")] - IndexUnsupportedTypes { - maybe_not_index: &'static str, - maybe_not_iterable: &'static str, - }, - #[error("index applied to an 'index' that can't be unpacked to u64: {0:?}")] - IndexNotU64(Number), - #[error("index applied to an iterable that's too short for the given index: {index:?}; iterable: {iterable:?}")] - IndexTooShort { - index: usize, - iterable: &'static str, - }, - #[error("slice applied to offset and length whose sum overflows usize: {0:?}")] - IndexOverflow(Number), - - #[error("lookup applied to unsupported types: maybe_not_key: {maybe_not_key:?}; maybe_not_map: {maybe_not_map:?}")] - LookupUnsupportedTypes { - maybe_not_key: &'static str, - maybe_not_map: &'static str, - }, - #[error("lookup applied to a map that doesn't contain the given key: {key:?}; map: {map:?}")] - LookupKeyMissing { - key: String, - map: Map, - }, - - #[error("sha256 applied an Elem of an unsupported type ({0})")] - HashUnsupportedType(&'static str), - - #[error("to_json/from_json serialization failed: ({0})")] - ToFromJsonFailed(String), - - #[error("from_json applied an Elem of an unsupported type ({0})")] - FromJsonUnsupportedType(&'static str), - - #[error("object_from_json applied an Elem of an unsupported type ({0})")] - ObjectFromJsonUnsupportedType(&'static str), - #[error("object_from_json applied unexpected JSON: ({0})")] - ObjectFromJsonUnexpecteJson(Value), - - #[error("array_from_json applied an Elem of an unsupported type ({0})")] - ArrayFromJsonUnsupportedType(&'static str), - #[error("array_from_json applied unexpected JSON: ({0})")] - ArrayFromJsonUnexpecteJson(Value), - - #[error("string_from_json applied an Elem of an unsupported type ({0})")] - StringFromJsonUnsupportedType(&'static str), - #[error("string_from_json applied unexpected JSON: ({0})")] - StringFromJsonUnexpecteJson(Value), - - #[error("unpack_json applied to a value that's not raw JSON or it didn't match the expected type: {non_json:?}; type: {elem_symbol:?}")] - UnpackJsonUnexpectedType { - non_json: &'static str, - elem_symbol: &'static str, - }, - #[error("unpack_json applied to raw JSON and an unsupported type: {json:?}; type: {elem_symbol:?}")] - UnpackJsonUnsupportedSymbol { - json: serde_json::Value, - elem_symbol: &'static str, - }, - - #[error("string_to_bytes applied to an Elem of an unsupported type ({0})")] - StringToBytesUnsupportedType(&'static str), -} - -impl From for ElemError { - fn from(error: serde_json::Error) -> Self { - ElemError::ToFromJsonFailed(format!("{}", error)) - } -} - - diff --git a/src/executor.rs b/src/executor.rs index 4c7037e..2f37b8f 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,5 @@ use crate::restack::{Restack, RestackError}; -use crate::elem::{Elem, ElemError}; +use crate::elem::{Elem}; /* use crate::types::{Instruction, Instructions}; */ use crate::instruction::{Instruction, Instructions}; use thiserror::Error; @@ -10,107 +10,107 @@ pub struct Executor { } impl Executor { - pub fn consume(&mut self, expressions: Instructions) -> Result<(), ExecError> { - self.debug()?; - for expr in expressions { - println!("------------------------------------------------------------------------------------------"); - println!("#: {:?}", expr); - match expr { - Instruction::Restack(restack) => self.restack(restack)?, - Instruction::Push(elem) => self.push(elem), - - Instruction::AssertTrue => self.pop()?.assert_true()?, - - Instruction::HashSha256 => self.pop_push(Elem::sha256)?, - Instruction::ToJson => self.pop_push(Elem::to_json)?, - Instruction::UnpackJson(elem_symbol) => self.pop_push(|x| x.unpack_json(elem_symbol))?, - Instruction::StringToBytes => self.pop_push(Elem::string_to_bytes)?, - - Instruction::CheckLe => self.pop_push2(Elem::check_le)?, - Instruction::CheckLt => self.pop_push2(Elem::check_lt)?, - Instruction::CheckEq => self.pop_push2(Elem::check_eq)?, - Instruction::Concat => self.pop_push2(Elem::concat)?, - Instruction::Index => self.pop_push2(Elem::index)?, - Instruction::Lookup => self.pop_push2(Elem::lookup)?, - - Instruction::Slice => self.pop_push3(Elem::slice)?, - } - self.debug()?; - } - Ok(()) - } - - pub fn debug(&self) -> Result<(), ExecError> { - println!("------------------------------------------------------------------------------------------"); - for stack_elem in &self.stack { - println!("------------------------------"); - println!("{}", serde_json::to_string_pretty(stack_elem)?) - } - Ok(()) - } + // pub fn consume(&mut self, expressions: Instructions) -> Result<(), ExecError> { + // self.debug()?; + // for expr in expressions { + // println!("------------------------------------------------------------------------------------------"); + // println!("#: {:?}", expr); + // match expr { + // Instruction::Restack(restack) => self.restack(restack)?, + // Instruction::Push(elem) => self.push(elem), + + // Instruction::AssertTrue => self.pop()?.assert_true()?, + + // Instruction::HashSha256 => self.pop_push(Elem::sha256)?, + // Instruction::ToJson => self.pop_push(Elem::to_json)?, + // Instruction::UnpackJson(elem_symbol) => self.pop_push(|x| x.unpack_json(elem_symbol))?, + // Instruction::StringToBytes => self.pop_push(Elem::string_to_bytes)?, + + // Instruction::CheckLe => self.pop_push2(Elem::check_le)?, + // Instruction::CheckLt => self.pop_push2(Elem::check_lt)?, + // Instruction::CheckEq => self.pop_push2(Elem::check_eq)?, + // Instruction::Concat => self.pop_push2(Elem::concat)?, + // Instruction::Index => self.pop_push2(Elem::index)?, + // Instruction::Lookup => self.pop_push2(Elem::lookup)?, + + // Instruction::Slice => self.pop_push3(Elem::slice)?, + // } + // self.debug()?; + // } + // Ok(()) + // } + + // pub fn debug(&self) -> Result<(), ExecError> { + // println!("------------------------------------------------------------------------------------------"); + // for stack_elem in &self.stack { + // println!("------------------------------"); + // println!("{}", serde_json::to_string_pretty(stack_elem)?) + // } + // Ok(()) + // } fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { restack.run(&mut self.stack)?; Ok(()) } - // TODO: since pop can fail, require passing debug info to it - // (so we know what we were expecting) - fn pop(&mut self) -> Result { - let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; - self.stack = self.stack.drain(1..).collect(); - Ok(result.clone()) - } - - pub fn push(&mut self, elem: Elem) { - let mut memo = vec![elem]; - memo.append(&mut self.stack.clone()); - self.stack = memo; - } - - pub fn pop_push(&mut self, f: impl Fn(Elem) -> Result) -> Result<(), ExecError> { - let one = self.pop()?; - self.push(f(one)?); - Ok(()) - } - - pub fn pop_push2(&mut self, f: impl Fn(Elem, Elem) -> Result) -> Result<(), ExecError> { - let one = self.pop()?; - let other = self.pop()?; - self.push(f(one, other)?); - Ok(()) - } - - pub fn pop_push3(&mut self, f: impl Fn(Elem, Elem, Elem) -> Result) -> Result<(), ExecError> { - let first = self.pop()?; - let second = self.pop()?; - let third = self.pop()?; - self.push(f(first, second, third)?); - Ok(()) - } + // // TODO: since pop can fail, require passing debug info to it + // // (so we know what we were expecting) + // fn pop(&mut self) -> Result { + // let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; + // self.stack = self.stack.drain(1..).collect(); + // Ok(result.clone()) + // } + + // pub fn push(&mut self, elem: Elem) { + // let mut memo = vec![elem]; + // memo.append(&mut self.stack.clone()); + // self.stack = memo; + // } + + // pub fn pop_push(&mut self, f: impl Fn(Elem) -> Result) -> Result<(), ExecError> { + // let one = self.pop()?; + // self.push(f(one)?); + // Ok(()) + // } + + // pub fn pop_push2(&mut self, f: impl Fn(Elem, Elem) -> Result) -> Result<(), ExecError> { + // let one = self.pop()?; + // let other = self.pop()?; + // self.push(f(one, other)?); + // Ok(()) + // } + + // pub fn pop_push3(&mut self, f: impl Fn(Elem, Elem, Elem) -> Result) -> Result<(), ExecError> { + // let first = self.pop()?; + // let second = self.pop()?; + // let third = self.pop()?; + // self.push(f(first, second, third)?); + // Ok(()) + // } } #[derive(Debug, Error)] pub enum ExecError { - #[error("ElemError({0:?})")] - ElemError(ElemError), + // #[error("ElemError({0:?})")] + // ElemError(ElemError), #[error("tried to pop from an empty stack")] EmptyStack, #[error("restack failed: {0}")] RestackExecError(RestackError), } -impl From for ExecError { - fn from(error: ElemError) -> Self { - ExecError::ElemError(error) - } -} - -impl From for ExecError { - fn from(error: serde_json::Error) -> Self { - ExecError::ElemError(ElemError::ToFromJsonFailed(format!("{}", error))) - } -} +// impl From for ExecError { +// fn from(error: ElemError) -> Self { +// ExecError::ElemError(error) +// } +// } + +// impl From for ExecError { +// fn from(error: serde_json::Error) -> Self { +// ExecError::ElemError(ElemError::ToFromJsonFailed(format!("{}", error))) +// } +// } impl From for ExecError { fn from (error: RestackError) -> Self { diff --git a/src/main.rs b/src/main.rs index 49a5ac0..e6cb1be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,30 +71,6 @@ fn main() { } "#; - let mut instructions_vec_t_1 = Instrs::new(); - instructions_vec_t_1.instr(Push { push: true }); - instructions_vec_t_1.restack(Restack::id()); - instructions_vec_t_1.instr(AssertTrue {}); - - // : Instrs = Instrs { instrs: vec![ - // TEST #1 - // Arc::new(Push { push: true }), - - // Instruction::Push(Elem::Bool(true)), - // Instruction::Restack(Restack::id()), - // Instruction::AssertTrue, - - // ]}; - - let mut stack = Stack::new(); - let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); - stack.push_elem(input_json_value); - - format!("{:?}", instructions_vec_t_1.run(&mut stack)); - println!("FINAL STACK"); - println!("{:?}", stack); - - // let json_instructions = parse_json()" let instructions_vec: Vec = vec![ // TEST #1 @@ -255,4 +231,17 @@ fn main() { // println!("demo_triple_with_tl_handles_intermediate_types:"); // demo_triple_with_tl_handles_intermediate_types().fold((), |_memo, x| println!("{:?}", x)); + + let mut instructions_vec_t_1 = Instrs::new(); + instructions_vec_t_1.instr(Push { push: true }); + instructions_vec_t_1.restack(Restack::id()); + instructions_vec_t_1.instr(AssertTrue {}); + + let mut stack = Stack::new(); + let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); + stack.push_elem(input_json_value); + + format!("{:?}", instructions_vec_t_1.run(&mut stack)); + println!("FINAL STACK"); + println!("{:?}", stack); } diff --git a/src/types.rs b/src/types.rs index dfcc9a8..64d5337 100644 --- a/src/types.rs +++ b/src/types.rs @@ -37,182 +37,20 @@ impl Iterator for Nil { } } -pub trait ElemList: Clone + IntoIterator { - type Hd: AnElem; - type Tl: ElemList; - - fn is_empty(&self) -> bool; - fn hd(&self) -> Self::Hd; - fn tl(&self) -> Self::Tl; - fn cons(self, x: T) -> ConsElem where Self: Sized; - fn pop(x: PhantomData, stack: &mut Stack) -> Result; -} - -impl ElemList for Nil { - type Hd = (); - type Tl = Nil; - - fn is_empty(&self) -> bool { - true - } - - fn hd(&self) -> Self::Hd { - () - } - - fn tl(&self) -> Self::Tl { - Self {} - } - - fn cons(self, x: T) -> ConsElem - where - Self: Sized, - { - ConsElem { - hd: x, - tl: self, - } - } - - fn pop(_x: PhantomData, _stack: &mut Stack) -> Result { - Ok(Nil {}) - } -} - -#[derive(Clone, PartialEq, Eq)] -pub struct ConsElem { - hd: T, - tl: U, -} - -#[derive(Clone, PartialEq, Eq)] -pub struct IterConsElem { - cons: ConsElem, - at_head: bool, -} - -impl IntoIterator for ConsElem { - type Item = Elem; - type IntoIter = IterConsElem; - - fn into_iter(self) -> Self::IntoIter { - IterConsElem { - cons: self, - at_head: true, - } - } -} - -impl Iterator for IterConsElem { - type Item = Elem; - - fn next(&mut self) -> Option { - if self.at_head { - Some(self.cons.clone().hd.to_elem()) - } else { - let self_cons = self.cons.clone(); - *self = self_cons.into_iter(); - self.next() - } - } -} - -impl ElemList for ConsElem { - type Hd = T; - type Tl = U; - - fn is_empty(&self) -> bool { - false - } - - fn hd(&self) -> Self::Hd { - self.hd.clone() - } - - fn tl(&self) -> Self::Tl { - self.tl.clone() - } - - fn cons(self, x: V) -> ConsElem - where - Self: Sized, - { - ConsElem { - hd: x, - tl: self, - } - } - - // TODO: add better errors - fn pop(_x: PhantomData, stack: &mut Stack) -> Result { - let hd_elem = stack.pop()?; - Ok(ConsElem { - hd: ::from_elem(PhantomData, hd_elem)?, - tl: Self::Tl::pop(PhantomData, stack)?, - }) - } -} - - -pub trait FromElemList { - type AsElemList: ElemList; - fn to_hlist_type(x: PhantomData) -> PhantomData; - // fn to_hlist(&self) -> Self::AsElemList; - fn from_hlist(t: PhantomData, x: Self::AsElemList) -> Self; -} - -impl FromElemList for () { - type AsElemList = Nil; - fn to_hlist_type(_x: PhantomData) -> PhantomData { PhantomData } - fn from_hlist(_t: PhantomData, _x: Self::AsElemList) -> Self { () } -} - -impl FromElemList for (T, U) -where - T: AnElem, - U: AnElem, -{ - type AsElemList = ConsElem>; - fn to_hlist_type(_x: PhantomData) -> PhantomData { PhantomData } - // fn to_hlist(&self) -> Self::AsElemList { - // Nil.cons(self.0).cons(self.1) - // } - - fn from_hlist(_t: PhantomData, x: Self::AsElemList) -> Self { - (x.hd(), x.tl().hd()) - } -} - -impl FromElemList for ConsElem -where - T: AnElem, - U: ElemList, -{ - type AsElemList = Self; - fn to_hlist_type(_x: PhantomData) -> PhantomData { PhantomData } - // fn to_hlist(&self) -> Self::AsElemList { - // Nil.cons(self.0).cons(self.1) - // } - - fn from_hlist(_t: PhantomData, x: Self::AsElemList) -> Self { x } -} - - - // TODO: add necessary traits, methods and rename to IsInstructionError or IsInstrError pub trait AnError: std::fmt::Debug { // fn to_stack_error(&self, line_no: LineNo) -> StackError; } -impl StackError { - fn instruction_default(name: &str, error_str: &str, line_no: LineNo) -> Self { - Self::RunInstruction { - name: name.to_string(), - error: error_str.to_string(), - line_no: line_no, - } - } -} +// impl StackError { +// fn instruction_default(name: &str, error_str: &str, line_no: LineNo) -> Self { +// Self::RunInstruction { +// name: name.to_string(), +// error: error_str.to_string(), +// line_no: line_no, +// } +// } +// } impl AnError for Empty { // fn to_stack_error(&self, _line_no: LineNo) -> StackError { @@ -220,222 +58,6 @@ impl AnError for Empty { // } } -// TODO: add Default, etc -pub trait IsInstruction: std::fmt::Debug { - type In: FromElemList; - type Out: AnElem; - type Error: AnError; - - fn run(&self, x: Self::In) -> Result; -} - -impl Stack { - fn run_instruction(&mut self, instr: T, line_no: LineNo) -> Result<(), StackError> { - let input = ElemList::pop(FromElemList::to_hlist_type(PhantomData::), self)?; - let output = instr.run(FromElemList::from_hlist(PhantomData::, input)).map_err(|e| StackError::RunInstruction { - name: format!("{:?}", instr), - error: format!("{:?}", e), - line_no: line_no})?; - Ok(self.push(output.to_elem())) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct AssertTrue {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct AssertTrueError {} -impl AnError for AssertTrueError {} - -impl IsInstruction for AssertTrue { - type In = ConsElem; - type Out = bool; - type Error = AssertTrueError; - - fn run(&self, x: Self::In) -> Result { - if x.hd() { - Ok(x.hd()) - } else { - Err(AssertTrueError {}) - } - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Push { - push: T, -} - -impl IsInstruction for Push { - type In = (); - type Out = T; - type Error = Empty; - - fn run(&self, _x: Self::In) -> Result { - Ok(self.push.clone()) - } -} - - - - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct HashSha256 {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct HashSha256Error {} -impl AnError for HashSha256Error {} - -impl IsInstruction for HashSha256 { - type In = ConsElem, Nil>; - type Out = Vec; - type Error = Empty; - - fn run(&self, x: Self::In) -> Result { - Ok(super::sha256(&x.hd())) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Concat { - t: PhantomData, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct ConcatError {} -impl AnError for ConcatError {} - -impl::Item>> IsInstruction for Concat { - type In = (T, T); - type Out = T; - type Error = Empty; - - fn run(&self, x: Self::In) -> Result { - let (lhs, rhs) = x; - Ok(lhs.into_iter().chain(rhs.into_iter()).collect()) - } -} - - // pub fn concat(self, other: Self) -> Result { - // match (self, other) { - // (Self::Bytes(x), Self::Bytes(y)) => Ok(Self::Bytes(Self::concat_generic(x, y))), - // (Self::String(x), Self::String(y)) => { - // Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) - // .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) - // }, - // (Self::Array(x), Self::Array(y)) => Ok(Self::Array(Self::concat_generic(x, y))), - // (Self::Object(x), Self::Object(y)) => Ok(Self::Object(Self::concat_generic(x, y))), - // (some_x, some_y) => { - // Err(ElemError::ConcatUnsupportedTypes { - // lhs: some_x.symbol_str(), - // rhs: some_y.symbol_str() - // }) - // }, - // } - // } - - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Slice {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct SliceError {} -impl AnError for SliceError {} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Index {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct IndexError {} -impl AnError for IndexError {} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Lookup {} -#[derive(Clone, Debug, PartialEq, Eq)] -struct LookupError { - key: String, - map: Map, -} -impl AnError for LookupError {} - -impl IsInstruction for Lookup { - type In = (String, Map); - type Out = Value; - type Error = LookupError; - - fn run(&self, x: Self::In) -> Result { - let (key, map) = x; - Ok(map.get(&key) - .ok_or_else(|| LookupError { - key: key, - map: map.clone(), - })?.clone()) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct UnpackJson { - t: PhantomData, -} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct UnpackJsonError {} -impl AnError for UnpackJsonError {} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct StringToBytes {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct StringToBytesError {} -impl AnError for StringToBytesError {} - - - -// TODO: POLYMORPHIC W/ ANY: PERHAPS Elem: AnElem ?? -// -// ideas: -// 1. use macros when it's a trait -// 2. gradual typing: allow Elem to be AnElem - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckLe {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckLeError {} -impl AnError for CheckLeError {} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckLt {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckLtError {} -impl AnError for CheckLtError {} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckEq {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct CheckEqError {} -impl AnError for CheckEqError {} - -// pub fn demo_triple() -> ConsElem<(), TBool, ConsElem<(), TUnit, ConsElem<(), TBool, Nil<()>>>> { -// Nil { t: PhantomData } -// .cons(TBool { get_bool: true }) -// .cons(TUnit { }) -// .cons(TBool { get_bool: false }) -// } - -// pub fn demo_triple_with_tl_handles_intermediate_types() -> ConsElem<(), TBool, ConsElem<(), TUnit, ConsElem<(), TBool, Nil<()>>>> { -// Nil { t: PhantomData } -// .cons(TBool { get_bool: true }) -// .cons(TUnit { }) -// .cons(TBool { get_bool: false }) -// .cons(TBool { get_bool: true }) -// .cons(TUnit { }) -// .tl() -// .tl() -// } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 8699260..609f42e 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -56,13 +56,6 @@ where } } - -// impl AnElem for Singleton { -// fn elem_symbol(_t: PhantomData) -> EnumSet { ::elem_symbol(PhantomData) } -// fn to_elem(self) -> Elem { self.t.to_elem() } -// fn from_elem(_t: PhantomData, x: Elem) -> Result { ::from_elem(PhantomData, x).map(|y| { Singleton { t: y, } }) } -// } - #[derive(Clone, Debug, Error)] pub enum ElemsPopError { #[error("Elems::pop singleton: tried to pop an Elem that was not found: {error:?}")] From 18e25133e85d318c799051241ff6115c57338c3c Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 16 Mar 2022 11:47:12 -0400 Subject: [PATCH 52/77] debugging type errors: added AJsonElem for rest of types, recreating main example w/ new type API --- src/lib.rs | 2 +- src/main.rs | 135 ++++++++++++++++++++++++++++++++++++++++++- src/stack.rs | 9 +++ src/types_scratch.rs | 104 +++++++++++++++++++++++++-------- 4 files changed, 223 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c7aa8b7..c1fa868 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ mod stack; pub use stack::{Stack, StackError}; mod types; mod types_scratch; -pub use types_scratch::{Instrs, AssertTrue, Push}; +pub use types_scratch::{Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq}; // pub use types::{Instruction, Instructions}; // , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList mod instruction; diff --git a/src/main.rs b/src/main.rs index e6cb1be..02cdfc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; -use cryptoscript::{Stack, Instrs, AssertTrue, Push}; +use cryptoscript::{Stack, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq}; // use cryptoscript::{demo_triple, demo_triple_with_tl_handles_intermediate_types, HList}; +use std::marker::PhantomData; +use serde_json::{Map, Number, Value}; #[cfg(test)] mod tests { @@ -237,11 +239,140 @@ fn main() { instructions_vec_t_1.restack(Restack::id()); instructions_vec_t_1.instr(AssertTrue {}); + // let mut stack = Stack::new(); + // let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); + // stack.push_elem(input_json_value); + + // println!("{:?}", instructions_vec_t_1.run(&mut stack)); + // println!("FINAL STACK"); + // println!("{:?}", stack); + + let mut instructions_vec_t_2 = Instrs::new(); + + // x["queries"] + instructions_vec_t_2.instr(Push { push: "queries".to_string() }); + instructions_vec_t_2.instr(Lookup {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); + + // x[0] + let zero: Number = From::from(0u8); + instructions_vec_t_2.instr(Push { push: zero }); + instructions_vec_t_2.instr(Index {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); + + // x["action"] = "tokenbalance" + instructions_vec_t_2.restack(Restack::dup()); + instructions_vec_t_2.instr(Push { push: "action".to_string() }); + instructions_vec_t_2.instr(Lookup {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); + instructions_vec_t_2.instr(CheckEq {}); + instructions_vec_t_2.instr(AssertTrue {}); + + // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + instructions_vec_t_2.restack(Restack::dup()); + instructions_vec_t_2.instr(Push { push: "contractaddress".to_string() }); + instructions_vec_t_2.instr(Lookup {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); + instructions_vec_t_2.instr(Push { push: "0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string() }); + instructions_vec_t_2.instr(CheckEq {}); + instructions_vec_t_2.instr(AssertTrue {}); + + // // x["response"]["result"] = "135499" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("response".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("result".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("135499".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["prompts"] + // Instruction::Restack(Restack::drop()), + // Instruction::Push(Elem::String("prompts".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Array), + + // // x[0] + // Instruction::Push(Elem::Number(From::from(0u8))), + // Instruction::Index, + // Instruction::UnpackJson(ElemSymbol::Object), + + // // x["action"] = "siwe" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("action".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("siwe".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["version"] = "1.1.0" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("version".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("1.1.0".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("data".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("fields".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("address".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), + // Instruction::CheckEq, + // Instruction::AssertTrue, + + // // sha256(x["data"]["message"]) + // Instruction::Restack(Restack::dup()), + // Instruction::Push(Elem::String("data".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("message".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::StringToBytes, + // Instruction::HashSha256, + + // // sha256(x["data"]["fields"]["address"]) + // Instruction::Restack(Restack::swap()), + // Instruction::Push(Elem::String("data".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("fields".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::Object), + // Instruction::Push(Elem::String("address".to_string())), + // Instruction::Lookup, + // Instruction::UnpackJson(ElemSymbol::String), + // Instruction::StringToBytes, + // Instruction::HashSha256, + + // // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = + // // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] + // Instruction::Concat, + // Instruction::HashSha256, + // Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), + // Instruction::CheckEq, + // Instruction::AssertTrue, + let mut stack = Stack::new(); let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); stack.push_elem(input_json_value); - format!("{:?}", instructions_vec_t_1.run(&mut stack)); + println!("instructions:\n{:?}", instructions_vec_t_2); + + println!("{:?}", instructions_vec_t_2.run(&mut stack)); println!("FINAL STACK"); println!("{:?}", stack); } diff --git a/src/stack.rs b/src/stack.rs index acd8481..a26120e 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -69,6 +69,15 @@ impl Stack { } GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) } + + pub fn debug(&self) -> Result<(), serde_json::Error> { + println!("------------------------------------------------------------------------------------------"); + for stack_elem in &self.stack { + println!("------------------------------"); + println!("{}", serde_json::to_string_pretty(stack_elem)?) + } + Ok(()) + } } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 609f42e..2528d83 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -774,6 +774,8 @@ pub enum InstructionError { }, RestackError(RestackError), + + DebugJsonError(serde_json::Error), } pub trait IsStackInstruction: Debug { @@ -843,6 +845,9 @@ impl Instrs { pub fn run(&self, stack: &mut Stack) -> Result<(), InstructionError> { for instr_or_restack in &self.instrs { + stack.debug().map_err(|e| InstructionError::DebugJsonError(e))?; + println!("------------------------------------------------------------------------------------------"); + println!("#: {:?}\n", instr_or_restack); match instr_or_restack { Ok(instr) => instr.stack_run(stack)?, Err(restack) => restack.run(&mut stack.stack) @@ -918,6 +923,7 @@ impl AnError for AssertTrueError {} impl IsInstructionT for AssertTrue { type IO = ConsOut, Nil>; + // TODO: replace w/ Empty type Error = AssertTrueError; fn name(_x: PhantomData) -> String { @@ -1215,21 +1221,20 @@ impl IsInstructionT for Lookup { #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct UnpackJson { - t: PhantomData, +pub struct UnpackJson { + pub t: PhantomData, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct UnpackJsonError {} +pub struct UnpackJsonError {} impl AnError for UnpackJsonError {} -// TODO: implement for rest of types trait AJsonElem: AnElem { - fn to_value(&self) -> Value; + fn to_value(self) -> Value; fn from_value(t: PhantomData, x: Value) -> Option; } impl AJsonElem for () { - fn to_value(&self) -> Value { + fn to_value(self) -> Value { Value::Null } @@ -1241,24 +1246,70 @@ impl AJsonElem for () { } } - // pub fn unpack_json(self, elem_symbol: ElemSymbol) -> Result { - // match (self, elem_symbol) { - // (Self::Json(serde_json::Value::Null), ElemSymbol::Unit) => Ok(Self::Unit), - // (Self::Json(serde_json::Value::Bool(x)), ElemSymbol::Bool) => Ok(Self::Bool(x)), - // (Self::Json(serde_json::Value::Number(x)), ElemSymbol::Number) => Ok(Self::Number(x)), - // (Self::Json(serde_json::Value::String(x)), ElemSymbol::String) => Ok(Self::String(x)), - // (Self::Json(serde_json::Value::Array(x)), ElemSymbol::Array) => Ok(Self::Array(x)), - // (Self::Json(serde_json::Value::Object(x)), ElemSymbol::Object) => Ok(Self::Object(x)), - // (Self::Json(json), elem_symbol) => Err(ElemError::UnpackJsonUnsupportedSymbol { - // json: json, - // elem_symbol: From::from(elem_symbol), - // }), - // (non_json, _) => Err(ElemError::UnpackJsonUnexpectedType { - // non_json: non_json.symbol_str(), - // elem_symbol: From::from(elem_symbol), - // }), - // } - // } +impl AJsonElem for bool { + fn to_value(self) -> Value { + Value::Bool(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option { + match x { + Value::Bool(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for Number { + fn to_value(self) -> Value { + Value::Number(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option { + match x { + Value::Number(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for String { + fn to_value(self) -> Value { + Value::String(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option { + match x { + Value::String(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for Vec { + fn to_value(self) -> Value { + Value::Array(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option { + match x { + Value::Array(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for Map { + fn to_value(self) -> Value { + Value::Object(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option { + match x { + Value::Object(y) => Some(y), + _ => None, + } + } +} impl IsInstructionT for UnpackJson { type IO = ConsOut, @@ -1505,3 +1556,8 @@ impl IsInstructionT for CheckEq { // } // } + + + + + From 244ecefe9b980794868fe2158762db187b611186 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 16 Mar 2022 14:17:47 -0400 Subject: [PATCH 53/77] added debug info to stack type and popping from stack --- src/elem.rs | 3 +- src/executor.rs | 4 +- src/main.rs | 6 ++- src/stack.rs | 5 +++ src/types.rs | 9 ++-- src/types_scratch.rs | 102 +++++++++++++++++++++++++++++++++++++++---- 6 files changed, 111 insertions(+), 18 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 5d28f37..15848ff 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -3,9 +3,8 @@ use crate::arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; use thiserror::Error; use std::cmp; -use std::convert::TryFrom; use std::marker::PhantomData; -use std::iter::{FromIterator, IntoIterator}; +use std::iter::{IntoIterator}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; diff --git a/src/executor.rs b/src/executor.rs index 2f37b8f..6687609 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,7 +1,7 @@ use crate::restack::{Restack, RestackError}; use crate::elem::{Elem}; -/* use crate::types::{Instruction, Instructions}; */ -use crate::instruction::{Instruction, Instructions}; +// use crate::types::{Instruction, Instructions}; +// use crate::instruction::{Instruction, Instructions}; use thiserror::Error; #[derive(Debug, Default)] diff --git a/src/main.rs b/src/main.rs index 02cdfc3..3fadcdc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -370,7 +370,11 @@ fn main() { let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); stack.push_elem(input_json_value); - println!("instructions:\n{:?}", instructions_vec_t_2); + println!("instructions:"); + for instruction in &instructions_vec_t_2.instrs { + println!("{:?}", instruction); + } + println!(""); println!("{:?}", instructions_vec_t_2.run(&mut stack)); println!("FINAL STACK"); diff --git a/src/stack.rs b/src/stack.rs index a26120e..3bc5cf7 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -70,7 +70,12 @@ impl Stack { GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) } + pub fn debug_type(&self) -> () { + println!("type: {:?}", &self.stack.clone().into_iter().map(|x| x.symbol()).collect::>()) + } + pub fn debug(&self) -> Result<(), serde_json::Error> { + self.debug_type(); println!("------------------------------------------------------------------------------------------"); for stack_elem in &self.stack { println!("------------------------------"); diff --git a/src/types.rs b/src/types.rs index 64d5337..5261f14 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,10 +1,11 @@ // use crate::restack::{RestackError}; -use crate::elem::{AnElem, Elem, ElemSymbol}; -use crate::stack::{Stack, StackError, LineNo, Location}; +use crate::elem::{Elem, ElemSymbol}; + +// Stack, StackError, LineNo, +use crate::stack::{Location}; use std::collections::BTreeMap; use std::cmp; -use std::iter::{FromIterator}; use std::fmt; use std::fmt::{Display, Formatter}; @@ -14,7 +15,7 @@ use std::marker::PhantomData; use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; -use serde_json::{Map, Number, Value}; +// use serde_json::{Map, Number, Value}; use thiserror::Error; #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 2528d83..53bca36 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,4 +1,4 @@ -use crate::elem::{Elem, AnElem}; +use crate::elem::{Elem, ElemSymbol, AnElem}; use crate::stack::{Stack, StackError}; use crate::restack::{Restack, RestackError}; use crate::types::{Empty, AnError, Nil}; @@ -11,7 +11,7 @@ use std::fmt::Debug; use std::sync::{Arc, Mutex}; use std::string::FromUtf8Error; -// use enumset::EnumSet; +use enumset::EnumSet; use generic_array::functional::FunctionalSequence; use generic_array::sequence::GenericSequence; use generic_array::typenum::{U0, U1, U2}; @@ -71,7 +71,32 @@ pub enum ElemsPopError { // TODO: add detail #[error("Elems::pop: generic_array internal error")] - GenericArray, + GenericArray { + elem_set: EnumSet, + vec: Vec, + size: usize, + }, + + #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{elem_set:?}\n{stack:?}\n{error:?}")] + IsListHd { + stack_type: Result>, Arc>, + elem_set: Result, Arc>, + stack: Stack, + error: Arc, + }, + + #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{stack:?}\n{error:?}")] + IsListTl { + stack_type: Result>, Arc>, + stack: Stack, + error: Arc, + }, + + #[error("Elems::elem_symbols (Or): Set includes repeated type: {elem_symbols_hd:?}\n{elem_symbols_tl:?}")] + ElemSymbolsIntersect { + elem_symbols_hd: EnumSet, + elem_symbols_tl: EnumSet, + } } impl From for ElemsPopError { @@ -98,6 +123,8 @@ pub trait Elems: Clone + Debug + IntoIterator { fn pop(_x: PhantomData, stack: &mut Stack) -> Result where Self: Sized; + + fn elem_symbols(t: PhantomData) -> Result, ElemsPopError>; } pub trait IElems: Elems {} @@ -166,13 +193,21 @@ where .pop_elem(PhantomData::) .map_err(|e| >::from(e)) }).collect::, ElemsPopError>>()?; - let array = GenericArray::from_exact_iter(vec).ok_or_else(|| { - ElemsPopError::GenericArray + let array = GenericArray::from_exact_iter(vec.clone()).ok_or_else(|| { + ElemsPopError::GenericArray { + elem_set: AnElem::elem_symbol(PhantomData::), + vec: vec.into_iter().map(|x| x.to_elem()).collect(), + size: ::to_usize(), + } })?; Ok(Singleton { array: array, }) } + + fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + Ok(AnElem::elem_symbol(PhantomData::)) + } } impl IElems for Singleton @@ -279,6 +314,20 @@ where }, } } + + fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + let mut elem_symbols_hd = AnElem::elem_symbol(PhantomData::); + let elem_symbols_tl = Elems::elem_symbols(PhantomData::)?; + if elem_symbols_hd.is_disjoint(elem_symbols_tl) { + elem_symbols_hd.union(elem_symbols_tl); + Ok(elem_symbols_hd) + } else { + Err(ElemsPopError::ElemSymbolsIntersect { + elem_symbols_hd: elem_symbols_hd, + elem_symbols_tl: elem_symbols_tl, + }) + } + } } impl IElems for Or @@ -394,6 +443,10 @@ where }, }) } + + fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + Elems::elem_symbols(PhantomData::>) + } } impl IOElems for ReturnSingleton @@ -482,6 +535,9 @@ where } + fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + Elems::elem_symbols(PhantomData::>) + } } impl IOElems for ReturnOr @@ -539,7 +595,8 @@ pub trait IsList: Clone + Debug + IntoIterator { } } - // TODO: wrap ElemsError w/ whole stack, position in stack, etc + fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError>; + fn pop(_x: PhantomData, stack: &mut Stack) -> Result where Self: Sized, @@ -547,8 +604,18 @@ pub trait IsList: Clone + Debug + IntoIterator { match ::empty_list() { Some(x) => Ok(x), None => { - let x = ::pop(PhantomData, stack)?; - let xs = ::pop(PhantomData, stack)?; + let original_stack = stack.clone(); + let x = ::pop(PhantomData, stack).map_err(|e| ElemsPopError::IsListHd { + stack_type: IsList::elem_symbols_vec(PhantomData::).map_err(|e| Arc::new(e)), + elem_set: Elems::elem_symbols(PhantomData::).map_err(|e| Arc::new(e)), + stack: original_stack.clone(), + error: Arc::new(e), + })?; + let xs = ::pop(PhantomData, stack).map_err(|e| ElemsPopError::IsListTl { + stack_type: IsList::elem_symbols_vec(PhantomData::).map_err(|e| Arc::new(e)), + stack: original_stack.clone(), + error: Arc::new(e), + })?; Ok(::cons_list(x, xs)) } } @@ -580,6 +647,10 @@ impl IsList for Nil { fn tl(self) -> Self::Tl { Self {} } + + fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError> { + Ok(vec![]) + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -639,6 +710,13 @@ impl IsList for Cons { fn tl(self) -> Self::Tl { self.tl } + + fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError> { + let elem_symbols_hd = Elems::elem_symbols(PhantomData::)?; + let mut elem_symbols_vec_tl = IsList::elem_symbols_vec(PhantomData::)?; + elem_symbols_vec_tl.insert(0, elem_symbols_hd); + Ok(elem_symbols_vec_tl) + } } @@ -733,6 +811,10 @@ where fn tl(self) -> Self::Tl { self.cons.tl() } + + fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError> { + IsList::elem_symbols_vec(PhantomData::>) + } } impl IOList for ConsOut @@ -1199,7 +1281,9 @@ pub struct LookupError { impl AnError for LookupError {} impl IsInstructionT for Lookup { - type IO = ConsOut, Cons, Cons, U1>, Nil>>>; + type IO = ConsOut, + Cons, + Cons, U1>, Nil>>>; type Error = LookupError; fn name(_x: PhantomData) -> String { From c155477808cef1fab509696b198ba90306e3dbc9 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 16 Mar 2022 17:13:32 -0400 Subject: [PATCH 54/77] WIP: replacing EnumSet's with ElemType's --- src/elem.rs | 179 ++++++++++++++++++++ src/instruction.rs | 380 +++++++++++++++++++++---------------------- src/stack.rs | 2 +- src/types.rs | 190 ++-------------------- src/types_scratch.rs | 134 +++++++++++---- 5 files changed, 482 insertions(+), 403 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 15848ff..b229c82 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -1,8 +1,11 @@ use crate::arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; +use crate::stack::{Location}; use thiserror::Error; use std::cmp; +use std::fmt; +use std::fmt::{Display, Formatter}; use std::marker::PhantomData; use std::iter::{IntoIterator}; @@ -196,11 +199,187 @@ impl Elem { +/* #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] */ +/* pub enum BaseElemType { */ +/* Any, */ +/* Concat, */ +/* Index, */ +/* Slice, */ +/* ElemSymbol(ElemSymbol), */ +/* } */ + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemTypeInfo { + /* base_elem_type: BaseElemType, */ + location: Location, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemType { + type_set: EnumSet, + info: Vec, +} + +// Formatting: +// ``` +// ElemType { +// type_set: {A, B, C}, +// info: _, +// } +// ``` +// +// Results in: +// ``` +// {A, B, C} +// ``` +impl Display for ElemType { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{{{}}}", + self.type_set.iter() + .fold(String::new(), + |memo, x| { + let x_str: &'static str = From::from(x); + if memo == "" { + x_str.to_string() + } else { + memo + ", " + &x_str.to_string() + } + } + )) + } +} + +#[cfg(test)] +mod elem_type_display_tests { + use super::*; + + #[test] + fn test_empty() { + let elem_type = ElemType { + type_set: EnumSet::empty(), + info: vec![], + }; + assert_eq!("{}", format!("{}", elem_type)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), + format!("{}", elem_type)); + } + } + + #[test] + fn test_all() { + assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", + format!("{}", ElemType::any(vec![]))); + } +} + +impl ElemSymbol { + pub fn elem_type(&self, locations: Vec) -> ElemType { + ElemType { + type_set: EnumSet::only(*self), + info: locations.iter() + .map(|&location| + ElemTypeInfo { + // base_elem_type: BaseElemType::ElemSymbol(*self), + location: location, + }).collect(), + } + } +} + +impl Elem { + pub fn elem_type(&self, locations: Vec) -> ElemType { + self.symbol().elem_type(locations) + } +} + +impl ElemType { + fn from_locations(type_set: EnumSet, + // base_elem_type: BaseElemType, + locations: Vec) -> Self { + ElemType { + type_set: type_set, + info: locations.iter() + .map(|&location| + ElemTypeInfo { + // base_elem_type: base_elem_type, + location: location, + }).collect(), + } + } + + pub fn any(locations: Vec) -> Self { + Self::from_locations( + EnumSet::all(), + // BaseElemType::Any, + locations) + } + + // pub fn concat_type(locations: Vec) -> Self { + // Self::from_locations( + // enum_set!(ElemSymbol::Bytes | + // ElemSymbol::String | + // ElemSymbol::Array | + // ElemSymbol::Object), + // // BaseElemType::Concat, + // locations) + // } + + // pub fn index_type(locations: Vec) -> Self { + // Self::from_locations( + // enum_set!(ElemSymbol::Array | + // ElemSymbol::Object), + // // BaseElemType::Index, + // locations) + // } + + // pub fn slice_type(locations: Vec) -> Self { + // Self::concat_type(locations) + // } + + pub fn unify(&self, other: Self) -> Result { + let both = self.type_set.intersection(other.type_set); + if both.is_empty() { + Err(ElemTypeError::UnifyEmpty { + lhs: self.clone(), + rhs: other.clone(), + }) + } else { + let mut both_info = self.info.clone(); + both_info.append(&mut other.info.clone()); + Ok(ElemType { + type_set: both, + info: both_info, + }) + } + } +} + +#[derive(Debug, PartialEq, Error)] +pub enum ElemTypeError { + #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] + UnifyEmpty { + lhs: ElemType, + rhs: ElemType, + // location: TyUnifyLocation, + }, +} + pub trait AnElem: Clone + std::fmt::Debug + PartialEq { + // fn elem_symbol(t: PhantomData) -> ElemType; fn elem_symbol(t: PhantomData) -> EnumSet; fn to_elem(self) -> Elem; fn from_elem(t: PhantomData, x: Elem) -> Result; diff --git a/src/instruction.rs b/src/instruction.rs index 0378b0c..9b44b28 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,7 +1,7 @@ use crate::restack::{Restack, RestackError}; -use crate::elem::{Elem, ElemSymbol}; +use crate::elem::{Elem, ElemSymbol, ElemType}; use crate::stack::{LineNo}; -use crate::types::{ElemType, TypeId, Context, Type, TypeError}; +use crate::types::{TypeId, Context, Type, TypeError}; // use std::collections::BTreeMap; // use std::cmp; @@ -81,179 +81,179 @@ impl Restack { } } -/// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] -/// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] -/// HashSha256, // : [ bytes ] -> [ bytes ] -/// CheckLe, // : [ x, x ] -> [ bool ] -/// CheckLt, // : [ x, x ] -> [ bool ] -/// CheckEq, // : [ x, x ] -> [ bool ] -/// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] -/// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] -/// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] -/// Lookup, // [ string, object ] -> [ json ] -/// AssertTrue, // [ bool ] -> [] -/// ToJson, // (t: type) : [ t ] -> [ json ] -/// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] -/// StringToBytes, // [ string ] -> [ bytes ] -impl Instruction { - pub fn type_of(&self, line_no: LineNo) -> Result { - match self { - Instruction::Restack(restack) => - Ok(restack - .type_of(line_no) - .or_else(|e| Err(InstructionTypeError::InstructionTypeOfRestack(e)))?), - - Instruction::AssertTrue => { - let mut context = Context::new(); - let bool_var = context - .push(ElemSymbol::Bool - .elem_type(vec![line_no.in_at(0)])); - Ok(Type { - context: context, - i_type: vec![bool_var], - o_type: vec![], - }) - }, - - Instruction::Push(elem) => { - let mut context = Context::new(); - let elem_var = context - .push(elem.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![], - o_type: vec![elem_var], - }) - }, - - Instruction::HashSha256 => { - let mut context = Context::new(); - let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.in_at(0), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![bytes_var], - o_type: vec![bytes_var], - }) - }, - - Instruction::ToJson => { - let mut context = Context::new(); - let any_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_var], - o_type: vec![json_var], - }) - }, - - Instruction::StringToBytes => { - let mut context = Context::new(); - let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); - let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![string_var], - o_type: vec![bytes_var], - }) - }, - - Instruction::UnpackJson(elem_symbol) => { - let mut context = Context::new(); - let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.in_at(0)])); - let elem_symbol_var = context.push(elem_symbol.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![json_var], - o_type: vec![elem_symbol_var], - }) - }, - - Instruction::CheckLe => { - let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); - let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_lhs_var, any_rhs_var], - o_type: vec![bool_var], - }) - }, - - Instruction::CheckLt => { - let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); - let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_lhs_var, any_rhs_var], - o_type: vec![bool_var], - }) - }, - - Instruction::CheckEq => { - let mut context = Context::new(); - let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); - let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); - let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![any_lhs_var, any_rhs_var], - o_type: vec![bool_var], - }) - }, - - Instruction::Concat => { - let mut context = Context::new(); - let concat_var = context.push(ElemType::concat_type(vec![line_no.in_at(0), line_no.in_at(1), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![concat_var, concat_var], - o_type: vec![concat_var], - }) - }, - - Instruction::Index => { - let mut context = Context::new(); - let number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); - let index_var = context.push(ElemType::index_type(vec![line_no.in_at(1), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![number_var, index_var], - o_type: vec![index_var], - }) - }, - - Instruction::Lookup => { - let mut context = Context::new(); - let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); - let object_var = context.push(ElemSymbol::Object.elem_type(vec![line_no.in_at(1), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![string_var, object_var], - o_type: vec![object_var], - }) - }, - - Instruction::Slice => { - let mut context = Context::new(); - let offset_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); - let length_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(1)])); - let slice_var = context.push(ElemType::slice_type(vec![line_no.in_at(2), line_no.out_at(0)])); - Ok(Type { - context: context, - i_type: vec![offset_number_var, length_number_var, slice_var], - o_type: vec![slice_var], - }) - }, - }.or_else(|e| Err(InstructionTypeError::InstructionTypeOfDetail { - instruction: self.clone(), - error: Box::new(e), - })) - } -} +// /// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] +// /// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] +// /// HashSha256, // : [ bytes ] -> [ bytes ] +// /// CheckLe, // : [ x, x ] -> [ bool ] +// /// CheckLt, // : [ x, x ] -> [ bool ] +// /// CheckEq, // : [ x, x ] -> [ bool ] +// /// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] +// /// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] +// /// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] +// /// Lookup, // [ string, object ] -> [ json ] +// /// AssertTrue, // [ bool ] -> [] +// /// ToJson, // (t: type) : [ t ] -> [ json ] +// /// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] +// /// StringToBytes, // [ string ] -> [ bytes ] +// impl Instruction { +// pub fn type_of(&self, line_no: LineNo) -> Result { +// match self { +// Instruction::Restack(restack) => +// Ok(restack +// .type_of(line_no) +// .or_else(|e| Err(InstructionTypeError::InstructionTypeOfRestack(e)))?), + +// Instruction::AssertTrue => { +// let mut context = Context::new(); +// let bool_var = context +// .push(ElemSymbol::Bool +// .elem_type(vec![line_no.in_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![bool_var], +// o_type: vec![], +// }) +// }, + +// Instruction::Push(elem) => { +// let mut context = Context::new(); +// let elem_var = context +// .push(elem.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![], +// o_type: vec![elem_var], +// }) +// }, + +// Instruction::HashSha256 => { +// let mut context = Context::new(); +// let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.in_at(0), line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![bytes_var], +// o_type: vec![bytes_var], +// }) +// }, + +// Instruction::ToJson => { +// let mut context = Context::new(); +// let any_var = context.push(ElemType::any(vec![line_no.in_at(0)])); +// let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![any_var], +// o_type: vec![json_var], +// }) +// }, + +// Instruction::StringToBytes => { +// let mut context = Context::new(); +// let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); +// let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![string_var], +// o_type: vec![bytes_var], +// }) +// }, + +// Instruction::UnpackJson(elem_symbol) => { +// let mut context = Context::new(); +// let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.in_at(0)])); +// let elem_symbol_var = context.push(elem_symbol.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![json_var], +// o_type: vec![elem_symbol_var], +// }) +// }, + +// Instruction::CheckLe => { +// let mut context = Context::new(); +// let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); +// let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); +// let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![any_lhs_var, any_rhs_var], +// o_type: vec![bool_var], +// }) +// }, + +// Instruction::CheckLt => { +// let mut context = Context::new(); +// let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); +// let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); +// let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![any_lhs_var, any_rhs_var], +// o_type: vec![bool_var], +// }) +// }, + +// Instruction::CheckEq => { +// let mut context = Context::new(); +// let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); +// let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); +// let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![any_lhs_var, any_rhs_var], +// o_type: vec![bool_var], +// }) +// }, + +// Instruction::Concat => { +// let mut context = Context::new(); +// let concat_var = context.push(ElemType::concat_type(vec![line_no.in_at(0), line_no.in_at(1), line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![concat_var, concat_var], +// o_type: vec![concat_var], +// }) +// }, + +// Instruction::Index => { +// let mut context = Context::new(); +// let number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); +// let index_var = context.push(ElemType::index_type(vec![line_no.in_at(1), line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![number_var, index_var], +// o_type: vec![index_var], +// }) +// }, + +// Instruction::Lookup => { +// let mut context = Context::new(); +// let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); +// let object_var = context.push(ElemSymbol::Object.elem_type(vec![line_no.in_at(1), line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![string_var, object_var], +// o_type: vec![object_var], +// }) +// }, + +// Instruction::Slice => { +// let mut context = Context::new(); +// let offset_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); +// let length_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(1)])); +// let slice_var = context.push(ElemType::slice_type(vec![line_no.in_at(2), line_no.out_at(0)])); +// Ok(Type { +// context: context, +// i_type: vec![offset_number_var, length_number_var, slice_var], +// o_type: vec![slice_var], +// }) +// }, +// }.or_else(|e| Err(InstructionTypeError::InstructionTypeOfDetail { +// instruction: self.clone(), +// error: Box::new(e), +// })) +// } +// } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] @@ -270,21 +270,21 @@ impl IntoIterator for Instructions { } } -impl Instructions { - pub fn type_of(&self) -> Result { - let mut current_type = Type::id(); - for (i, instruction) in self.instructions.iter().enumerate() { - current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) - .or_else(|e| Err(InstructionTypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location - line_no: i, - error: Box::new(e), - }))?; - - println!("line {i}: {current_type}", i = i, current_type = current_type); - } - Ok(current_type) - } -} +// impl Instructions { +// pub fn type_of(&self) -> Result { +// let mut current_type = Type::id(); +// for (i, instruction) in self.instructions.iter().enumerate() { +// current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) +// .or_else(|e| Err(InstructionTypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location +// line_no: i, +// error: Box::new(e), +// }))?; + +// println!("line {i}: {current_type}", i = i, current_type = current_type); +// } +// Ok(current_type) +// } +// } // Test program #1: [] -> [] // diff --git a/src/stack.rs b/src/stack.rs index 3bc5cf7..a241ca2 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -71,7 +71,7 @@ impl Stack { } pub fn debug_type(&self) -> () { - println!("type: {:?}", &self.stack.clone().into_iter().map(|x| x.symbol()).collect::>()) + println!("stack type: {:?}", &self.stack.clone().into_iter().map(|x| x.symbol()).collect::>()) } pub fn debug(&self) -> Result<(), serde_json::Error> { diff --git a/src/types.rs b/src/types.rs index 5261f14..0d546bf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,8 +1,8 @@ // use crate::restack::{RestackError}; -use crate::elem::{Elem, ElemSymbol}; +use crate::elem::{Elem, ElemType, ElemTypeError}; // Stack, StackError, LineNo, -use crate::stack::{Location}; +// use crate::stack::{Location}; use std::collections::BTreeMap; use std::cmp; @@ -13,8 +13,8 @@ use std::fmt::{Display, Formatter}; use std::marker::PhantomData; // use std::sync::Arc; -use enumset::{EnumSet, enum_set}; -use serde::{Deserialize, Serialize}; +// use enumset::{EnumSet, enum_set}; +// use serde::{Deserialize, Serialize}; // use serde_json::{Map, Number, Value}; use thiserror::Error; @@ -186,171 +186,6 @@ impl AnError for Empty { // + property tests for typing methods themselves // + test that a function having a particular type -> it runs w/o type errors on such inputs -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum BaseElemType { - Any, - Concat, - Index, - Slice, - ElemSymbol(ElemSymbol), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct ElemTypeInfo { - base_elem_type: BaseElemType, - location: Location, -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct ElemType { - type_set: EnumSet, - info: Vec, -} - -// Formatting: -// ``` -// ElemType { -// type_set: {A, B, C}, -// info: _, -// } -// ``` -// -// Results in: -// ``` -// {A, B, C} -// ``` -impl Display for ElemType { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, - "{{{}}}", - self.type_set.iter() - .fold(String::new(), - |memo, x| { - let x_str: &'static str = From::from(x); - if memo == "" { - x_str.to_string() - } else { - memo + ", " + &x_str.to_string() - } - } - )) - } -} - -#[cfg(test)] -mod elem_type_display_tests { - use super::*; - - #[test] - fn test_empty() { - let elem_type = ElemType { - type_set: EnumSet::empty(), - info: vec![], - }; - assert_eq!("{}", format!("{}", elem_type)); - } - - #[test] - fn test_singleton() { - for elem_symbol in EnumSet::all().iter() { - let elem_type = ElemType { - type_set: EnumSet::only(elem_symbol), - info: vec![], - }; - assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), - format!("{}", elem_type)); - } - } - - #[test] - fn test_all() { - assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", - format!("{}", ElemType::any(vec![]))); - } -} - -impl ElemSymbol { - pub fn elem_type(&self, locations: Vec) -> ElemType { - ElemType { - type_set: EnumSet::only(*self), - info: locations.iter() - .map(|&location| - ElemTypeInfo { - base_elem_type: BaseElemType::ElemSymbol(*self), - location: location, - }).collect(), - } - } -} - -impl Elem { - pub fn elem_type(&self, locations: Vec) -> ElemType { - self.symbol().elem_type(locations) - } -} - -impl ElemType { - fn from_locations(type_set: EnumSet, - base_elem_type: BaseElemType, - locations: Vec) -> Self { - ElemType { - type_set: type_set, - info: locations.iter() - .map(|&location| - ElemTypeInfo { - base_elem_type: base_elem_type, - location: location, - }).collect(), - } - } - - pub fn any(locations: Vec) -> Self { - Self::from_locations( - EnumSet::all(), - BaseElemType::Any, - locations) - } - - pub fn concat_type(locations: Vec) -> Self { - Self::from_locations( - enum_set!(ElemSymbol::Bytes | - ElemSymbol::String | - ElemSymbol::Array | - ElemSymbol::Object), - BaseElemType::Concat, - locations) - } - - pub fn index_type(locations: Vec) -> Self { - Self::from_locations( - enum_set!(ElemSymbol::Array | - ElemSymbol::Object), - BaseElemType::Index, - locations) - } - - pub fn slice_type(locations: Vec) -> Self { - Self::concat_type(locations) - } - - pub fn unify(&self, other: Self) -> Result { - let both = self.type_set.intersection(other.type_set); - if both.is_empty() { - Err(ElemTypeError::UnifyEmpty { - lhs: self.clone(), - rhs: other.clone(), - }) - } else { - let mut both_info = self.info.clone(); - both_info.append(&mut other.info.clone()); - Ok(ElemType { - type_set: both, - info: both_info, - }) - } - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct TypeId { type_id: usize, @@ -691,6 +526,13 @@ impl Type { o_type: self.o_type.iter().chain(mut_offset_other.o_type.iter().skip(zip_len)).copied().collect(), }) } + + pub fn prepend_inputs(&mut self, num_copies: usize, elem_type: ElemType) -> () { + if 0 < num_copies { + let type_id = self.context.push(elem_type); + self.i_type = (1..num_copies).into_iter().map(|_| type_id).chain(self.i_type.into_iter()).collect() + } + } } // Formatting: @@ -794,16 +636,6 @@ mod type_display_tests { // TODO: add layers of detail to TypeIdMapGetUnknownTypeId -#[derive(Debug, PartialEq, Error)] -pub enum ElemTypeError { - #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] - UnifyEmpty { - lhs: ElemType, - rhs: ElemType, - // location: TyUnifyLocation, - }, -} - #[derive(Debug, PartialEq, Error)] pub enum TypeIdMapError { #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 53bca36..9884978 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,7 +1,7 @@ -use crate::elem::{Elem, ElemSymbol, AnElem}; +use crate::elem::{Elem, ElemType, ElemSymbol, AnElem}; use crate::stack::{Stack, StackError}; use crate::restack::{Restack, RestackError}; -use crate::types::{Empty, AnError, Nil}; +use crate::types::{Type, Empty, AnError, Nil}; use std::cmp; use std::convert::TryFrom; @@ -79,23 +79,23 @@ pub enum ElemsPopError { #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{elem_set:?}\n{stack:?}\n{error:?}")] IsListHd { - stack_type: Result>, Arc>, - elem_set: Result, Arc>, + stack_type: Result, Arc>, + elem_set: Result>, stack: Stack, error: Arc, }, #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{stack:?}\n{error:?}")] IsListTl { - stack_type: Result>, Arc>, + stack_type: Result, Arc>, stack: Stack, error: Arc, }, #[error("Elems::elem_symbols (Or): Set includes repeated type: {elem_symbols_hd:?}\n{elem_symbols_tl:?}")] ElemSymbolsIntersect { - elem_symbols_hd: EnumSet, - elem_symbols_tl: EnumSet, + elem_symbols_hd: ElemType, + elem_symbols_tl: ElemType, } } @@ -116,7 +116,7 @@ pub trait Elems: Clone + Debug + IntoIterator { // fn right(s: PhantomData, x: Self::Tl) -> Self; fn or) -> T, G: Fn(&Self::Tl) -> T>(&self, f: F, g: G) -> T; - // fn elem_symbols(t: PhantomData) -> EnumSet; + // fn elem_symbols(t: PhantomData) -> ElemType; // fn to_elems(self) -> Elem; // fn from_elems(t: PhantomData, x: &mut Stack) -> Result; @@ -124,7 +124,10 @@ pub trait Elems: Clone + Debug + IntoIterator { where Self: Sized; - fn elem_symbols(t: PhantomData) -> Result, ElemsPopError>; + fn elem_symbols(t: PhantomData) -> Result; + + // outputting nothing + // fn type_of_elems(t: PhantomData) -> ; } pub trait IElems: Elems {} @@ -205,11 +208,54 @@ where }) } - fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + fn elem_symbols(_t: PhantomData) -> Result { Ok(AnElem::elem_symbol(PhantomData::)) } } + +// // TODO: relocate LineNo, ArgumentIndex, Location +// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// pub struct LineNo { +// pub line_no: usize, +// } + +// impl From for LineNo { +// fn from(line_no: usize) -> Self { +// LineNo { +// line_no: line_no, +// } +// } +// } + +// pub type ArgumentIndex = usize; + +// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// pub struct Location { +// line_no: LineNo, +// argument_index: ArgumentIndex, +// is_input: bool, +// } + +// impl LineNo { +// pub fn in_at(&self, argument_index: usize) -> Location { +// Location { +// line_no: *self, +// argument_index: argument_index, +// is_input: true, +// } +// } + +// pub fn out_at(&self, argument_index: usize) -> Location { +// Location { +// line_no: *self, +// argument_index: argument_index, +// is_input: false, +// } +// } +// } + + impl IElems for Singleton where T: AnElem, @@ -315,19 +361,20 @@ where } } - fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { - let mut elem_symbols_hd = AnElem::elem_symbol(PhantomData::); - let elem_symbols_tl = Elems::elem_symbols(PhantomData::)?; - if elem_symbols_hd.is_disjoint(elem_symbols_tl) { - elem_symbols_hd.union(elem_symbols_tl); - Ok(elem_symbols_hd) - } else { - Err(ElemsPopError::ElemSymbolsIntersect { - elem_symbols_hd: elem_symbols_hd, - elem_symbols_tl: elem_symbols_tl, - }) - } - } + // TODO: use ElemType::unify + // fn elem_symbols(_t: PhantomData) -> Result { + // let mut elem_symbols_hd = AnElem::elem_symbol(PhantomData::); + // let elem_symbols_tl = Elems::elem_symbols(PhantomData::)?; + // if elem_symbols_hd.is_disjoint(elem_symbols_tl) { + // elem_symbols_hd.union(elem_symbols_tl); + // Ok(elem_symbols_hd) + // } else { + // Err(ElemsPopError::ElemSymbolsIntersect { + // elem_symbols_hd: elem_symbols_hd, + // elem_symbols_tl: elem_symbols_tl, + // }) + // } + // } } impl IElems for Or @@ -444,7 +491,7 @@ where }) } - fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + fn elem_symbols(_t: PhantomData) -> Result { Elems::elem_symbols(PhantomData::>) } } @@ -535,7 +582,7 @@ where } - fn elem_symbols(_t: PhantomData) -> Result, ElemsPopError> { + fn elem_symbols(_t: PhantomData) -> Result { Elems::elem_symbols(PhantomData::>) } } @@ -595,7 +642,7 @@ pub trait IsList: Clone + Debug + IntoIterator { } } - fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError>; + fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError>; fn pop(_x: PhantomData, stack: &mut Stack) -> Result where @@ -648,7 +695,7 @@ impl IsList for Nil { Self {} } - fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError> { + fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError> { Ok(vec![]) } } @@ -711,7 +758,7 @@ impl IsList for Cons { self.tl } - fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError> { + fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError> { let elem_symbols_hd = Elems::elem_symbols(PhantomData::)?; let mut elem_symbols_vec_tl = IsList::elem_symbols_vec(PhantomData::)?; elem_symbols_vec_tl.insert(0, elem_symbols_hd); @@ -742,6 +789,7 @@ pub trait IOList: IsList { type Return: IOElems; fn returning(&self) -> Option; + fn type_of(t: PhantomData) -> Result; } impl IOList for Cons @@ -754,6 +802,15 @@ where fn returning(&self) -> Option { self.tl.returning() } + + // TODO: test + fn type_of(t: PhantomData) -> Result { + let num_elem_symbols_hd = <::N as Unsigned>::to_usize(); + let elem_symbols_hd = Elems::elem_symbols(PhantomData::)?; + let mut type_tl = IOList::type_of(PhantomData::)?; + type_tl.prepend_inputs(num_elem_symbols_hd, elem_symbols_hd); + Ok(type_tl) + } } @@ -812,7 +869,7 @@ where self.cons.tl() } - fn elem_symbols_vec(t: PhantomData) -> Result>, ElemsPopError> { + fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError> { IsList::elem_symbols_vec(PhantomData::>) } } @@ -862,6 +919,7 @@ pub enum InstructionError { pub trait IsStackInstruction: Debug { fn name(&self) -> String; + fn type_of(&self) -> Result; fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError>; } @@ -873,6 +931,10 @@ where IsInstructionT::name(PhantomData::) } + fn type_of(&self) -> Result { + IOList::type_of(PhantomData::<::IO>) + } + fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError> { let stack_input = &IsList::pop(PhantomData::<::IO>, stack) .map_err(|e| InstructionError::ElemsPopError(e))?; @@ -931,9 +993,15 @@ impl Instrs { println!("------------------------------------------------------------------------------------------"); println!("#: {:?}\n", instr_or_restack); match instr_or_restack { - Ok(instr) => instr.stack_run(stack)?, - Err(restack) => restack.run(&mut stack.stack) - .map_err(|e| InstructionError::RestackError(e))?, + Ok(instr) => { + println!("instr: {:?}\n", instr.type_of()); + instr.stack_run(stack)? + }, + Err(restack) => { + println!("restack: {:?}\n", restack); + restack.run(&mut stack.stack) + .map_err(|e| InstructionError::RestackError(e))? + }, } } Ok(()) @@ -1613,7 +1681,7 @@ impl IsInstructionT for CheckEq { // impl AnElem for Or { -// fn elem_symbol(_t: PhantomData) -> EnumSet { +// fn elem_symbol(_t: PhantomData) -> ElemType { // let t_set = ::elem_symbol(PhantomData); // let u_set = ::elem_symbol(PhantomData); // t_set.union(u_set) From cccbb1b1453fbbb63a62213115e69be56e1c6e8a Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 17 Mar 2022 15:38:28 -0400 Subject: [PATCH 55/77] WIP: include debug info w/ elem sets and propagate --- src/elem.rs | 9 ++- src/types.rs | 48 +++++++++++----- src/types_scratch.rs | 131 +++++++++++++++++++++++++++---------------- 3 files changed, 123 insertions(+), 65 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index b229c82..3fede80 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -214,10 +214,11 @@ pub struct ElemTypeInfo { location: Location, } +// TODO: make fields private? #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ElemType { - type_set: EnumSet, - info: Vec, + pub type_set: EnumSet, + pub info: Vec, } // Formatting: @@ -364,7 +365,7 @@ impl ElemType { } } -#[derive(Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error)] pub enum ElemTypeError { #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] UnifyEmpty { @@ -379,6 +380,8 @@ pub enum ElemTypeError { pub trait AnElem: Clone + std::fmt::Debug + PartialEq { + // TODO: rename + // fn elem_symbol(t: PhantomData) -> ElemType; fn elem_symbol(t: PhantomData) -> EnumSet; fn to_elem(self) -> Elem; diff --git a/src/types.rs b/src/types.rs index 0d546bf..3235a1c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,7 +11,7 @@ use std::fmt; use std::fmt::{Display, Formatter}; // use std::alloc::string; use std::marker::PhantomData; -// use std::sync::Arc; +use std::sync::Arc; // use enumset::{EnumSet, enum_set}; // use serde::{Deserialize, Serialize}; @@ -389,7 +389,7 @@ impl Context { Ok(self.context.get(index).ok_or_else(|| ContextError::GetUnknownTypeId { context: self.clone(), index: *index, - error: Box::new(error()), + error: Arc::new(error()), })?.clone()) } @@ -417,6 +417,18 @@ impl Context { self.context.insert(yi, xy_type); Ok(()) } + + // maximum possible, not maximum present + pub fn max_type_id(&self) -> Result { + let type_id = self.next_type_id.type_id; + if type_id == 0 { + Err(ContextError::MaxTypeId(self.clone())) + } else { + Ok(TypeId { + type_id: type_id - 1, + }) + } + } } @@ -527,11 +539,20 @@ impl Type { }) } - pub fn prepend_inputs(&mut self, num_copies: usize, elem_type: ElemType) -> () { - if 0 < num_copies { - let type_id = self.context.push(elem_type); - self.i_type = (1..num_copies).into_iter().map(|_| type_id).chain(self.i_type.into_iter()).collect() - } + // generic iterable? + pub fn prepend_inputs(&mut self, elem_types: Vec) -> () { + + // for elem_type in elem_types { + // let type_id = self.context.push(elem_type); + // self.i_type = + + // _ + // } + + // if 0 < num_copies { + // let type_id = self.context.push(elem_type); + // self.i_type = (1..num_copies).into_iter().map(|_| type_id).chain(self.i_type.into_iter()).collect() + // } } } @@ -632,11 +653,7 @@ mod type_display_tests { -// TODO: split up TypeError -// TODO: add layers of detail to TypeIdMapGetUnknownTypeId - - -#[derive(Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error)] pub enum TypeIdMapError { #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] GetUnknownTypeId { @@ -653,13 +670,13 @@ pub enum TypeIdMapError { }, } -#[derive(Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error)] pub enum ContextError { #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}, error: {error:?}")] GetUnknownTypeId { context: Context, index: TypeId, - error: Box, + error: Arc, }, #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, / @@ -712,6 +729,9 @@ pub enum ContextError { #[error("Context::normalize_on building TypeIdMap failed: {0:?}")] TypeIdMapError(TypeIdMapError), + + #[error("Context::max_type_id: next_type_id == 0: {0:?}")] + MaxTypeId(Context), } impl From for ContextError { diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 9884978..b7b334d 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,7 +1,7 @@ -use crate::elem::{Elem, ElemType, ElemSymbol, AnElem}; +use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, AnElem}; use crate::stack::{Stack, StackError}; use crate::restack::{Restack, RestackError}; -use crate::types::{Type, Empty, AnError, Nil}; +use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; use std::cmp; use std::convert::TryFrom; @@ -92,11 +92,11 @@ pub enum ElemsPopError { error: Arc, }, - #[error("Elems::elem_symbols (Or): Set includes repeated type: {elem_symbols_hd:?}\n{elem_symbols_tl:?}")] - ElemSymbolsIntersect { - elem_symbols_hd: ElemType, - elem_symbols_tl: ElemType, - } + #[error("Elems::elem_type (Or): Set includes repeated type: {0:?}")] + ElemTypeError(ElemTypeError), + + #[error("::type_of(): ContextError when adding type: {0:?}")] + ReturnOrContextError(ContextError), } impl From for ElemsPopError { @@ -116,7 +116,6 @@ pub trait Elems: Clone + Debug + IntoIterator { // fn right(s: PhantomData, x: Self::Tl) -> Self; fn or) -> T, G: Fn(&Self::Tl) -> T>(&self, f: F, g: G) -> T; - // fn elem_symbols(t: PhantomData) -> ElemType; // fn to_elems(self) -> Elem; // fn from_elems(t: PhantomData, x: &mut Stack) -> Result; @@ -124,10 +123,7 @@ pub trait Elems: Clone + Debug + IntoIterator { where Self: Sized; - fn elem_symbols(t: PhantomData) -> Result; - - // outputting nothing - // fn type_of_elems(t: PhantomData) -> ; + fn elem_type(t: PhantomData) -> Result; } pub trait IElems: Elems {} @@ -168,6 +164,8 @@ pub trait IOElems: Elems { // TODO: rename to 'returned' to match Return fn returning(&self) -> Option; + + fn type_of(t: PhantomData) -> Result; } @@ -208,8 +206,12 @@ where }) } - fn elem_symbols(_t: PhantomData) -> Result { - Ok(AnElem::elem_symbol(PhantomData::)) + // TODO: add info + fn elem_type(_t: PhantomData) -> Result { + Ok(ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }) } } @@ -361,20 +363,16 @@ where } } - // TODO: use ElemType::unify - // fn elem_symbols(_t: PhantomData) -> Result { - // let mut elem_symbols_hd = AnElem::elem_symbol(PhantomData::); - // let elem_symbols_tl = Elems::elem_symbols(PhantomData::)?; - // if elem_symbols_hd.is_disjoint(elem_symbols_tl) { - // elem_symbols_hd.union(elem_symbols_tl); - // Ok(elem_symbols_hd) - // } else { - // Err(ElemsPopError::ElemSymbolsIntersect { - // elem_symbols_hd: elem_symbols_hd, - // elem_symbols_tl: elem_symbols_tl, - // }) - // } - // } + // TODO: add info + fn elem_type(_t: PhantomData) -> Result { + let elem_type_hd = ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }; + let elem_type_tl = Elems::elem_type(PhantomData::)?; + elem_type_hd.unify(elem_type_tl) + .map_err(|e| ElemsPopError::ElemTypeError(e)) + } } impl IElems for Or @@ -491,8 +489,8 @@ where }) } - fn elem_symbols(_t: PhantomData) -> Result { - Elems::elem_symbols(PhantomData::>) + fn elem_type(_t: PhantomData) -> Result { + Elems::elem_type(PhantomData::>) } } @@ -512,6 +510,20 @@ where fn returning(&self) -> Option { self.returning.returned().map(|x| x.to_elem()) } + + fn type_of(t: PhantomData) -> Result { + let num_inputs = ::to_usize(); + let mut context = Context::new(); + let type_id = context.push(ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }); + Ok(Type { + context: context, + i_type: (1..num_inputs).into_iter().map(|_| type_id).collect(), + o_type: vec![type_id], + }) + } } @@ -582,8 +594,8 @@ where } - fn elem_symbols(_t: PhantomData) -> Result { - Elems::elem_symbols(PhantomData::>) + fn elem_type(_t: PhantomData) -> Result { + Elems::elem_type(PhantomData::>) } } @@ -614,6 +626,20 @@ where Self::Right(x) => x.returning(), } } + + // TODO: add error info + fn type_of(t: PhantomData) -> Result { + let mut type_tl = IOElems::type_of(PhantomData::)?; + let last_type_id = type_tl.context.max_type_id() + .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; + let next_type_id = type_tl.context.push(ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }); + type_tl.context.unify(last_type_id, next_type_id) + .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; + Ok(type_tl) + } } @@ -642,7 +668,7 @@ pub trait IsList: Clone + Debug + IntoIterator { } } - fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError>; + fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError>; fn pop(_x: PhantomData, stack: &mut Stack) -> Result where @@ -653,13 +679,13 @@ pub trait IsList: Clone + Debug + IntoIterator { None => { let original_stack = stack.clone(); let x = ::pop(PhantomData, stack).map_err(|e| ElemsPopError::IsListHd { - stack_type: IsList::elem_symbols_vec(PhantomData::).map_err(|e| Arc::new(e)), - elem_set: Elems::elem_symbols(PhantomData::).map_err(|e| Arc::new(e)), + stack_type: IsList::elem_type_vec(PhantomData::).map_err(|e| Arc::new(e)), + elem_set: Elems::elem_type(PhantomData::).map_err(|e| Arc::new(e)), stack: original_stack.clone(), error: Arc::new(e), })?; let xs = ::pop(PhantomData, stack).map_err(|e| ElemsPopError::IsListTl { - stack_type: IsList::elem_symbols_vec(PhantomData::).map_err(|e| Arc::new(e)), + stack_type: IsList::elem_type_vec(PhantomData::).map_err(|e| Arc::new(e)), stack: original_stack.clone(), error: Arc::new(e), })?; @@ -695,7 +721,7 @@ impl IsList for Nil { Self {} } - fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError> { + fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError> { Ok(vec![]) } } @@ -758,11 +784,11 @@ impl IsList for Cons { self.tl } - fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError> { - let elem_symbols_hd = Elems::elem_symbols(PhantomData::)?; - let mut elem_symbols_vec_tl = IsList::elem_symbols_vec(PhantomData::)?; - elem_symbols_vec_tl.insert(0, elem_symbols_hd); - Ok(elem_symbols_vec_tl) + fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError> { + let elem_type_hd = Elems::elem_type(PhantomData::)?; + let mut elem_type_vec_tl = IsList::elem_type_vec(PhantomData::)?; + elem_type_vec_tl.insert(0, elem_type_hd); + Ok(elem_type_vec_tl) } } @@ -805,10 +831,11 @@ where // TODO: test fn type_of(t: PhantomData) -> Result { - let num_elem_symbols_hd = <::N as Unsigned>::to_usize(); - let elem_symbols_hd = Elems::elem_symbols(PhantomData::)?; + let num_elem_type_hd = <::N as Unsigned>::to_usize(); + let elem_type_hd = Elems::elem_type(PhantomData::)?; let mut type_tl = IOList::type_of(PhantomData::)?; - type_tl.prepend_inputs(num_elem_symbols_hd, elem_symbols_hd); + + type_tl.prepend_inputs(num_elem_type_hd, elem_type_hd).collect()); Ok(type_tl) } } @@ -869,8 +896,8 @@ where self.cons.tl() } - fn elem_symbols_vec(t: PhantomData) -> Result, ElemsPopError> { - IsList::elem_symbols_vec(PhantomData::>) + fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError> { + IsList::elem_type_vec(PhantomData::>) } } @@ -884,9 +911,17 @@ where fn returning(&self) -> Option { self.cons.hd.returning() } -} + // TODO: add info to errors + fn type_of(t: PhantomData) -> Result { + // let num_elem_type_hd = <::N as Unsigned>::to_usize(); + let type_hd = IOElems::type_of(PhantomData::)?; + let elem_type_tl = IsList::elem_type_vec(PhantomData::)?; + type_hd.prepend_inputs(elem_type_tl); + Ok(type_hd) + } +} From 757902dce8d39cc95954ad4a61b19cf2125a7e5c Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 18 Mar 2022 15:29:48 -0400 Subject: [PATCH 56/77] calculate type at each instruction evaluation and include w/ debug info --- src/main.rs | 5 ++-- src/types.rs | 60 ++++++++++++++++++++++++++++++++++---------- src/types_scratch.rs | 36 ++++++++++++++++++-------- 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3fadcdc..dc68ef9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -376,7 +376,8 @@ fn main() { } println!(""); - println!("{:?}", instructions_vec_t_2.run(&mut stack)); + println!("{:?}\n", instructions_vec_t_2.run(&mut stack)); + println!("FINAL STACK"); - println!("{:?}", stack); + stack.debug(); } diff --git a/src/types.rs b/src/types.rs index 3235a1c..504b1ef 100644 --- a/src/types.rs +++ b/src/types.rs @@ -418,6 +418,11 @@ impl Context { Ok(()) } + pub fn unify_elem_type(&mut self, xi: TypeId, elem_type: ElemType) -> Result<(), ContextError> { + let yi = self.push(elem_type); + self.unify(xi, yi) + } + // maximum possible, not maximum present pub fn max_type_id(&self) -> Result { let type_id = self.next_type_id.type_id; @@ -489,6 +494,24 @@ impl Type { }) } + pub fn specialize_to_input_stack(&mut self, stack_type: Vec) -> Result<(), TypeError> { + if self.i_type.len() <= stack_type.len() { + let mut stack_type_iter = stack_type.into_iter(); + for (type_id, elem_type) in self.i_type.clone().into_iter().zip(&mut stack_type_iter) { + self.context.unify_elem_type(type_id, elem_type).map_err(|e| TypeError::ContextError(e))? + } + for elem_type in stack_type_iter { + self.context.push(elem_type); + } + Ok(()) + } else { + Err(TypeError::SpecializeToInputStack { + type_of: self.clone(), + stack_type: stack_type, + }) + } + } + // f : self // g : other // self.compose(other) : (f ++ g).type_of() @@ -539,21 +562,26 @@ impl Type { }) } - // generic iterable? - pub fn prepend_inputs(&mut self, elem_types: Vec) -> () { - - // for elem_type in elem_types { - // let type_id = self.context.push(elem_type); - // self.i_type = - - // _ - // } + pub fn prepend_inputs(&mut self, num_copies: usize, elem_type: ElemType) -> () { + if 0 < num_copies { + let type_id = self.context.push(elem_type); + self.i_type = (1..num_copies).into_iter() + .map(|_| type_id) + .chain(self.i_type.clone().into_iter()) + .collect() + } + } - // if 0 < num_copies { - // let type_id = self.context.push(elem_type); - // self.i_type = (1..num_copies).into_iter().map(|_| type_id).chain(self.i_type.into_iter()).collect() - // } + pub fn append_inputs(&mut self, elem_types: T) -> () + where + T: IntoIterator, + { + for elem_type in elem_types { + let type_id = self.context.push(elem_type); + self.i_type.push(type_id) + } } + } // Formatting: @@ -754,6 +782,12 @@ pub enum TypeError { #[error("Type::normalize applying TypeIdMap failed: {0:?}")] TypeIdMapError(TypeIdMapError), + + #[error("Type::specialize_to_input_stack: stack_type shorter than expected:\n{type_of}\n{stack_type:?}")] + SpecializeToInputStack { + type_of: Type, + stack_type: Vec, + }, } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index b7b334d..bb29b60 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -511,7 +511,7 @@ where self.returning.returned().map(|x| x.to_elem()) } - fn type_of(t: PhantomData) -> Result { + fn type_of(_t: PhantomData) -> Result { let num_inputs = ::to_usize(); let mut context = Context::new(); let type_id = context.push(ElemType { @@ -628,7 +628,7 @@ where } // TODO: add error info - fn type_of(t: PhantomData) -> Result { + fn type_of(_t: PhantomData) -> Result { let mut type_tl = IOElems::type_of(PhantomData::)?; let last_type_id = type_tl.context.max_type_id() .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; @@ -721,7 +721,7 @@ impl IsList for Nil { Self {} } - fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError> { + fn elem_type_vec(_t: PhantomData) -> Result, ElemsPopError> { Ok(vec![]) } } @@ -784,7 +784,7 @@ impl IsList for Cons { self.tl } - fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError> { + fn elem_type_vec(_t: PhantomData) -> Result, ElemsPopError> { let elem_type_hd = Elems::elem_type(PhantomData::)?; let mut elem_type_vec_tl = IsList::elem_type_vec(PhantomData::)?; elem_type_vec_tl.insert(0, elem_type_hd); @@ -830,12 +830,12 @@ where } // TODO: test - fn type_of(t: PhantomData) -> Result { + fn type_of(_t: PhantomData) -> Result { let num_elem_type_hd = <::N as Unsigned>::to_usize(); let elem_type_hd = Elems::elem_type(PhantomData::)?; let mut type_tl = IOList::type_of(PhantomData::)?; - type_tl.prepend_inputs(num_elem_type_hd, elem_type_hd).collect()); + type_tl.prepend_inputs(num_elem_type_hd, elem_type_hd); Ok(type_tl) } } @@ -896,7 +896,7 @@ where self.cons.tl() } - fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError> { + fn elem_type_vec(_t: PhantomData) -> Result, ElemsPopError> { IsList::elem_type_vec(PhantomData::>) } } @@ -913,12 +913,12 @@ where } // TODO: add info to errors - fn type_of(t: PhantomData) -> Result { + fn type_of(_t: PhantomData) -> Result { // let num_elem_type_hd = <::N as Unsigned>::to_usize(); - let type_hd = IOElems::type_of(PhantomData::)?; + let mut type_hd = IOElems::type_of(PhantomData::)?; let elem_type_tl = IsList::elem_type_vec(PhantomData::)?; - type_hd.prepend_inputs(elem_type_tl); + type_hd.append_inputs(elem_type_tl); Ok(type_hd) } } @@ -1029,7 +1029,21 @@ impl Instrs { println!("#: {:?}\n", instr_or_restack); match instr_or_restack { Ok(instr) => { - println!("instr: {:?}\n", instr.type_of()); + let mut instr_type = instr.type_of(); + stack.debug_type(); + match instr_type { + Ok(instr_type) => { + println!("instr: {}\n", instr_type); + let mut mut_instr_type = instr_type.clone(); + match mut_instr_type.specialize_to_input_stack(stack.clone().stack.into_iter().map(|x| x.elem_type(vec![])).collect()) { + Ok(specialized) => println!("specialized: {}\n", mut_instr_type), + Err(e) => println!("specialization failed: {:?}\n", e), + } + }, + Err(e) => println!("instr type_of errror: {:?}\n", e), + } + println!(""); + instr.stack_run(stack)? }, Err(restack) => { From 9a7cb9e3049d013e7ffe1e5ab6a17f923382d21d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 18 Mar 2022 16:11:08 -0400 Subject: [PATCH 57/77] prettier-printed errors -> found and fixed off-by-one error --- src/elem.rs | 2 +- src/main.rs | 54 ++++++++++++++++++++++++-------------------- src/restack.rs | 2 +- src/stack.rs | 5 +++- src/types.rs | 33 +++++++++++++++++---------- src/types_scratch.rs | 34 +++++++++++++++++++--------- 6 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 3fede80..15dc2bc 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -367,7 +367,7 @@ impl ElemType { #[derive(Clone, Debug, PartialEq, Error)] pub enum ElemTypeError { - #[error("ElemType::unify applied to non-intersecting types: lhs: {lhs:?}; rhs: {rhs:?}")] + #[error("ElemType::unify applied to non-intersecting types:\nlhs:\n{lhs}\nrhs:\n{rhs}")] UnifyEmpty { lhs: ElemType, rhs: ElemType, diff --git a/src/main.rs b/src/main.rs index dc68ef9..1ae64d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -250,32 +250,33 @@ fn main() { let mut instructions_vec_t_2 = Instrs::new(); // x["queries"] + instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); instructions_vec_t_2.instr(Push { push: "queries".to_string() }); instructions_vec_t_2.instr(Lookup {}); instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); - // x[0] - let zero: Number = From::from(0u8); - instructions_vec_t_2.instr(Push { push: zero }); - instructions_vec_t_2.instr(Index {}); - instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); - - // x["action"] = "tokenbalance" - instructions_vec_t_2.restack(Restack::dup()); - instructions_vec_t_2.instr(Push { push: "action".to_string() }); - instructions_vec_t_2.instr(Lookup {}); - instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); - instructions_vec_t_2.instr(CheckEq {}); - instructions_vec_t_2.instr(AssertTrue {}); - - // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" - instructions_vec_t_2.restack(Restack::dup()); - instructions_vec_t_2.instr(Push { push: "contractaddress".to_string() }); - instructions_vec_t_2.instr(Lookup {}); - instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); - instructions_vec_t_2.instr(Push { push: "0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string() }); - instructions_vec_t_2.instr(CheckEq {}); - instructions_vec_t_2.instr(AssertTrue {}); + // // x[0] + // let zero: Number = From::from(0u8); + // instructions_vec_t_2.instr(Push { push: zero }); + // instructions_vec_t_2.instr(Index {}); + // instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); + + // // x["action"] = "tokenbalance" + // instructions_vec_t_2.restack(Restack::dup()); + // instructions_vec_t_2.instr(Push { push: "action".to_string() }); + // instructions_vec_t_2.instr(Lookup {}); + // instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); + // instructions_vec_t_2.instr(CheckEq {}); + // instructions_vec_t_2.instr(AssertTrue {}); + + // // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + // instructions_vec_t_2.restack(Restack::dup()); + // instructions_vec_t_2.instr(Push { push: "contractaddress".to_string() }); + // instructions_vec_t_2.instr(Lookup {}); + // instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); + // instructions_vec_t_2.instr(Push { push: "0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string() }); + // instructions_vec_t_2.instr(CheckEq {}); + // instructions_vec_t_2.instr(AssertTrue {}); // // x["response"]["result"] = "135499" // Instruction::Restack(Restack::dup()), @@ -376,8 +377,11 @@ fn main() { } println!(""); - println!("{:?}\n", instructions_vec_t_2.run(&mut stack)); + match instructions_vec_t_2.run(&mut stack) { + Ok(()) => (), + Err(e) => println!("failed:\n{}\n", e), + } - println!("FINAL STACK"); - stack.debug(); + // println!("FINAL STACK"); + // stack.debug(); } diff --git a/src/restack.rs b/src/restack.rs index fc9f1d7..7317384 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -143,7 +143,7 @@ impl Restack { } -#[derive(Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error)] pub enum RestackError { #[error("invalid Restack: restack_index = {restack_index:?} out of bounds for restack_depth = {restack_depth:?}")] StackIndexInvalid { diff --git a/src/stack.rs b/src/stack.rs index a241ca2..598c2ec 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -71,7 +71,10 @@ impl Stack { } pub fn debug_type(&self) -> () { - println!("stack type: {:?}", &self.stack.clone().into_iter().map(|x| x.symbol()).collect::>()) + println!("stack type: {:?}", + &self.stack.clone().into_iter() + .map(|x| x.symbol_str()) + .collect::>()) } pub fn debug(&self) -> Result<(), serde_json::Error> { diff --git a/src/types.rs b/src/types.rs index 504b1ef..0182b82 100644 --- a/src/types.rs +++ b/src/types.rs @@ -191,6 +191,13 @@ pub struct TypeId { type_id: usize, } + +impl Display for TypeId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "type#{}", self.type_id) + } +} + impl TypeId { // TODO: test by checking: // xs.map(TypeId).fold(x, offset) = TypeId(xs.fold(x, +)) @@ -494,6 +501,7 @@ impl Type { }) } + pub fn specialize_to_input_stack(&mut self, stack_type: Vec) -> Result<(), TypeError> { if self.i_type.len() <= stack_type.len() { let mut stack_type_iter = stack_type.into_iter(); @@ -501,7 +509,8 @@ impl Type { self.context.unify_elem_type(type_id, elem_type).map_err(|e| TypeError::ContextError(e))? } for elem_type in stack_type_iter { - self.context.push(elem_type); + let type_id = self.context.push(elem_type); + self.i_type.push(type_id); } Ok(()) } else { @@ -700,16 +709,16 @@ pub enum TypeIdMapError { #[derive(Clone, Debug, PartialEq, Error)] pub enum ContextError { - #[error("Context::get applied to a TypeId: {index:?}, not in the Context: {context:?}, error: {error:?}")] + #[error("Context::get applied to a TypeId: \n{index:?}\n, not in the Context: \n{context:?}\n, error: \n{error:?}\n")] GetUnknownTypeId { context: Context, index: TypeId, error: Arc, }, - #[error("Context::disjoint_union applied to lhs: {lhs:?}, and rhs: {rhs:?}, / - with type_id: {type_id:?}, and elem_type: {elem_type:?}, conflicted / - with lhs entry conflicting_elem_type: {conflicting_elem_type:?}")] + #[error("Context::disjoint_union applied to lhs: \n{lhs:?}\n, and rhs: \n{rhs:?}\n, / + with type_id: \n{type_id:?}\n, and elem_type: \n{elem_type:?}\n, conflicted / + with lhs entry conflicting_elem_type: {conflicting_elem_type:?\n}\n")] DisjointUnion { type_id: TypeId, elem_type: ElemType, @@ -718,28 +727,28 @@ pub enum ContextError { rhs: Context, }, - #[error("Context::normalize_on applied to invalid basis: type_id: {type_id:?}, context: {context:?}, basis: {basis:?}")] + #[error("Context::normalize_on applied to invalid basis: type_id: \n{type_id:?}\n, context: \n{context:?}\n, basis: \n{basis:?}\n")] NormalizeOnInvalidBasis { type_id: TypeId, context: Context, basis: Vec, }, - #[error("Context::update_type_id called on missing 'from: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] + #[error("Context::update_type_id called on missing 'from: TypeId':\n from: \n{from:?}\n to: {to:?}\n context: {context:?}")] UpdateTypeIdFromMissing { from: TypeId, to: TypeId, context: Context, }, - #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: {from:?}\n to: {to:?}\n context: {context:?}")] + #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: \n{from:?}\n\n to: \n{to:?}\n context: \n{context:?}\n")] UpdateTypeIdToPresent { from: TypeId, to: TypeId, context: Context, }, - #[error("Context::unify failed:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n is_lhs: {is_lhs:?}\n")] + #[error("Context::unify failed:\n xs: \n{xs:?}\n xi: \n{xi:?}\n yi: \n{yi:?}\n is_lhs: \n{is_lhs:?}\n")] Unify { xs: Context, xi: TypeId, @@ -747,7 +756,7 @@ pub enum ContextError { is_lhs: bool, }, - #[error("Context::unify failed to unify ElemType's:\n xs: {xs:?}\n xi: {xi:?}\n yi: {yi:?}\n elem_error: {error:?}\n")] + #[error("Context::unify failed to unify ElemType's:\n\nxs:\n{xs}\n\nxi:\n{xi}\n\nyi:\n{yi}\n\nelem_error:\n{error}\n")] UnifyElemType { xs: Context, xi: TypeId, @@ -755,10 +764,10 @@ pub enum ContextError { error: ElemTypeError, }, - #[error("Context::normalize_on building TypeIdMap failed: {0:?}")] + #[error("Context::normalize_on building TypeIdMap failed: \n{0:?}\n")] TypeIdMapError(TypeIdMapError), - #[error("Context::max_type_id: next_type_id == 0: {0:?}")] + #[error("Context::max_type_id: next_type_id == 0: \n{0:?}\n")] MaxTypeId(Context), } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index bb29b60..bdfae68 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -70,14 +70,14 @@ pub enum ElemsPopError { }, // TODO: add detail - #[error("Elems::pop: generic_array internal error")] + #[error("Elems::pop: generic_array internal error\n\nelem_set:\n{elem_set:?}\n\nvec:\n{vec:?}\n\nsize:\n{size}")] GenericArray { elem_set: EnumSet, vec: Vec, size: usize, }, - #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{elem_set:?}\n{stack:?}\n{error:?}")] + #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{elem_set:?}\n{stack:?}\n\nerror:\n{error}")] IsListHd { stack_type: Result, Arc>, elem_set: Result>, @@ -85,7 +85,7 @@ pub enum ElemsPopError { error: Arc, }, - #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{stack:?}\n{error:?}")] + #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{stack:?}\n\nerror:\n{error}")] IsListTl { stack_type: Result, Arc>, stack: Stack, @@ -189,7 +189,7 @@ where where Self: Sized, { - let vec = (1..::to_usize()).map(|_array_ix| { + let vec = (0..::to_usize()).map(|_array_ix| { stack .pop_elem(PhantomData::) .map_err(|e| >::from(e)) @@ -935,21 +935,26 @@ pub trait IsInstructionT: Clone + Debug + PartialEq { fn run(&self, x: &Self::IO) -> Result<(), Self::Error>; } -#[derive(Debug)] +#[derive(Clone, Debug, Error)] pub enum InstructionError { + #[error("InstructionError::ElemsPopError:\n{0}")] ElemsPopError(ElemsPopError), + #[error("RawInstructionError:\n{0}")] RawInstructionError(String), + #[error("MissingOutput:\n{instruction}\n\n{stack_input}")] // TODO: more granular error typing MissingOutput { instruction: String, stack_input: String, }, + #[error("InstructionError::RestackError:\n{0}")] RestackError(RestackError), - DebugJsonError(serde_json::Error), + #[error("InstructionError::DebugJsonError:\n{0}")] + DebugJsonError(Arc), } pub trait IsStackInstruction: Debug { @@ -1024,26 +1029,33 @@ impl Instrs { pub fn run(&self, stack: &mut Stack) -> Result<(), InstructionError> { for instr_or_restack in &self.instrs { - stack.debug().map_err(|e| InstructionError::DebugJsonError(e))?; + stack.debug().map_err(|e| InstructionError::DebugJsonError(Arc::new(e)))?; println!("------------------------------------------------------------------------------------------"); println!("#: {:?}\n", instr_or_restack); match instr_or_restack { Ok(instr) => { let mut instr_type = instr.type_of(); stack.debug_type(); + format!(""); + match instr_type { Ok(instr_type) => { println!("instr: {}\n", instr_type); let mut mut_instr_type = instr_type.clone(); - match mut_instr_type.specialize_to_input_stack(stack.clone().stack.into_iter().map(|x| x.elem_type(vec![])).collect()) { + match mut_instr_type + .specialize_to_input_stack(stack + .clone() + .stack + .into_iter() + .map(|x| x.elem_type(vec![])) + .collect()) { Ok(specialized) => println!("specialized: {}\n", mut_instr_type), - Err(e) => println!("specialization failed: {:?}\n", e), + Err(e) => println!("specialization failed: {}\n", e), } }, - Err(e) => println!("instr type_of errror: {:?}\n", e), + Err(e) => println!("instr type_of errror: {}\n", e), } println!(""); - instr.stack_run(stack)? }, Err(restack) => { From 0eaae816cfdd2f692a7eed65a1ee69d4b7023b12 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 21 Mar 2022 17:03:16 -0400 Subject: [PATCH 58/77] use clap for cli parsing, pretty print elem/stack, get running and parsing with IsInstructionT working --- Cargo.toml | 1 + src/cli.rs | 150 ++++++++++++++++ src/elem.rs | 144 ++++++++++++++- src/instruction.rs | 146 +++++++++++---- src/lib.rs | 6 +- src/main.rs | 91 +++++----- src/parse.rs | 4 +- src/stack.rs | 33 +++- src/types.rs | 39 ++-- src/types_scratch.rs | 416 +++++++++++++++++++++++++++++-------------- 10 files changed, 796 insertions(+), 234 deletions(-) create mode 100644 src/cli.rs diff --git a/Cargo.toml b/Cargo.toml index 1dc18ab..9b74d53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "3.1.6", features = ["derive"] } enumset = { version = "1.0.8", features = ["serde"] } generic-array = "0.14" hex = "0.4" diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..c3370af --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,150 @@ +// use crate::elem::{StackType}; +use crate::stack::{Stack}; +// use crate::restack::{Restack, RestackError}; +// use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; +use crate::types_scratch::{Instrs, StackInstructionError}; +use crate::instruction::{InstructionError}; +use crate::parse::{parse_json, ParseError}; + + +// use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; +// use cryptoscript::{Stack, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; + +use std::fs; +use std::io; +use std::path::PathBuf; +use std::sync::Arc; + +use clap::{Parser, Subcommand}; +// use clap::derive; +use serde_json::Value; +use thiserror::Error; + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +pub struct Cli { + /// Cryptoscript program to run + #[clap(short, long, parse(from_os_str), value_name = "FILE")] + code: PathBuf, + + /// JSON input (omit to provide via STDIN) + #[clap(short, long, parse(from_os_str), value_name = "FILE")] + input: Option, + + #[clap(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +enum Commands { + /// Parse only + Parse, + + // TODO: implement + // /// Type check only + // Type, +} + +#[derive(Clone, Debug, Error)] +pub enum CliError { + #[error("Cli::get_input: invalid input path:\n{input_path:?}")] + InvalidInputPath { + input_path: Option, + }, + + #[error("StackInstructionError:\n{0}")] + StackInstructionError(StackInstructionError), + + #[error("InstructionError:\n{0}")] + InstructionError(InstructionError), + + #[error("ParseError:\n{0}")] + ParseError(Arc), + + #[error("std::io::Error:\n{0}")] + IOError(Arc), + + #[error("Cli::get_input: serde_json::from_str threw error:\n{0}")] + SerdeJsonError(Arc), +} + +impl From for CliError { + fn from(error: StackInstructionError) -> Self { + Self::StackInstructionError(error) + } +} + +impl From for CliError { + fn from(error: InstructionError) -> Self { + Self::InstructionError(error) + } +} + +impl From for CliError { + fn from(error: ParseError) -> Self { + Self::ParseError(Arc::new(error)) + } +} +impl From for CliError { + fn from(error: io::Error) -> Self { + Self::IOError(Arc::new(error)) + } +} + +impl From for CliError { + fn from(error: serde_json::Error) -> Self { + Self::SerdeJsonError(Arc::new(error)) + } +} + +impl Cli { + pub fn parse_code(&self) -> Result { + let instructions_str = fs::read_to_string(self.code.clone())?; + Ok(parse_json(&instructions_str)?.to_instrs()?) + } + + pub fn get_input(&self) -> Result { + if let Some(input_path) = self.input.as_deref() { + let input_str = fs::read_to_string(input_path)?; + Ok(serde_json::from_str(&input_str)?) + } else { + Err(CliError::InvalidInputPath { + input_path: self.input.clone(), + }) + } + } + + pub fn parse_and_run_result(&self) -> Result<(), CliError> { + let instructions = self.parse_code()?; + let mut stack = Stack::new(); + let input_json_value = self.get_input()?; + stack.push_elem(input_json_value); + + println!("instructions:"); + for instruction in &instructions.instrs { + println!("{:?}", instruction); + } + println!(""); + Ok(instructions.run(&mut stack)?) + } + + pub fn parse_and_run(&self) -> () { + match self.parse_and_run_result() { + Ok(()) => println!("successful!"), + Err(e) => println!("failed:\n{}\n", e), + } + } + + pub fn run(&self) -> () { + match self.command { + None => self.parse_and_run(), + Some(Commands::Parse) => { + match self.parse_code() { + Ok(parsed) => println!("parsed:\n{:?}", parsed), + Err(e) => println!("parsing failed:\n{}", e), + } + }, + } + } +} + diff --git a/src/elem.rs b/src/elem.rs index 15dc2bc..1570c4f 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -5,9 +5,9 @@ use thiserror::Error; use std::cmp; use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use std::marker::PhantomData; -use std::iter::{IntoIterator}; +use std::iter::{FromIterator, IntoIterator}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; @@ -42,6 +42,34 @@ impl PartialOrd for Elem { } } +// println!("{:x?}", + +impl Display for Elem { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + match self { + Self::Unit => write!(f, "()"), + Self::Bool(x) => write!(f, "{}", x), + Self::Number(x) => write!(f, "{}", x), + Self::Bytes(x) => write!(f, "{}", hex::encode(x.as_slice())), + Self::String(x) => write!(f, "{}", x), + Self::Array(x) => { + f.debug_list() + .entries(x.iter() + .map(|x| format!("{}", x))) + .finish() + }, + Self::Object(x) => { + f.debug_list() + .entries(x.iter() + .map(|(x, y)| format!("({}, {})", x.clone(), y.clone()))) + .finish() + }, + Self::Json(x) => write!(f, "{}", x), + } + } +} + + // EnumSetType implies: Copy, PartialEq, Eq #[derive(EnumSetType, Debug, PartialOrd, Ord, Serialize, Deserialize)] @@ -347,6 +375,16 @@ impl ElemType { // Self::concat_type(locations) // } + pub fn union(&self, other: Self) -> Result { + let both = self.type_set.union(other.type_set); + let mut both_info = self.info.clone(); + both_info.append(&mut other.info.clone()); + Ok(ElemType { + type_set: both, + info: both_info, + }) + } + pub fn unify(&self, other: Self) -> Result { let both = self.type_set.intersection(other.type_set); if both.is_empty() { @@ -377,6 +415,91 @@ pub enum ElemTypeError { +// BEGIN DebugAsDisplay +#[derive(Clone, PartialEq, Eq)] +struct DebugAsDisplay +where + T: Display, +{ + t: T, +} + +impl Display for DebugAsDisplay +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.t) + } +} + +impl Debug for DebugAsDisplay +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.t) + } +} +// END DebugAsDisplay + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StackType { + pub types: Vec, +} + +impl IntoIterator for StackType { + type Item = ElemType; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.types.into_iter() + } +} + +impl FromIterator for StackType { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + StackType { + types: FromIterator::from_iter(iter), + } + } +} + +impl StackType { + pub fn len(&self) -> usize { + self.types.len() + } + + pub fn push(&mut self, elem_type: ElemType) -> () { + self.types.insert(0, elem_type) + } + + pub fn push_n(&mut self, elem_type: ElemType, count: usize) -> () { + for _index in 0..count { + self.push(elem_type.clone()) + } + } +} + +// Uses DebugAsDisplay to eliminate '"' around strings: +// ["{Number}", "{Array, Object}"] -> [{Number}, {Array, Object}] +impl Display for StackType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_list() + .entries(self.types + .iter() + .map(|x| DebugAsDisplay { t: format!("{}", x) })) + .finish()?; + Ok(()) + } +} + + + + pub trait AnElem: Clone + std::fmt::Debug + PartialEq { @@ -388,6 +511,21 @@ pub trait AnElem: Clone + std::fmt::Debug + PartialEq { fn from_elem(t: PhantomData, x: Elem) -> Result; } +impl AnElem for Elem { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::all() + } + + fn to_elem(self) -> Elem { + self + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + Ok(x) + } +} + + impl AnElem for () { fn elem_symbol(_t: PhantomData) -> EnumSet { EnumSet::only(ElemSymbol::Unit) @@ -559,7 +697,7 @@ impl AnElem for Value { #[derive(Clone, Debug, Error)] pub enum AnElemError { - #[error("AnElem::from_elem: element popped from the stack {found:?} wasn't the expected type {expected:?}")] + #[error("AnElem::from_elem: element popped from the stack\n\n{found}\n\nwasn't the expected type:\n{expected:?}")] UnexpectedElemType { expected: EnumSet, found: Elem, diff --git a/src/instruction.rs b/src/instruction.rs index 9b44b28..add8f93 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,7 +1,10 @@ use crate::restack::{Restack, RestackError}; -use crate::elem::{Elem, ElemSymbol, ElemType}; +use crate::elem::{ElemSymbol, ElemType}; use crate::stack::{LineNo}; use crate::types::{TypeId, Context, Type, TypeError}; +use crate::types_scratch::{StackInstructionError, Instruction, Instr, Instrs, + Push, HashSha256, CheckLe, CheckLt, CheckEq, + StringEq, Concat, Slice, Index, Lookup, AssertTrue, ToJson, UnpackJson, StringToBytes}; // use std::collections::BTreeMap; // use std::cmp; @@ -9,34 +12,89 @@ use crate::types::{TypeId, Context, Type, TypeError}; // use std::fmt::{Display, Formatter}; // // use std::alloc::string; // use std::marker::PhantomData; -// use std::sync::Arc; +use std::sync::Arc; +use std::marker::PhantomData; // use enumset::{EnumSet, enum_set}; use serde::{Deserialize, Serialize}; -// use serde_json::{Map, Number, Value}; +use serde_json::{Map, Number, Value}; use thiserror::Error; -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -pub enum Instruction { - Push(Elem), - Restack(Restack), - HashSha256, - CheckLe, - CheckLt, - CheckEq, - Concat, - Slice, - Index, - Lookup, - AssertTrue, - ToJson, - UnpackJson(ElemSymbol), - StringToBytes, +#[derive(Clone, Debug, Error)] +pub enum InstructionError { + #[error("Instruction::to_instr UnpackJson does not support: {elem_symbol:?}")] + UnpackJson { + elem_symbol: ElemSymbol, + } } +impl Instruction { + pub fn to_instr(self) -> Result { + match self { + Self::Push(elem) => Ok(Instr::Instr(Arc::new(Push { push: elem }))), + Self::Restack(restack) => Ok(Instr::Restack(restack.clone())), + Self::HashSha256 => Ok(Instr::Instr(Arc::new(HashSha256 {}))), + Self::CheckLe => Ok(Instr::Instr(Arc::new(CheckLe {}))), + Self::CheckLt => Ok(Instr::Instr(Arc::new(CheckLt {}))), + Self::CheckEq => Ok(Instr::Instr(Arc::new(CheckEq {}))), + Self::StringEq => Ok(Instr::Instr(Arc::new(StringEq {}))), + Self::Concat => Ok(Instr::Instr(Arc::new(Concat {}))), + Self::Slice => Ok(Instr::Instr(Arc::new(Slice {}))), + Self::Index => Ok(Instr::Instr(Arc::new(Index {}))), + Self::Lookup => Ok(Instr::Instr(Arc::new(Lookup {}))), + Self::AssertTrue => Ok(Instr::Instr(Arc::new(AssertTrue {}))), + Self::ToJson => Ok(Instr::Instr(Arc::new(ToJson {}))), + Self::UnpackJson(elem_symbol) => { + match elem_symbol { + ElemSymbol::Unit => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::<()> }))), + ElemSymbol::Bool => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), + ElemSymbol::Number => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), + ElemSymbol::String => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), + ElemSymbol::Array => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::> }))), + ElemSymbol::Object => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::> }))), + _ => Err(InstructionError::UnpackJson { + elem_symbol: elem_symbol, + }) + } + }, + Self::StringToBytes => Ok(Instr::Instr(Arc::new(StringToBytes {}))), + } + } +} + + +// #[derive(Clone, Debug)] +// pub struct Instrs { +// // TODO: replace Result with Either? +// pub instrs: Vec, Restack>>, +// } + + + +// fn example_instrs() -> Instrs { +// Instrs { +// instrs: vec![ +// Arc::new(Concat {}), +// Arc::new(AssertTrue {}), +// Arc::new(Push { push: () }), +// Arc::new(HashSha256 {}), +// Arc::new(Slice {}), +// Arc::new(Index {}), +// Arc::new(ToJson {}), +// Arc::new(Lookup {}), +// Arc::new(UnpackJson { t: PhantomData::<()> }), +// Arc::new(StringToBytes {}), +// Arc::new(CheckLe {}), +// Arc::new(CheckLt {}), +// Arc::new(CheckEq {}) +// ], +// } +// } + + -#[derive(Debug, PartialEq, Error)] +#[derive(Debug, Error)] pub enum InstructionTypeError { // TODO: move to instruction:: #[error("Instruction::type_of resulted in an error involving: {instruction:?};\n {error:?}")] @@ -62,8 +120,6 @@ pub enum InstructionTypeError { } - - impl Restack { // TODO: fix locations: out locations are mislabeled as in locations pub fn type_of(&self, line_no: LineNo) -> Result { @@ -81,6 +137,38 @@ impl Restack { } } +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub struct Instructions { + pub instructions: Vec, +} + +impl IntoIterator for Instructions { + type Item = Instruction; + type IntoIter = as std::iter::IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.instructions.into_iter() + } +} + + +impl Instructions { + pub fn to_instrs(self) -> Result { + Ok(Instrs { + instrs: self.into_iter().map(|x| x.to_instr()).collect::, InstructionError>>()?, + }) + } +} + +impl Instrs { + pub fn to_instructions(self) -> Result { + Ok(Instructions { + instructions: self.instrs.into_iter().map(|x| x.to_instruction()).collect::, StackInstructionError>>()?, + }) + } +} + + // /// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] // /// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] // /// HashSha256, // : [ bytes ] -> [ bytes ] @@ -256,20 +344,6 @@ impl Restack { // } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -pub struct Instructions { - pub instructions: Vec, -} - -impl IntoIterator for Instructions { - type Item = Instruction; - type IntoIter = as std::iter::IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.instructions.into_iter() - } -} - // impl Instructions { // pub fn type_of(&self) -> Result { // let mut current_type = Type::id(); diff --git a/src/lib.rs b/src/lib.rs index c1fa868..2956a22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,15 +8,17 @@ mod stack; pub use stack::{Stack, StackError}; mod types; mod types_scratch; -pub use types_scratch::{Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq}; +pub use types_scratch::{Instruction, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; // pub use types::{Instruction, Instructions}; // , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList mod instruction; -pub use instruction::{Instruction, Instructions}; +pub use instruction::{Instructions}; mod parse; pub use parse::{parse, parse_json}; mod executor; pub use executor::Executor; +mod cli; +pub use cli::Cli; use sha2::{Digest, Sha256}; diff --git a/src/main.rs b/src/main.rs index 1ae64d2..e87fc74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,11 @@ -use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; +use cryptoscript::{parse_json, Elem, ElemSymbol, Instruction, Instructions, Restack}; +use cryptoscript::{Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, StringEq}; +use cryptoscript::{Cli}; -use cryptoscript::{Stack, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq}; -// use cryptoscript::{demo_triple, demo_triple_with_tl_handles_intermediate_types, HList}; use std::marker::PhantomData; + +use clap::{Parser}; +// use cryptoscript::{demo_triple, demo_triple_with_tl_handles_intermediate_types, HList}; use serde_json::{Map, Number, Value}; #[cfg(test)] @@ -30,7 +33,7 @@ mod tests { fn main() { - let input_json = r#" + let _input_json = r#" { "queries": [ { @@ -255,28 +258,31 @@ fn main() { instructions_vec_t_2.instr(Lookup {}); instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); - // // x[0] - // let zero: Number = From::from(0u8); - // instructions_vec_t_2.instr(Push { push: zero }); - // instructions_vec_t_2.instr(Index {}); - // instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); - - // // x["action"] = "tokenbalance" - // instructions_vec_t_2.restack(Restack::dup()); - // instructions_vec_t_2.instr(Push { push: "action".to_string() }); - // instructions_vec_t_2.instr(Lookup {}); - // instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); - // instructions_vec_t_2.instr(CheckEq {}); - // instructions_vec_t_2.instr(AssertTrue {}); - - // // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" - // instructions_vec_t_2.restack(Restack::dup()); - // instructions_vec_t_2.instr(Push { push: "contractaddress".to_string() }); - // instructions_vec_t_2.instr(Lookup {}); - // instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); - // instructions_vec_t_2.instr(Push { push: "0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string() }); - // instructions_vec_t_2.instr(CheckEq {}); - // instructions_vec_t_2.instr(AssertTrue {}); + // x[0] + let zero: Number = From::from(0u8); + instructions_vec_t_2.instr(Push { push: zero }); + instructions_vec_t_2.instr(Index {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData::> }); + + // x["action"] = "tokenbalance" + instructions_vec_t_2.restack(Restack::dup()); + instructions_vec_t_2.instr(Push { push: "action".to_string() }); + instructions_vec_t_2.instr(Lookup {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); + instructions_vec_t_2.instr(Push { push: "tokenbalance".to_string() }); + instructions_vec_t_2.instr(StringEq {}); + instructions_vec_t_2.instr(AssertTrue {}); + instructions_vec_t_2.restack(Restack::drop()); + + // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + instructions_vec_t_2.restack(Restack::dup()); + instructions_vec_t_2.instr(Push { push: "contractaddress".to_string() }); + instructions_vec_t_2.instr(Lookup {}); + instructions_vec_t_2.instr(UnpackJson { t: PhantomData:: }); + instructions_vec_t_2.instr(Push { push: "0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string() }); + instructions_vec_t_2.instr(StringEq {}); + instructions_vec_t_2.instr(AssertTrue {}); + instructions_vec_t_2.restack(Restack::drop()); // // x["response"]["result"] = "135499" // Instruction::Restack(Restack::dup()), @@ -367,21 +373,26 @@ fn main() { // Instruction::CheckEq, // Instruction::AssertTrue, - let mut stack = Stack::new(); - let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); - stack.push_elem(input_json_value); - println!("instructions:"); - for instruction in &instructions_vec_t_2.instrs { - println!("{:?}", instruction); - } - println!(""); + // let json_instructions = serde_json::to_string_pretty(&serde_json::to_value(instructions_vec_t_2.to_instructions().unwrap().clone()).unwrap()).unwrap(); + // println!("json_instructions:\n\n{}", json_instructions); - match instructions_vec_t_2.run(&mut stack) { - Ok(()) => (), - Err(e) => println!("failed:\n{}\n", e), - } + // let mut stack = Stack::new(); + // let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); + // stack.push_elem(input_json_value); - // println!("FINAL STACK"); - // stack.debug(); + // println!("instructions:"); + // for instruction in &instructions_vec_t_2.instrs { + // println!("{:?}", instruction); + // } + // println!(""); + + // match instructions_vec_t_2.run(&mut stack) { + // Ok(()) => (), + // Err(e) => println!("failed:\n{}\n", e), + // } + // + + let cli = Cli::parse(); + cli.run(); } diff --git a/src/parse.rs b/src/parse.rs index f123edb..4bfb0d3 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -10,8 +10,8 @@ /// digit hexadecimal number. use crate::elem::{Elem}; -// use crate::types::{Instruction, Instructions}; -use crate::instruction::{Instruction, Instructions}; +use crate::types_scratch::{Instruction}; +use crate::instruction::{Instructions}; use std::str::FromStr; diff --git a/src/stack.rs b/src/stack.rs index 598c2ec..4411594 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,12 +1,12 @@ // use crate::restack::{RestackError}; -use crate::elem::{Elem, AnElem, AnElemError, ElemSymbol}; +use crate::elem::{Elem, StackType, AnElem, AnElemError, ElemSymbol}; // use std::collections::BTreeMap; // use std::cmp; // use std::iter::{FromIterator}; -// use std::fmt; -// use std::fmt::{Display, Formatter}; +use std::fmt; +use std::fmt::{Display, Formatter}; // // use std::alloc::string; // use std::marker::PhantomData; // use std::sync::Arc; @@ -27,6 +27,18 @@ pub struct Stack { pub stack: Vec, } +impl Display for Stack { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_list() + .entries(self.stack + .iter() + .map(|x| format!("{}", x))) + .finish()?; + Ok(()) + } +} + + impl Stack { pub fn new() -> Self { Stack { @@ -70,11 +82,14 @@ impl Stack { GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) } + pub fn type_of(&self) -> StackType { + StackType { + types: self.stack.clone().into_iter().map(|x| x.elem_type(vec![])).collect(), + } + } + pub fn debug_type(&self) -> () { - println!("stack type: {:?}", - &self.stack.clone().into_iter() - .map(|x| x.symbol_str()) - .collect::>()) + println!("stack type:\n{}", self.type_of()) } pub fn debug(&self) -> Result<(), serde_json::Error> { @@ -95,10 +110,10 @@ pub enum StackError { #[error("Stack::pop: tried to pop from an empty stack")] EmptyStack, - #[error("Stack:pop_elem threw an error from AnElem {0:?}")] + #[error("Stack:pop_elem threw an error from AnElem\n{0}")] AnElemError(AnElemError), - #[error("pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack:?})")] + #[error("pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack})")] UnexpectedElemTypeIn { expected: EnumSet, found: Elem, diff --git a/src/types.rs b/src/types.rs index 0182b82..d7f87ac 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,5 @@ // use crate::restack::{RestackError}; -use crate::elem::{Elem, ElemType, ElemTypeError}; +use crate::elem::{Elem, ElemType, ElemTypeError, StackType}; // Stack, StackError, LineNo, // use crate::stack::{Location}; @@ -493,7 +493,7 @@ impl Type { let mut basis = self.i_type.clone(); basis.append(&mut self.o_type.clone()); basis.dedup(); - let (new_context, type_map) = self.context.normalize_on(basis).map_err(|e| TypeError::ContextError(e))?; + let (new_context, type_map) = self.context.normalize_on(basis).map_err(|e| TypeError::NormalizeContextError(e))?; Ok(Type { context: new_context, i_type: type_map.run(self.i_type.clone()).map_err(|e| TypeError::TypeIdMapError(e))?, @@ -502,11 +502,18 @@ impl Type { } - pub fn specialize_to_input_stack(&mut self, stack_type: Vec) -> Result<(), TypeError> { + pub fn specialize_to_input_stack(&mut self, stack_type: StackType) -> Result<(), TypeError> { if self.i_type.len() <= stack_type.len() { let mut stack_type_iter = stack_type.into_iter(); for (type_id, elem_type) in self.i_type.clone().into_iter().zip(&mut stack_type_iter) { - self.context.unify_elem_type(type_id, elem_type).map_err(|e| TypeError::ContextError(e))? + // TODO: elimate copy? + let elem_type_copy = elem_type.clone(); + self.context.unify_elem_type(type_id, elem_type).map_err(|e| TypeError::Specialization { + type_id: type_id, + elem_type: elem_type_copy, + context: self.context.clone(), + error: e, + })? } for elem_type in stack_type_iter { let type_id = self.context.push(elem_type); @@ -547,7 +554,7 @@ impl Type { // println!("offset_other: {}", offset_other); context.disjoint_union(offset_other.context.clone()) - .map_err(|e| TypeError::ContextError(e))?; + .map_err(|e| TypeError::ComposeContextError(e))?; // println!("context union: {}", context); let mut mut_offset_other = offset_other.clone(); @@ -558,7 +565,7 @@ impl Type { zip_len += 1; context .unify(o_type, i_type) - .map_err(|e| TypeError::ContextError(e))?; + .map_err(|e| TypeError::ComposeContextError(e))?; mut_offset_other .update_type_id(o_type, i_type)?; Ok(()) @@ -780,8 +787,19 @@ impl From for ContextError { #[derive(Debug, PartialEq, Error)] pub enum TypeError { - #[error("ContextError {0}")] - ContextError(ContextError), + #[error("Specialization error:\ntype_id:\n{type_id}\n\nelem_type:\n{elem_type}\n\ncontext:\n{context}\n\nerror:\n{error}")] + Specialization { + type_id: TypeId, + elem_type: ElemType, + context: Context, + error: ContextError, + }, + + #[error("NormalizeContextError {0}")] + NormalizeContextError(ContextError), + + #[error("ComposeContextError {0}")] + ComposeContextError(ContextError), #[error("TypeError::update_type_id failed when updating the Context: {0}")] UpdateTypeId(ContextError), @@ -792,10 +810,11 @@ pub enum TypeError { #[error("Type::normalize applying TypeIdMap failed: {0:?}")] TypeIdMapError(TypeIdMapError), - #[error("Type::specialize_to_input_stack: stack_type shorter than expected:\n{type_of}\n{stack_type:?}")] + // TODO: use StackType and Display instead of Vec + #[error("Type::specialize_to_input_stack: stack_type shorter than expected:\n{type_of}\n{stack_type}")] SpecializeToInputStack { type_of: Type, - stack_type: Vec, + stack_type: StackType, }, } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index bdfae68..29ec6f7 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,4 +1,4 @@ -use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, AnElem}; +use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, StackType, AnElem}; use crate::stack::{Stack, StackError}; use crate::restack::{Restack, RestackError}; use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; @@ -7,6 +7,7 @@ use std::cmp; use std::convert::TryFrom; // use std::iter::FromIterator; use std::marker::PhantomData; +// use std::fmt; use std::fmt::Debug; use std::sync::{Arc, Mutex}; use std::string::FromUtf8Error; @@ -16,6 +17,7 @@ use generic_array::functional::FunctionalSequence; use generic_array::sequence::GenericSequence; use generic_array::typenum::{U0, U1, U2}; use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; +use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; use thiserror::Error; use typenum::marker_traits::Unsigned; @@ -58,12 +60,13 @@ where #[derive(Clone, Debug, Error)] pub enum ElemsPopError { - #[error("Elems::pop singleton: tried to pop an Elem that was not found: {error:?}")] + #[error("Elems::pop singleton: tried to pop an Elem that was not found:\nelem_symbol:\n{elem_symbol:?}\n\n{error}")] PopSingleton { + elem_symbol: EnumSet, error: StackError, }, - #[error("Elems::pop: tried to pop a set of Elem's that were not found: {hd_error:?}\n{tl_errors:?}")] + #[error("Elems::pop: tried to pop a set of Elem's that were not found:\n{hd_error}\n\n{tl_errors}")] Pop { hd_error: Arc, tl_errors: Arc, @@ -77,36 +80,31 @@ pub enum ElemsPopError { size: usize, }, - #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{elem_set:?}\n{stack:?}\n\nerror:\n{error}")] + #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nelem_set:\n{elem_set}\n\nstack_type:\n{stack_type_of}\n\nerror:\n{error}")] IsListHd { - stack_type: Result, Arc>, - elem_set: Result>, - stack: Stack, + stack_type: StackType, + elem_set: ElemType, + stack_type_of: StackType, error: Arc, }, - #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\n{stack_type:?}\n{stack:?}\n\nerror:\n{error}")] + #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nstack_type_of:\n{stack_type_of}\n\nerror:\n{error}")] IsListTl { - stack_type: Result, Arc>, - stack: Stack, + stack_type: StackType, + stack_type_of: StackType, error: Arc, }, #[error("Elems::elem_type (Or): Set includes repeated type: {0:?}")] ElemTypeError(ElemTypeError), + #[error("::type_of(): ContextError when adding Tl type: {0:?}")] + ReturnOrTl(Arc), + #[error("::type_of(): ContextError when adding type: {0:?}")] ReturnOrContextError(ContextError), } -impl From for ElemsPopError { - fn from(error: StackError) -> Self { - Self::PopSingleton { - error: error, - } - } -} - pub trait Elems: Clone + Debug + IntoIterator { type Hd: AnElem; type N: ArrayLength; @@ -192,7 +190,10 @@ where let vec = (0..::to_usize()).map(|_array_ix| { stack .pop_elem(PhantomData::) - .map_err(|e| >::from(e)) + .map_err(|e| ElemsPopError::PopSingleton { + elem_symbol: AnElem::elem_symbol(PhantomData::), + error: e, + }) }).collect::, ElemsPopError>>()?; let array = GenericArray::from_exact_iter(vec.clone()).ok_or_else(|| { ElemsPopError::GenericArray { @@ -216,48 +217,6 @@ where } -// // TODO: relocate LineNo, ArgumentIndex, Location -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -// pub struct LineNo { -// pub line_no: usize, -// } - -// impl From for LineNo { -// fn from(line_no: usize) -> Self { -// LineNo { -// line_no: line_no, -// } -// } -// } - -// pub type ArgumentIndex = usize; - -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -// pub struct Location { -// line_no: LineNo, -// argument_index: ArgumentIndex, -// is_input: bool, -// } - -// impl LineNo { -// pub fn in_at(&self, argument_index: usize) -> Location { -// Location { -// line_no: *self, -// argument_index: argument_index, -// is_input: true, -// } -// } - -// pub fn out_at(&self, argument_index: usize) -> Location { -// Location { -// line_no: *self, -// argument_index: argument_index, -// is_input: false, -// } -// } -// } - - impl IElems for Singleton where T: AnElem, @@ -370,7 +329,7 @@ where info: vec![], }; let elem_type_tl = Elems::elem_type(PhantomData::)?; - elem_type_hd.unify(elem_type_tl) + elem_type_hd.union(elem_type_tl) .map_err(|e| ElemsPopError::ElemTypeError(e)) } } @@ -629,7 +588,8 @@ where // TODO: add error info fn type_of(_t: PhantomData) -> Result { - let mut type_tl = IOElems::type_of(PhantomData::)?; + let mut type_tl = IOElems::type_of(PhantomData::) + .map_err(|e| ElemsPopError::ReturnOrTl(Arc::new(e)))?; let last_type_id = type_tl.context.max_type_id() .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; let next_type_id = type_tl.context.push(ElemType { @@ -668,7 +628,7 @@ pub trait IsList: Clone + Debug + IntoIterator { } } - fn elem_type_vec(t: PhantomData) -> Result, ElemsPopError>; + fn stack_type(t: PhantomData) -> Result; fn pop(_x: PhantomData, stack: &mut Stack) -> Result where @@ -678,17 +638,17 @@ pub trait IsList: Clone + Debug + IntoIterator { Some(x) => Ok(x), None => { let original_stack = stack.clone(); - let x = ::pop(PhantomData, stack).map_err(|e| ElemsPopError::IsListHd { - stack_type: IsList::elem_type_vec(PhantomData::).map_err(|e| Arc::new(e)), - elem_set: Elems::elem_type(PhantomData::).map_err(|e| Arc::new(e)), - stack: original_stack.clone(), + let x = ::pop(PhantomData, stack).or_else(|e| Err(ElemsPopError::IsListHd { + stack_type: IsList::stack_type(PhantomData::)?, + elem_set: Elems::elem_type(PhantomData::)?, + stack_type_of: original_stack.clone().type_of(), error: Arc::new(e), - })?; - let xs = ::pop(PhantomData, stack).map_err(|e| ElemsPopError::IsListTl { - stack_type: IsList::elem_type_vec(PhantomData::).map_err(|e| Arc::new(e)), - stack: original_stack.clone(), + }))?; + let xs = ::pop(PhantomData, stack).or_else(|e| Err(ElemsPopError::IsListTl { + stack_type: IsList::stack_type(PhantomData::)?, + stack_type_of: original_stack.clone().type_of(), error: Arc::new(e), - })?; + }))?; Ok(::cons_list(x, xs)) } } @@ -721,8 +681,10 @@ impl IsList for Nil { Self {} } - fn elem_type_vec(_t: PhantomData) -> Result, ElemsPopError> { - Ok(vec![]) + fn stack_type(_t: PhantomData) -> Result { + Ok(StackType { + types: vec![], + }) } } @@ -784,11 +746,12 @@ impl IsList for Cons { self.tl } - fn elem_type_vec(_t: PhantomData) -> Result, ElemsPopError> { + fn stack_type(_t: PhantomData) -> Result { let elem_type_hd = Elems::elem_type(PhantomData::)?; - let mut elem_type_vec_tl = IsList::elem_type_vec(PhantomData::)?; - elem_type_vec_tl.insert(0, elem_type_hd); - Ok(elem_type_vec_tl) + let elem_type_hd_count = <::N as Unsigned>::to_usize(); + let mut stack_type_tl = IsList::stack_type(PhantomData::)?; + stack_type_tl.push_n(elem_type_hd, elem_type_hd_count); + Ok(stack_type_tl) } } @@ -896,8 +859,8 @@ where self.cons.tl() } - fn elem_type_vec(_t: PhantomData) -> Result, ElemsPopError> { - IsList::elem_type_vec(PhantomData::>) + fn stack_type(_t: PhantomData) -> Result { + IsList::stack_type(PhantomData::>) } } @@ -916,7 +879,7 @@ where fn type_of(_t: PhantomData) -> Result { // let num_elem_type_hd = <::N as Unsigned>::to_usize(); let mut type_hd = IOElems::type_of(PhantomData::)?; - let elem_type_tl = IsList::elem_type_vec(PhantomData::)?; + let elem_type_tl = IsList::stack_type(PhantomData::)?; type_hd.append_inputs(elem_type_tl); Ok(type_hd) @@ -927,21 +890,41 @@ where -pub trait IsInstructionT: Clone + Debug + PartialEq { +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub enum Instruction { + Push(Elem), + Restack(Restack), + HashSha256, + CheckLe, + CheckLt, + CheckEq, + StringEq, + Concat, + Slice, + Index, + Lookup, + AssertTrue, + ToJson, + UnpackJson(ElemSymbol), + StringToBytes, +} + +pub trait IsInstructionT: Debug { type IO: IOList; type Error: AnError; + fn to_instruction(&self) -> Result; fn name(x: PhantomData) -> String; fn run(&self, x: &Self::IO) -> Result<(), Self::Error>; } #[derive(Clone, Debug, Error)] -pub enum InstructionError { - #[error("InstructionError::ElemsPopError:\n{0}")] +pub enum StackInstructionError { + #[error("StackInstructionError::ElemsPopError:\n{0}")] ElemsPopError(ElemsPopError), - #[error("RawInstructionError:\n{0}")] - RawInstructionError(String), + #[error("RawStackInstructionError:\n{0}")] + RawStackInstructionError(String), #[error("MissingOutput:\n{instruction}\n\n{stack_input}")] // TODO: more granular error typing @@ -950,23 +933,35 @@ pub enum InstructionError { stack_input: String, }, - #[error("InstructionError::RestackError:\n{0}")] + #[error("StackInstructionError::RestackError:\n{0}")] RestackError(RestackError), - #[error("InstructionError::DebugJsonError:\n{0}")] + #[error("StackInstructionError::DebugJsonError:\n{0}")] DebugJsonError(Arc), + + #[error("UnpackJsonNotSingleton:\n{first_value:?}\n{second_value:?}")] + UnpackJsonNotSingleton { + first_value: Option, + second_value: Option, + }, + } pub trait IsStackInstruction: Debug { + fn to_instruction(&self) -> Result; fn name(&self) -> String; fn type_of(&self) -> Result; - fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError>; + fn stack_run(&self, stack: &mut Stack) -> Result<(), StackInstructionError>; } impl IsStackInstruction for T where T: IsInstructionT, { + fn to_instruction(&self) -> Result { + self.to_instruction() + } + fn name(&self) -> String { IsInstructionT::name(PhantomData::) } @@ -975,14 +970,14 @@ where IOList::type_of(PhantomData::<::IO>) } - fn stack_run(&self, stack: &mut Stack) -> Result<(), InstructionError> { + fn stack_run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { let stack_input = &IsList::pop(PhantomData::<::IO>, stack) - .map_err(|e| InstructionError::ElemsPopError(e))?; + .map_err(|e| StackInstructionError::ElemsPopError(e))?; self.run(stack_input) - .map_err(|e| InstructionError::RawInstructionError(format!("{:?}", e)))?; + .map_err(|e| StackInstructionError::RawStackInstructionError(format!("{:?}", e)))?; let output_value = stack_input .returning() - .ok_or_else(|| InstructionError::MissingOutput { + .ok_or_else(|| StackInstructionError::MissingOutput { instruction: format!("{:?}", self), stack_input: format!("{:?}", stack_input), })?; @@ -993,10 +988,26 @@ where +#[derive(Clone, Debug)] +pub enum Instr { + Instr(Arc), + Restack(Restack), +} + +impl Instr { + pub fn to_instruction(&self) -> Result { + match self { + Self::Instr(instr) => instr.to_instruction(), + Self::Restack(restack) => Ok(Instruction::Restack(restack.clone())), + } + } +} + + #[derive(Clone, Debug)] pub struct Instrs { // TODO: replace Result with Either? - pub instrs: Vec, Restack>>, + pub instrs: Vec, } // fn example_instrs() -> Instrs { @@ -1027,30 +1038,28 @@ impl Instrs { } } - pub fn run(&self, stack: &mut Stack) -> Result<(), InstructionError> { + pub fn run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { for instr_or_restack in &self.instrs { - stack.debug().map_err(|e| InstructionError::DebugJsonError(Arc::new(e)))?; + stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; println!("------------------------------------------------------------------------------------------"); - println!("#: {:?}\n", instr_or_restack); + println!("{:?}\n", instr_or_restack); match instr_or_restack { - Ok(instr) => { - let mut instr_type = instr.type_of(); + Instr::Instr(instr) => { + println!(""); stack.debug_type(); - format!(""); - - match instr_type { + match instr.type_of() { Ok(instr_type) => { println!("instr: {}\n", instr_type); let mut mut_instr_type = instr_type.clone(); match mut_instr_type - .specialize_to_input_stack(stack - .clone() - .stack - .into_iter() - .map(|x| x.elem_type(vec![])) - .collect()) { - Ok(specialized) => println!("specialized: {}\n", mut_instr_type), - Err(e) => println!("specialization failed: {}\n", e), + .specialize_to_input_stack(stack.type_of()) { + // .clone() + // .stack + // .into_iter() + // .map(|x| x.elem_type(vec![])) + // .collect()) { + Ok(()) => println!("specialized: {}\n", mut_instr_type), + Err(e) => println!("specialization failed:\n{}\n", e), } }, Err(e) => println!("instr type_of errror: {}\n", e), @@ -1058,22 +1067,25 @@ impl Instrs { println!(""); instr.stack_run(stack)? }, - Err(restack) => { - println!("restack: {:?}\n", restack); + Instr::Restack(restack) => { restack.run(&mut stack.stack) - .map_err(|e| InstructionError::RestackError(e))? + .map_err(|e| StackInstructionError::RestackError(e))? }, } } + println!("------------------------------------------------------------------------------------------"); + println!("Finished running successfully.\n"); + println!("Final stack:"); + stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; Ok(()) } pub fn instr(&mut self, instr: impl IsStackInstruction + 'static) -> () { - self.instrs.push(Ok(Arc::new(instr))) + self.instrs.push(Instr::Instr(Arc::new(instr))) } pub fn restack(&mut self, restack: Restack) -> () { - self.instrs.push(Err(restack)) + self.instrs.push(Instr::Restack(restack)) } } @@ -1099,6 +1111,10 @@ impl IsInstructionT for Concat { ReturnSingleton, U2>>>, Nil>; type Error = ConcatError; + fn to_instruction(&self) -> Result { + Ok(Instruction::Concat) + } + fn name(_x: PhantomData) -> String { "concat".to_string() } @@ -1137,6 +1153,10 @@ impl IsInstructionT for AssertTrue { // TODO: replace w/ Empty type Error = AssertTrueError; + fn to_instruction(&self) -> Result { + Ok(Instruction::AssertTrue) + } + fn name(_x: PhantomData) -> String { "assert_true".to_string() } @@ -1163,6 +1183,10 @@ impl IsInstructionT for Push { type IO = ConsOut, Nil>; type Error = Empty; + fn to_instruction(&self) -> Result { + Ok(Instruction::Push(self.push.clone().to_elem())) + } + fn name(_x: PhantomData) -> String { format!("push_{:?}", AnElem::elem_symbol(PhantomData::)) } @@ -1185,6 +1209,10 @@ impl IsInstructionT for HashSha256 { type IO = ConsOut, U1>, Nil>; type Error = Empty; + fn to_instruction(&self) -> Result { + Ok(Instruction::HashSha256) + } + fn name(_x: PhantomData) -> String { "sha256".to_string() } @@ -1238,6 +1266,10 @@ impl IsInstructionT for Slice { Cons, Nil>>; type Error = SliceError; + fn to_instruction(&self) -> Result { + Ok(Instruction::Slice) + } + fn name(_x: PhantomData) -> String { "slice".to_string() } @@ -1325,22 +1357,27 @@ impl AnError for IndexError {} // bytes, array, object impl IsInstructionT for Index { type IO = ConsOut, - Cons, U2, - Singleton, U2>>, - Cons, Nil>>>; + Cons, + Cons, U1, + Singleton, U1>>, Nil>>>; type Error = IndexError; + fn to_instruction(&self) -> Result { + Ok(Instruction::Index) + } + fn name(_x: PhantomData) -> String { "index".to_string() } fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; - let y = x.clone().tl().hd(); - let index = &x.clone().tl().tl().hd().array[0]; + let index = x.clone().tl().hd().array[0].clone(); + let y = &x.clone().tl().tl().hd(); let u_index = index.as_u64() .ok_or_else(|| IndexError::IndexNotU64(index.clone())) .and_then(|x| usize::try_from(x).map_err(|_| IndexError::Overflow(index.clone())))?; + let result = match y.clone() { Or::Left(array) => { array[0] @@ -1382,6 +1419,10 @@ impl IsInstructionT for ToJson { type IO = ConsOut, Cons, Nil>>; type Error = ToJsonError; + fn to_instruction(&self) -> Result { + Ok(Instruction::ToJson) + } + fn name(_x: PhantomData) -> String { "to_json".to_string() } @@ -1415,6 +1456,10 @@ impl IsInstructionT for Lookup { Cons, U1>, Nil>>>; type Error = LookupError; + fn to_instruction(&self) -> Result { + Ok(Instruction::Lookup) + } + fn name(_x: PhantomData) -> String { "lookup".to_string() } @@ -1433,17 +1478,17 @@ impl IsInstructionT for Lookup { } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Debug)] pub struct UnpackJson { pub t: PhantomData, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Debug)] pub struct UnpackJsonError {} impl AnError for UnpackJsonError {} trait AJsonElem: AnElem { fn to_value(self) -> Value; - fn from_value(t: PhantomData, x: Value) -> Option; + fn from_value(t: PhantomData, x: Value) -> Option where Self: Sized; } impl AJsonElem for () { @@ -1451,7 +1496,7 @@ impl AJsonElem for () { Value::Null } - fn from_value(_t: PhantomData, x: Value) -> Option { + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { match x { Value::Null => Some(()), _ => None, @@ -1464,7 +1509,7 @@ impl AJsonElem for bool { Value::Bool(self) } - fn from_value(_t: PhantomData, x: Value) -> Option { + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { match x { Value::Bool(y) => Some(y), _ => None, @@ -1477,7 +1522,7 @@ impl AJsonElem for Number { Value::Number(self) } - fn from_value(_t: PhantomData, x: Value) -> Option { + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { match x { Value::Number(y) => Some(y), _ => None, @@ -1490,7 +1535,7 @@ impl AJsonElem for String { Value::String(self) } - fn from_value(_t: PhantomData, x: Value) -> Option { + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { match x { Value::String(y) => Some(y), _ => None, @@ -1503,7 +1548,7 @@ impl AJsonElem for Vec { Value::Array(self) } - fn from_value(_t: PhantomData, x: Value) -> Option { + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { match x { Value::Array(y) => Some(y), _ => None, @@ -1516,7 +1561,7 @@ impl AJsonElem for Map { Value::Object(self) } - fn from_value(_t: PhantomData, x: Value) -> Option { + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { match x { Value::Object(y) => Some(y), _ => None, @@ -1529,6 +1574,17 @@ impl IsInstructionT for UnpackJson { Cons, Nil>>; type Error = UnpackJsonError; + fn to_instruction(&self) -> Result { + let mut symbol_set = ::elem_symbol(PhantomData).into_iter(); + match (symbol_set.next(), symbol_set.next()) { + (Some(elem_symbol), None) => Ok(Instruction::UnpackJson(elem_symbol)), + (x, y) => Err(StackInstructionError::UnpackJsonNotSingleton { + first_value: x, + second_value: y, + }), + } + } + fn name(_x: PhantomData) -> String { "unpack_json".to_string() } @@ -1552,6 +1608,10 @@ impl IsInstructionT for StringToBytes { type IO = ConsOut, U0>, Cons, Nil>>; type Error = Empty; + fn to_instruction(&self) -> Result { + Ok(Instruction::StringToBytes) + } + fn name(_x: PhantomData) -> String { "string_to_bytes".to_string() } @@ -1577,6 +1637,10 @@ impl IsInstructionT for CheckLe { type IO = ConsOut, Cons, Nil>>; type Error = CheckLeError; + fn to_instruction(&self) -> Result { + Ok(Instruction::CheckLe) + } + fn name(_x: PhantomData) -> String { "check_le".to_string() } @@ -1615,6 +1679,10 @@ impl IsInstructionT for CheckLt { type IO = ConsOut, Cons, Nil>>; type Error = CheckLtError; + fn to_instruction(&self) -> Result { + Ok(Instruction::CheckLt) + } + fn name(_x: PhantomData) -> String { "check_lt".to_string() } @@ -1653,6 +1721,10 @@ impl IsInstructionT for CheckEq { type IO = ConsOut, Cons, Nil>>; type Error = CheckEqError; + fn to_instruction(&self) -> Result { + Ok(Instruction::CheckEq) + } + fn name(_x: PhantomData) -> String { "check_eq".to_string() } @@ -1677,6 +1749,46 @@ impl IsInstructionT for CheckEq { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct StringEq {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StringEqError { + lhs: String, + rhs: String, +} +impl AnError for StringEqError {} + +impl IsInstructionT for StringEq { + type IO = ConsOut, Cons, Nil>>; + type Error = StringEqError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::StringEq) + } + + fn name(_x: PhantomData) -> String { + "check_eq".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let array = &x.clone().tl().hd().array; + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| StringEqError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Equal => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} + @@ -1735,8 +1847,6 @@ impl IsInstructionT for CheckEq { // Dict> -> Empty -// IsEq -// type IsEqBool: const bool; @@ -1774,3 +1884,45 @@ impl IsInstructionT for CheckEq { +// // TODO: relocate LineNo, ArgumentIndex, Location +// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// pub struct LineNo { +// pub line_no: usize, +// } + +// impl From for LineNo { +// fn from(line_no: usize) -> Self { +// LineNo { +// line_no: line_no, +// } +// } +// } + +// pub type ArgumentIndex = usize; + +// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +// pub struct Location { +// line_no: LineNo, +// argument_index: ArgumentIndex, +// is_input: bool, +// } + +// impl LineNo { +// pub fn in_at(&self, argument_index: usize) -> Location { +// Location { +// line_no: *self, +// argument_index: argument_index, +// is_input: true, +// } +// } + +// pub fn out_at(&self, argument_index: usize) -> Location { +// Location { +// line_no: *self, +// argument_index: argument_index, +// is_input: false, +// } +// } +// } + + From 9fe5fb8bccc97dba4c86b978651562d5c7c7f24d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 21 Mar 2022 18:35:17 -0400 Subject: [PATCH 59/77] cleanup and get largest example working again --- src/elem.rs | 28 ---- src/instruction.rs | 226 +------------------------ src/main.rs | 385 +++++++++++++++++-------------------------- src/types_scratch.rs | 175 +++++--------------- 4 files changed, 191 insertions(+), 623 deletions(-) diff --git a/src/elem.rs b/src/elem.rs index 1570c4f..18be54c 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -42,8 +42,6 @@ impl PartialOrd for Elem { } } -// println!("{:x?}", - impl Display for Elem { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { match self { @@ -333,14 +331,12 @@ impl Elem { impl ElemType { fn from_locations(type_set: EnumSet, - // base_elem_type: BaseElemType, locations: Vec) -> Self { ElemType { type_set: type_set, info: locations.iter() .map(|&location| ElemTypeInfo { - // base_elem_type: base_elem_type, location: location, }).collect(), } @@ -349,32 +345,9 @@ impl ElemType { pub fn any(locations: Vec) -> Self { Self::from_locations( EnumSet::all(), - // BaseElemType::Any, locations) } - // pub fn concat_type(locations: Vec) -> Self { - // Self::from_locations( - // enum_set!(ElemSymbol::Bytes | - // ElemSymbol::String | - // ElemSymbol::Array | - // ElemSymbol::Object), - // // BaseElemType::Concat, - // locations) - // } - - // pub fn index_type(locations: Vec) -> Self { - // Self::from_locations( - // enum_set!(ElemSymbol::Array | - // ElemSymbol::Object), - // // BaseElemType::Index, - // locations) - // } - - // pub fn slice_type(locations: Vec) -> Self { - // Self::concat_type(locations) - // } - pub fn union(&self, other: Self) -> Result { let both = self.type_set.union(other.type_set); let mut both_info = self.info.clone(); @@ -409,7 +382,6 @@ pub enum ElemTypeError { UnifyEmpty { lhs: ElemType, rhs: ElemType, - // location: TyUnifyLocation, }, } diff --git a/src/instruction.rs b/src/instruction.rs index add8f93..d01c650 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -4,7 +4,7 @@ use crate::stack::{LineNo}; use crate::types::{TypeId, Context, Type, TypeError}; use crate::types_scratch::{StackInstructionError, Instruction, Instr, Instrs, Push, HashSha256, CheckLe, CheckLt, CheckEq, - StringEq, Concat, Slice, Index, Lookup, AssertTrue, ToJson, UnpackJson, StringToBytes}; + StringEq, BytesEq, Concat, Slice, Index, Lookup, AssertTrue, ToJson, UnpackJson, StringToBytes}; // use std::collections::BTreeMap; // use std::cmp; @@ -39,6 +39,7 @@ impl Instruction { Self::CheckLt => Ok(Instr::Instr(Arc::new(CheckLt {}))), Self::CheckEq => Ok(Instr::Instr(Arc::new(CheckEq {}))), Self::StringEq => Ok(Instr::Instr(Arc::new(StringEq {}))), + Self::BytesEq => Ok(Instr::Instr(Arc::new(BytesEq {}))), Self::Concat => Ok(Instr::Instr(Arc::new(Concat {}))), Self::Slice => Ok(Instr::Instr(Arc::new(Slice {}))), Self::Index => Ok(Instr::Instr(Arc::new(Index {}))), @@ -63,37 +64,6 @@ impl Instruction { } } - -// #[derive(Clone, Debug)] -// pub struct Instrs { -// // TODO: replace Result with Either? -// pub instrs: Vec, Restack>>, -// } - - - -// fn example_instrs() -> Instrs { -// Instrs { -// instrs: vec![ -// Arc::new(Concat {}), -// Arc::new(AssertTrue {}), -// Arc::new(Push { push: () }), -// Arc::new(HashSha256 {}), -// Arc::new(Slice {}), -// Arc::new(Index {}), -// Arc::new(ToJson {}), -// Arc::new(Lookup {}), -// Arc::new(UnpackJson { t: PhantomData::<()> }), -// Arc::new(StringToBytes {}), -// Arc::new(CheckLe {}), -// Arc::new(CheckLt {}), -// Arc::new(CheckEq {}) -// ], -// } -// } - - - #[derive(Debug, Error)] pub enum InstructionTypeError { // TODO: move to instruction:: @@ -168,198 +138,6 @@ impl Instrs { } } - -// /// Push(Elem), // (t: type, elem: type(t)) : [] -> [ t ] -// /// Restack(Restack), // (r: restack) : [ .. ] -> [ .. ] -// /// HashSha256, // : [ bytes ] -> [ bytes ] -// /// CheckLe, // : [ x, x ] -> [ bool ] -// /// CheckLt, // : [ x, x ] -> [ bool ] -// /// CheckEq, // : [ x, x ] -> [ bool ] -// /// Concat, // (t: type, prf: is_concat(t)) : [ t, t ] -> [ t ] -// /// Slice, // (t: type, prf: is_slice(t)) : [ int, int, t ] -> [ t ] -// /// Index, // (t: type, prf: is_index(t)) : [ int, t ] -> [ json ] -// /// Lookup, // [ string, object ] -> [ json ] -// /// AssertTrue, // [ bool ] -> [] -// /// ToJson, // (t: type) : [ t ] -> [ json ] -// /// UnpackJson(ElemSymbol), // (t: type) : [ json ] -> [ t ] -// /// StringToBytes, // [ string ] -> [ bytes ] -// impl Instruction { -// pub fn type_of(&self, line_no: LineNo) -> Result { -// match self { -// Instruction::Restack(restack) => -// Ok(restack -// .type_of(line_no) -// .or_else(|e| Err(InstructionTypeError::InstructionTypeOfRestack(e)))?), - -// Instruction::AssertTrue => { -// let mut context = Context::new(); -// let bool_var = context -// .push(ElemSymbol::Bool -// .elem_type(vec![line_no.in_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![bool_var], -// o_type: vec![], -// }) -// }, - -// Instruction::Push(elem) => { -// let mut context = Context::new(); -// let elem_var = context -// .push(elem.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![], -// o_type: vec![elem_var], -// }) -// }, - -// Instruction::HashSha256 => { -// let mut context = Context::new(); -// let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.in_at(0), line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![bytes_var], -// o_type: vec![bytes_var], -// }) -// }, - -// Instruction::ToJson => { -// let mut context = Context::new(); -// let any_var = context.push(ElemType::any(vec![line_no.in_at(0)])); -// let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![any_var], -// o_type: vec![json_var], -// }) -// }, - -// Instruction::StringToBytes => { -// let mut context = Context::new(); -// let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); -// let bytes_var = context.push(ElemSymbol::Bytes.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![string_var], -// o_type: vec![bytes_var], -// }) -// }, - -// Instruction::UnpackJson(elem_symbol) => { -// let mut context = Context::new(); -// let json_var = context.push(ElemSymbol::Json.elem_type(vec![line_no.in_at(0)])); -// let elem_symbol_var = context.push(elem_symbol.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![json_var], -// o_type: vec![elem_symbol_var], -// }) -// }, - -// Instruction::CheckLe => { -// let mut context = Context::new(); -// let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); -// let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); -// let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![any_lhs_var, any_rhs_var], -// o_type: vec![bool_var], -// }) -// }, - -// Instruction::CheckLt => { -// let mut context = Context::new(); -// let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); -// let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); -// let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![any_lhs_var, any_rhs_var], -// o_type: vec![bool_var], -// }) -// }, - -// Instruction::CheckEq => { -// let mut context = Context::new(); -// let any_lhs_var = context.push(ElemType::any(vec![line_no.in_at(0)])); -// let any_rhs_var = context.push(ElemType::any(vec![line_no.in_at(1)])); -// let bool_var = context.push(ElemSymbol::Bool.elem_type(vec![line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![any_lhs_var, any_rhs_var], -// o_type: vec![bool_var], -// }) -// }, - -// Instruction::Concat => { -// let mut context = Context::new(); -// let concat_var = context.push(ElemType::concat_type(vec![line_no.in_at(0), line_no.in_at(1), line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![concat_var, concat_var], -// o_type: vec![concat_var], -// }) -// }, - -// Instruction::Index => { -// let mut context = Context::new(); -// let number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); -// let index_var = context.push(ElemType::index_type(vec![line_no.in_at(1), line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![number_var, index_var], -// o_type: vec![index_var], -// }) -// }, - -// Instruction::Lookup => { -// let mut context = Context::new(); -// let string_var = context.push(ElemSymbol::String.elem_type(vec![line_no.in_at(0)])); -// let object_var = context.push(ElemSymbol::Object.elem_type(vec![line_no.in_at(1), line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![string_var, object_var], -// o_type: vec![object_var], -// }) -// }, - -// Instruction::Slice => { -// let mut context = Context::new(); -// let offset_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(0)])); -// let length_number_var = context.push(ElemSymbol::Number.elem_type(vec![line_no.in_at(1)])); -// let slice_var = context.push(ElemType::slice_type(vec![line_no.in_at(2), line_no.out_at(0)])); -// Ok(Type { -// context: context, -// i_type: vec![offset_number_var, length_number_var, slice_var], -// o_type: vec![slice_var], -// }) -// }, -// }.or_else(|e| Err(InstructionTypeError::InstructionTypeOfDetail { -// instruction: self.clone(), -// error: Box::new(e), -// })) -// } -// } - - -// impl Instructions { -// pub fn type_of(&self) -> Result { -// let mut current_type = Type::id(); -// for (i, instruction) in self.instructions.iter().enumerate() { -// current_type = current_type.compose(instruction.type_of(From::from(i + 1))?) -// .or_else(|e| Err(InstructionTypeError::InstructionsTypeOfLineNo { // TODO: deprecated by Location -// line_no: i, -// error: Box::new(e), -// }))?; - -// println!("line {i}: {current_type}", i = i, current_type = current_type); -// } -// Ok(current_type) -// } -// } - // Test program #1: [] -> [] // // Instruction::Push(Elem::Bool(true)), diff --git a/src/main.rs b/src/main.rs index e87fc74..b7d1fbe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ -use cryptoscript::{parse_json, Elem, ElemSymbol, Instruction, Instructions, Restack}; -use cryptoscript::{Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, StringEq}; +use cryptoscript::{parse_json, Elem, ElemSymbol, Instruction, Instructions}; +use cryptoscript::{Restack, Stack, Instrs}; +use cryptoscript::{AssertTrue, Push, Lookup, UnpackJson, Index, StringEq, CheckEq}; use cryptoscript::{Cli}; use std::marker::PhantomData; use clap::{Parser}; -// use cryptoscript::{demo_triple, demo_triple_with_tl_handles_intermediate_types, HList}; use serde_json::{Map, Number, Value}; #[cfg(test)] @@ -33,7 +33,7 @@ mod tests { fn main() { - let _input_json = r#" + let input_json = r#" { "queries": [ { @@ -45,7 +45,7 @@ fn main() { "tag": "latest", "blockno": "8000000", "apikey": "YourApiKeyToken", - "response": + "response": { "status": "1", "message": "OK", @@ -76,7 +76,6 @@ fn main() { } "#; - // let json_instructions = parse_json()" let instructions_vec: Vec = vec![ // TEST #1 // Instruction::Push(Elem::Bool(true)), @@ -84,128 +83,134 @@ fn main() { // Instruction::AssertTrue, // FOR DEBUGGING TYPER - Instruction::Push(Elem::Json(Default::default())), + // Instruction::Push(Elem::Json(Default::default())), Instruction::UnpackJson(ElemSymbol::Object), Instruction::Restack(Restack::dup()), // x["queries"] - // Instruction::Push(Elem::String("queries".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Array), - - // // x[0] - // Instruction::Push(Elem::Number(From::from(0u8))), - // Instruction::Index, - // Instruction::UnpackJson(ElemSymbol::Object), - - // // x["action"] = "tokenbalance" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("action".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("tokenbalance".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("contractaddress".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, + Instruction::Push(Elem::String("queries".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Array), - // // x["response"]["result"] = "135499" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("response".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("result".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("135499".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["prompts"] - // Instruction::Restack(Restack::drop()), - // Instruction::Push(Elem::String("prompts".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Array), - - // // x[0] - // Instruction::Push(Elem::Number(From::from(0u8))), - // Instruction::Index, - // Instruction::UnpackJson(ElemSymbol::Object), - - // // x["action"] = "siwe" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("action".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("siwe".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["version"] = "1.1.0" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("version".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("1.1.0".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("data".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("fields".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("address".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, + // x[0] + Instruction::Push(Elem::Number(From::from(0u8))), + Instruction::Index, + Instruction::UnpackJson(ElemSymbol::Object), - // // sha256(x["data"]["message"]) - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("data".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("message".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::StringToBytes, - // Instruction::HashSha256, - - // // sha256(x["data"]["fields"]["address"]) - // Instruction::Restack(Restack::swap()), - // Instruction::Push(Elem::String("data".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("fields".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("address".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::StringToBytes, - // Instruction::HashSha256, - - // // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = - // // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] - // Instruction::Concat, - // Instruction::HashSha256, - // Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), - // Instruction::CheckEq, - // Instruction::AssertTrue, + // x["action"] = "tokenbalance" + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("action".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::Push(Elem::String("tokenbalance".to_string())), + Instruction::StringEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), + + // x["contractaddress"] = "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("contractaddress".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::Push(Elem::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())), + Instruction::StringEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), + + // x["response"]["result"] = "135499" + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("response".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Push(Elem::String("result".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::Push(Elem::String("135499".to_string())), + Instruction::StringEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), + + // x["prompts"] + Instruction::Restack(Restack::drop()), + Instruction::Push(Elem::String("prompts".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Array), + + // x[0] + Instruction::Push(Elem::Number(From::from(0u8))), + Instruction::Index, + Instruction::UnpackJson(ElemSymbol::Object), + // x["action"] = "siwe" + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("action".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::Push(Elem::String("siwe".to_string())), + Instruction::StringEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), + + // x["version"] = "1.1.0" + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("version".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::Push(Elem::String("1.1.0".to_string())), + Instruction::StringEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), + + // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("data".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Push(Elem::String("fields".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Push(Elem::String("address".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), + Instruction::StringEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), + + // sha256(x["data"]["message"]) + Instruction::Restack(Restack::dup()), + Instruction::Push(Elem::String("data".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Push(Elem::String("message".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::StringToBytes, + Instruction::HashSha256, + + // sha256(x["data"]["fields"]["address"]) + Instruction::Restack(Restack::swap()), + Instruction::Push(Elem::String("data".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Push(Elem::String("fields".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::Object), + Instruction::Push(Elem::String("address".to_string())), + Instruction::Lookup, + Instruction::UnpackJson(ElemSymbol::String), + Instruction::StringToBytes, + Instruction::HashSha256, + + // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = + // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] + Instruction::Concat, + Instruction::HashSha256, + Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), + Instruction::BytesEq, + Instruction::AssertTrue, + Instruction::Restack(Restack::drop()), ]; let instructions = Instructions { instructions: instructions_vec, @@ -214,29 +219,11 @@ fn main() { let json_instructions = serde_json::to_string_pretty(&serde_json::to_value(instructions.clone()).unwrap()).unwrap(); assert_eq!(parse_json(&json_instructions).unwrap(), instructions); - // let mut exec = Executor::default(); - // exec.push(Elem::Json(serde_json::from_str(input_json).unwrap())); - /* exec.consume(instructions) */ - /* .expect("error processing instructions"); */ - - /* println!("FINAL STACK"); */ - // println!("{:?}", exec); - println!(""); - // match instructions.type_of() { // Ok(r) => println!("\nfinal type:\n{}", r), // Err(e) => println!("{}", e), // } - // println!(""); - // println!("demo_triple:"); - // demo_triple().fold((), |_memo, x| println!("{:?}", x)); - - // println!(""); - // println!("demo_triple_with_tl_handles_intermediate_types:"); - // demo_triple_with_tl_handles_intermediate_types().fold((), |_memo, x| println!("{:?}", x)); - - let mut instructions_vec_t_1 = Instrs::new(); instructions_vec_t_1.instr(Push { push: true }); instructions_vec_t_1.restack(Restack::id()); @@ -284,99 +271,6 @@ fn main() { instructions_vec_t_2.instr(AssertTrue {}); instructions_vec_t_2.restack(Restack::drop()); - // // x["response"]["result"] = "135499" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("response".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("result".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("135499".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["prompts"] - // Instruction::Restack(Restack::drop()), - // Instruction::Push(Elem::String("prompts".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Array), - - // // x[0] - // Instruction::Push(Elem::Number(From::from(0u8))), - // Instruction::Index, - // Instruction::UnpackJson(ElemSymbol::Object), - - // // x["action"] = "siwe" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("action".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("siwe".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["version"] = "1.1.0" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("version".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("1.1.0".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // x["data"]["fields"]["address"] = "0xe04f27eb70e025b78871a2ad7eabe85e61212761" - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("data".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("fields".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("address".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::Push(Elem::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - // // sha256(x["data"]["message"]) - // Instruction::Restack(Restack::dup()), - // Instruction::Push(Elem::String("data".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("message".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::StringToBytes, - // Instruction::HashSha256, - - // // sha256(x["data"]["fields"]["address"]) - // Instruction::Restack(Restack::swap()), - // Instruction::Push(Elem::String("data".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("fields".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::Object), - // Instruction::Push(Elem::String("address".to_string())), - // Instruction::Lookup, - // Instruction::UnpackJson(ElemSymbol::String), - // Instruction::StringToBytes, - // Instruction::HashSha256, - - // // sha256(sha256(x["data"]["message"]) ++ sha256(x["data"]["fields"]["address"])) = - // // [53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139] - // Instruction::Concat, - // Instruction::HashSha256, - // Instruction::Push(Elem::Bytes(vec![53,163,178,139,122,187,171,47,42,135,175,176,240,11,10,152,228,238,106,205,132,68,80,79,188,54,124,242,97,132,31,139])), - // Instruction::CheckEq, - // Instruction::AssertTrue, - - - // let json_instructions = serde_json::to_string_pretty(&serde_json::to_value(instructions_vec_t_2.to_instructions().unwrap().clone()).unwrap()).unwrap(); - // println!("json_instructions:\n\n{}", json_instructions); - // let mut stack = Stack::new(); // let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); // stack.push_elem(input_json_value); @@ -391,8 +285,31 @@ fn main() { // Ok(()) => (), // Err(e) => println!("failed:\n{}\n", e), // } - // - let cli = Cli::parse(); - cli.run(); + // let cli = Cli::parse(); + // cli.run(); + + let mut stack = Stack::new(); + let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); + stack.push_elem(input_json_value); + + println!("instructions:"); + for instruction in instructions.clone() { + println!("{:?}", instruction); + } + println!(""); + + let instructions_vec_t_3 = match instructions.to_instrs() { + Ok(instructions_vec_t) => instructions_vec_t, + Err(e) => { + println!("Instructions::to_instrs() failed:\n{}", e); + panic!("Instructions::to_instrs() failed:\n{}", e) + }, + }; + + match instructions_vec_t_3.run(&mut stack) { + Ok(()) => (), + Err(e) => println!("failed:\n{}\n", e), + } + } diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 29ec6f7..413a3b5 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -819,10 +819,6 @@ impl IntoIterator for ConsOut { fn into_iter(self) -> Self::IntoIter { self.cons.into_iter() - // IterCons { - // cons: self.cons, - // at_head: true, - // } } } @@ -899,6 +895,7 @@ pub enum Instruction { CheckLt, CheckEq, StringEq, + BytesEq, Concat, Slice, Index, @@ -1039,9 +1036,10 @@ impl Instrs { } pub fn run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { - for instr_or_restack in &self.instrs { + for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; println!("------------------------------------------------------------------------------------------"); + println!("line_no: {}", line_no); println!("{:?}\n", instr_or_restack); match instr_or_restack { Instr::Instr(instr) => { @@ -1789,140 +1787,43 @@ impl IsInstructionT for StringEq { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct BytesEq {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BytesEqError { + lhs: Vec, + rhs: Vec, +} +impl AnError for BytesEqError {} +impl IsInstructionT for BytesEq { + type IO = ConsOut, Cons, U2>, Nil>>; + type Error = BytesEqError; + fn to_instruction(&self) -> Result { + Ok(Instruction::BytesEq) + } + fn name(_x: PhantomData) -> String { + "check_eq".to_string() + } - - - - - - - - - - - - - - - - -// Cons>, Nil> - -// ( {U, T} ) - -// Cons>>, Nil> - -// ( {U, T} ) -> {U, T} - - -// forall x, .. z. IsIn {A, B, C} x, .. => [x, x, y, Bool, y] -> [x, Bool] - -// Or < Singleton -// ReturningOr< ReturningSingleton - - -// -// Instruction -// Instruction -// Instruction -// Instruction - - -// [A, B, C] -// Instruction -// [A, B, C] - - - -// Or> - -// Or<(), Singleton<()>> - -// Or> - -// IsNot - -// Dict> -> Empty - - - - - -// impl AnElem for Or { -// fn elem_symbol(_t: PhantomData) -> ElemType { -// let t_set = ::elem_symbol(PhantomData); -// let u_set = ::elem_symbol(PhantomData); -// t_set.union(u_set) -// } - -// fn to_elem(self) -> Elem { -// match self { -// Self::Left(x) => x.to_elem(), -// Self::Right(x) => x.to_elem(), -// } -// } - -// fn from_elem(_t: PhantomData, x: Elem) -> Result { -// AnElem::from_elem(PhantomData::, x.clone()) -// .map(|y| Or::Left(y)) -// .or_else(|e_hd| { -// Ok(Or::Right(AnElem::from_elem(PhantomData::, x)?)) -// .map_err(|e_tl| { -// AnElemError::PopOr { -// e_hd: Box::new(e_hd), -// e_tl: Box::new(e_tl), -// }}) -// }) -// } -// } - - - - - - -// // TODO: relocate LineNo, ArgumentIndex, Location -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -// pub struct LineNo { -// pub line_no: usize, -// } - -// impl From for LineNo { -// fn from(line_no: usize) -> Self { -// LineNo { -// line_no: line_no, -// } -// } -// } - -// pub type ArgumentIndex = usize; - -// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -// pub struct Location { -// line_no: LineNo, -// argument_index: ArgumentIndex, -// is_input: bool, -// } - -// impl LineNo { -// pub fn in_at(&self, argument_index: usize) -> Location { -// Location { -// line_no: *self, -// argument_index: argument_index, -// is_input: true, -// } -// } - -// pub fn out_at(&self, argument_index: usize) -> Location { -// Location { -// line_no: *self, -// argument_index: argument_index, -// is_input: false, -// } -// } -// } - + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let array = &x.clone().tl().hd().array; + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| BytesEqError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Equal => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} From cd652404fd2bcbbbf9bfce5b3539b6f352aec1ee Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 22 Mar 2022 21:40:03 -0400 Subject: [PATCH 60/77] add basic template for schema w/ test --- Cargo.toml | 3 +- src/json_template.rs | 165 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/main.rs | 43 +++++++++++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/json_template.rs diff --git a/Cargo.toml b/Cargo.toml index 9b74d53..01855fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,12 @@ enumset = { version = "1.0.8", features = ["serde"] } generic-array = "0.14" hex = "0.4" hex-literal = "0.3" +indexmap = "1.5" k256 = { version = "0.10.2", features = ["std", "ecdsa", "serde"] } quickcheck = "1.0.3" quickcheck_macros = "1.0.0" serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["arbitrary_precision"] } +serde_json = { version = "1.0.79", features = ["arbitrary_precision", "preserve_order"] } sha2 = "0.9" sha3 = "0.9" thiserror = "1.0" diff --git a/src/json_template.rs b/src/json_template.rs new file mode 100644 index 0000000..4285fa8 --- /dev/null +++ b/src/json_template.rs @@ -0,0 +1,165 @@ +// use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, StackType, AnElem}; +// use crate::stack::{Stack, StackError}; +// use crate::restack::{Restack, RestackError}; +// use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; + +use std::fmt; +// use std::fmt::Debug; +use std::sync::{Arc}; +use std::marker::PhantomData; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::de::{Visitor, MapAccess}; + +use indexmap::{IndexMap}; +use serde_json::{Map, Number, Value}; +use thiserror::Error; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TMap { + map: IndexMap, +} + +impl TMap { + pub fn new() -> Self { + TMap { + map: IndexMap::new(), + } + } + + pub fn insert(&mut self, key: String, value: T) -> Option { + self.map.insert(key, value) + } +} + +impl Serialize for TMap { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.map.clone() + .into_iter() + .map(|(x, y)| Ok((x, y.to_json()?))) + .collect::, TValueError>>() + .map_err(|e| serde::ser::Error::custom(format!("Serialize for TMap:\n{:?}", e)))? + .serialize(serializer) + } +} + +struct TMapVisitor { + marker: PhantomData TMap> +} + +impl TMapVisitor { + fn new() -> Self { + TMapVisitor { + marker: PhantomData + } + } +} + +impl<'de, T> Visitor<'de> for TMapVisitor +where + T: Deserialize<'de>, +{ + type Value = TMap; + + // Format a message stating what data this Visitor expects to receive. + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // TODO: extend description + formatter.write_str("TMap") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut map = IndexMap::with_capacity(access.size_hint().unwrap_or(0)); + + while let Some((key, value)) = access.next_entry()? { + map.insert(key, value); + } + + Ok(TMap { + map: map, + }) + } +} + +impl<'de, T> Deserialize<'de> for TMap +where + T: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(TMapVisitor::new()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum TValue { + Null, + Bool(bool), + Number(Number), + String(String), + Array(Vec), + Object(TMap), + Var(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TValueRunError { + variable: String, + value: Vec, + variables: Map, +} + +impl TValue { + pub fn to_json(&self) -> Result { + serde_json::to_value(self) + .map_err(|e| TValueError::SerdeJsonError(Arc::new(e))) + } + + /// Resolve all of the (Var)'s using the given (variables) + pub fn run(self, variables: Map) -> Result { + let self_copy = self.clone(); + match self { + Self::Null => Ok(Value::Null), + Self::Bool(x) => Ok(Value::Bool(x)), + Self::Number(x) => Ok(Value::Number(x)), + Self::String(x) => Ok(Value::String(x)), + Self::Array(x) => Ok(Value::Array(x.into_iter().map(|y| y.run(variables.clone())).collect::, TValueRunError>>()?)), + Self::Object(x) => Ok(Value::Object(x.map.into_iter().map(|(y, z)| Ok((y, z.run(variables.clone())?))).collect::, TValueRunError>>()?)), + Self::Var(x) => { + variables.get(&x) + .map(|y| y.clone()) + .ok_or_else(|| TValueRunError { + variable: x, + value: vec![self_copy], + variables: variables, + }) + }, + } + } +} + +#[derive(Clone, Debug, Error)] +pub enum TValueError { + #[error("TValue::to_json:\n{0}")] + SerdeJsonError(Arc), +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Template { + pub variables: Map, + pub template: TValue, +} + +impl Template { + pub fn run(self) -> Result { + self.template.run(self.variables) + } +} + diff --git a/src/lib.rs b/src/lib.rs index 2956a22..668b945 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ pub use stack::{Stack, StackError}; mod types; mod types_scratch; pub use types_scratch::{Instruction, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; +mod json_template; +pub use json_template::{TMap, TValue, Template}; // pub use types::{Instruction, Instructions}; // , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList mod instruction; diff --git a/src/main.rs b/src/main.rs index b7d1fbe..db66bc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,11 @@ use cryptoscript::{parse_json, Elem, ElemSymbol, Instruction, Instructions}; use cryptoscript::{Restack, Stack, Instrs}; use cryptoscript::{AssertTrue, Push, Lookup, UnpackJson, Index, StringEq, CheckEq}; use cryptoscript::{Cli}; +use cryptoscript::{TMap, TValue, Template}; use std::marker::PhantomData; +// use indexmap::IndexMap; use clap::{Parser}; use serde_json::{Map, Number, Value}; @@ -312,4 +314,45 @@ fn main() { Err(e) => println!("failed:\n{}\n", e), } + + println!(""); + println!(""); + println!("Template test:"); + + // ERC-20 token balance (currently) + + // GET + // https://api.etherscan.io/api + // ?module=account + // &action=tokenbalance + // &contractaddress=0x57d90b64a1a57749b0f932f1a3395792e12e7055 + // &address=0xe04f27eb70e025b78871a2ad7eabe85e61212761 + // &tag=latest + // &apikey=YourApiKeyToken + + let mut variables = Map::new(); + variables.insert("contractaddress".to_string(), Value::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())); + variables.insert("address".to_string(), Value::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())); + variables.insert("apikey".to_string(), Value::String("YourApiKeyToken".to_string())); + + let mut template = TMap::new(); + template.insert("type".to_string(), TValue::String("GET".to_string())); + template.insert("URL".to_string(), TValue::String("https://api.etherscan.io/api".to_string())); + + let mut query_parameters = TMap::new(); + query_parameters.insert("module".to_string(), TValue::String("account".to_string())); + query_parameters.insert("action".to_string(), TValue::String("tokenbalance".to_string())); + query_parameters.insert("contractaddress".to_string(), TValue::Var("contractaddress".to_string())); + query_parameters.insert("address".to_string(), TValue::Var("address".to_string())); + query_parameters.insert("tag".to_string(), TValue::String("latest".to_string())); + query_parameters.insert("apikey".to_string(), TValue::Var("apikey".to_string())); + template.insert("parameters".to_string(), TValue::Object(query_parameters)); + + let template = Template { + variables: variables, + template: TValue::Object(template), + }; + + println!("{:?}", template.run()); + } From 6c4729bccfb91a94c74f9f2a14dbf290b8f38eb0 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 23 Mar 2022 12:28:19 -0400 Subject: [PATCH 61/77] debugging tokio version error, add query/template handling to cli, demo running w/o api key --- Cargo.toml | 3 + src/cli.rs | 46 +++++++++++--- src/json_template.rs | 140 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- src/main.rs | 114 +++++++++++++++++++++++++++-------- 5 files changed, 271 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01855fa..8287297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] clap = { version = "3.1.6", features = ["derive"] } enumset = { version = "1.0.8", features = ["serde"] } +futures = { version = "0.3.21", features = ["executor", "thread-pool"] } generic-array = "0.14" hex = "0.4" hex-literal = "0.3" @@ -15,9 +16,11 @@ indexmap = "1.5" k256 = { version = "0.10.2", features = ["std", "ecdsa", "serde"] } quickcheck = "1.0.3" quickcheck_macros = "1.0.0" +reqwest = { version = "0.11.10", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.79", features = ["arbitrary_precision", "preserve_order"] } sha2 = "0.9" sha3 = "0.9" +tokio = { version = "1.17.0", features = ["macros", "rt-multi-thread"] } thiserror = "1.0" typenum = "1.15.0" diff --git a/src/cli.rs b/src/cli.rs index c3370af..877bf94 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,7 +5,7 @@ use crate::stack::{Stack}; use crate::types_scratch::{Instrs, StackInstructionError}; use crate::instruction::{InstructionError}; use crate::parse::{parse_json, ParseError}; - +use crate::json_template::{Query, QueryError}; // use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; // use cryptoscript::{Stack, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; @@ -23,11 +23,23 @@ use thiserror::Error; #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { + /// Query to run + #[clap(short, long, parse(from_os_str), value_name = "FILE")] + query: PathBuf, + + /// Query cache json file + #[clap(long, parse(from_os_str), value_name = "FILE")] + cache_location: PathBuf, + + /// Query variables (in JSON) + #[clap(short, long)] + variables: String, + /// Cryptoscript program to run #[clap(short, long, parse(from_os_str), value_name = "FILE")] code: PathBuf, - /// JSON input (omit to provide via STDIN) + /// JSON input #[clap(short, long, parse(from_os_str), value_name = "FILE")] input: Option, @@ -52,6 +64,9 @@ pub enum CliError { input_path: Option, }, + #[error("QueryError:\n{0}")] + QueryError(QueryError), + #[error("StackInstructionError:\n{0}")] StackInstructionError(StackInstructionError), @@ -68,6 +83,12 @@ pub enum CliError { SerdeJsonError(Arc), } +impl From for CliError { + fn from(error: QueryError) -> Self { + Self::QueryError(error) + } +} + impl From for CliError { fn from(error: StackInstructionError) -> Self { Self::StackInstructionError(error) @@ -98,6 +119,12 @@ impl From for CliError { } impl Cli { + pub fn parse_query(&self) -> Result { + let query_str = fs::read_to_string(self.query.clone())?; + let query: Query = serde_json::from_str(&query_str)?; + Ok(query) + } + pub fn parse_code(&self) -> Result { let instructions_str = fs::read_to_string(self.code.clone())?; Ok(parse_json(&instructions_str)?.to_instrs()?) @@ -114,12 +141,17 @@ impl Cli { } } - pub fn parse_and_run_result(&self) -> Result<(), CliError> { + pub async fn parse_and_run_result(&self) -> Result<(), CliError> { let instructions = self.parse_code()?; let mut stack = Stack::new(); + let input_json_value = self.get_input()?; stack.push_elem(input_json_value); + let variables = serde_json::from_str(&self.variables)?; + let query_result = self.parse_query()?.run(variables, self.cache_location.clone()).await?; + stack.push_elem(query_result); + println!("instructions:"); for instruction in &instructions.instrs { println!("{:?}", instruction); @@ -128,16 +160,16 @@ impl Cli { Ok(instructions.run(&mut stack)?) } - pub fn parse_and_run(&self) -> () { - match self.parse_and_run_result() { + pub async fn parse_and_run(&self) -> () { + match self.parse_and_run_result().await { Ok(()) => println!("successful!"), Err(e) => println!("failed:\n{}\n", e), } } - pub fn run(&self) -> () { + pub async fn run(&self) -> () { match self.command { - None => self.parse_and_run(), + None => self.parse_and_run().await, Some(Commands::Parse) => { match self.parse_code() { Ok(parsed) => println!("parsed:\n{:?}", parsed), diff --git a/src/json_template.rs b/src/json_template.rs index 4285fa8..2551613 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -15,6 +15,19 @@ use indexmap::{IndexMap}; use serde_json::{Map, Number, Value}; use thiserror::Error; +//// BEGIN query +use reqwest::Client; +use std::fs; +use std::path::PathBuf; + +// use futures::executor::block_on; +// use futures::executor::ThreadPool; +// use futures::executor::LocalPool; + +// use std::io; +//// END query + + #[derive(Clone, Debug, PartialEq, Eq)] pub struct TMap { map: IndexMap, @@ -163,3 +176,130 @@ impl Template { } } + + + + + + + + +//// BEGIN query +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum QueryType { + Get, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Query { + pub name: String, + pub url: String, + pub template: TValue, + pub cached: bool, + pub query_type: QueryType, +} + +#[derive(Clone, Debug, Error)] +pub enum QueryError { + #[error("Query::get_cached: not cached:\n{name:?}\n{url:?}")] + NotCached { + name: String, + url: String, + }, + + #[error("TValueRunError:\n{0:?}")] + TValueRunError(TValueRunError), + + #[error("ReqwestError:\n{0}")] + ReqwestError(Arc), + + #[error("StdIoError:\n{0}")] + StdIoError(Arc), + + #[error("SerdeJsonError:\n{0}")] + SerdeJsonError(Arc), +} + +impl From for QueryError { + fn from(error: TValueRunError) -> Self { + Self::TValueRunError(error) + } +} + +impl From for QueryError { + fn from(error: reqwest::Error) -> Self { + Self::ReqwestError(Arc::new(error)) + } +} + +impl From for QueryError { + fn from(error: std::io::Error) -> Self { + Self::StdIoError(Arc::new(error)) + } +} + +impl From for QueryError { + fn from(error: serde_json::Error) -> Self { + Self::SerdeJsonError(Arc::new(error)) + } +} + +impl Query { + pub async fn get_cached(self, variables: Map, cache_location: PathBuf) -> Result { + if self.cached { + let cache_str = fs::read_to_string(cache_location)?; + let cache: Map = serde_json::from_str(&cache_str)?; + let cache_index = format!("{:?}:{:?}", self.name, variables); + cache.get(&cache_index).ok_or_else(|| { + QueryError::NotCached { + name: self.name, + url: self.url, + }}).map(|x| x.clone()) + } else { + Err(QueryError::NotCached { + name: self.name, + url: self.url, + }) + } + } + + pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result { + // let pool = ThreadPool::new().unwrap(); + // let mut pool = LocalPool::new(); + // let mut rt = tokio::runtime::Runtime::new().unwrap(); + // let future = async { /* ... */ }; + + let ran_template = self.clone().template.run(variables.clone())?; + // let result_block = async { + let result_block = { + match self.clone().get_cached(variables, cache_location).await { + Ok(result) => Ok(result), + Err(_e) => { + match self.query_type { + QueryType::Get => { + let client = Client::new(); + let result = client.get(self.url) + .json(&ran_template) + .send() + .await + .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; + Ok(result.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) + }, + } + }, + } + }; + + result_block.map_err(|e| QueryError::ReqwestError(Arc::new(e))) + // Ok(result_block) + + // Ok(result_block.wait()?) + + // // pool.spawn_ok(result_block); + // // pool.run_until(result_block).map_err(|e| QueryError::ReqwestError(Arc::new(e))) + // rt.block_on(result_block).map_err(|e| QueryError::ReqwestError(Arc::new(e))) + } +} +//// END query + + diff --git a/src/lib.rs b/src/lib.rs index 668b945..7de91ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ mod types; mod types_scratch; pub use types_scratch::{Instruction, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; mod json_template; -pub use json_template::{TMap, TValue, Template}; +pub use json_template::{TMap, TValue, Template, Query, QueryType, QueryError}; // pub use types::{Instruction, Instructions}; // , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList mod instruction; diff --git a/src/main.rs b/src/main.rs index db66bc2..2138d3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use cryptoscript::{Restack, Stack, Instrs}; use cryptoscript::{AssertTrue, Push, Lookup, UnpackJson, Index, StringEq, CheckEq}; use cryptoscript::{Cli}; use cryptoscript::{TMap, TValue, Template}; +use cryptoscript::{Query, QueryType}; use std::marker::PhantomData; @@ -33,9 +34,10 @@ mod tests { } } -fn main() { +#[tokio::main] +async fn main() { - let input_json = r#" + let _input_json = r#" { "queries": [ { @@ -288,31 +290,38 @@ fn main() { // Err(e) => println!("failed:\n{}\n", e), // } - // let cli = Cli::parse(); - // cli.run(); - let mut stack = Stack::new(); - let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); - stack.push_elem(input_json_value); - println!("instructions:"); - for instruction in instructions.clone() { - println!("{:?}", instruction); - } - println!(""); - let instructions_vec_t_3 = match instructions.to_instrs() { - Ok(instructions_vec_t) => instructions_vec_t, - Err(e) => { - println!("Instructions::to_instrs() failed:\n{}", e); - panic!("Instructions::to_instrs() failed:\n{}", e) - }, - }; - match instructions_vec_t_3.run(&mut stack) { - Ok(()) => (), - Err(e) => println!("failed:\n{}\n", e), - } + + // let mut stack = Stack::new(); + // let input_json_value: serde_json::Value = serde_json::from_str(input_json).unwrap(); + // stack.push_elem(input_json_value); + + // println!("instructions:"); + // for instruction in instructions.clone() { + // println!("{:?}", instruction); + // } + // println!(""); + + // let instructions_vec_t_3 = match instructions.to_instrs() { + // Ok(instructions_vec_t) => instructions_vec_t, + // Err(e) => { + // println!("Instructions::to_instrs() failed:\n{}", e); + // panic!("Instructions::to_instrs() failed:\n{}", e) + // }, + // }; + + // match instructions_vec_t_3.run(&mut stack) { + // Ok(()) => (), + // Err(e) => println!("failed:\n{}\n", e), + // } + + + + + println!(""); @@ -346,13 +355,66 @@ fn main() { query_parameters.insert("address".to_string(), TValue::Var("address".to_string())); query_parameters.insert("tag".to_string(), TValue::String("latest".to_string())); query_parameters.insert("apikey".to_string(), TValue::Var("apikey".to_string())); - template.insert("parameters".to_string(), TValue::Object(query_parameters)); + template.insert("parameters".to_string(), TValue::Object(query_parameters.clone())); - let template = Template { + let _full_template = Template { variables: variables, template: TValue::Object(template), }; - println!("{:?}", template.run()); + // let json_template = serde_json::to_string_pretty(&serde_json::to_value(full_template.clone()).unwrap()).unwrap(); + // println!("{}", json_template); + + // { + // "variables": { + // "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", + // "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + // "apikey": "YourApiKeyToken" + // }, + // "template": { + // "Object": { + // "type": { + // "String": "GET" + // }, + // "URL": { + // "String": "https://api.etherscan.io/api" + // }, + // "parameters": { + // "Object": { + // "module": { + // "String": "account" + // }, + // "action": { + // "String": "tokenbalance" + // }, + // "contractaddress": { + // "Var": "contractaddress" + // }, + // "address": { + // "Var": "address" + // }, + // "tag": { + // "String": "latest" + // }, + // "apikey": { + // "Var": "apikey" + // } + // } + // } + // } + // } + // } + // let query = Query { + // name: "erc20".to_string(), + // url: "https://api.etherscan.io/api".to_string(), + // template: TValue::Object(query_parameters), + // cached: true, + // query_type: QueryType::Get, + // }; + // let json_query = serde_json::to_string_pretty(&serde_json::to_value(query.clone()).unwrap()).unwrap(); + // println!("{}", json_query); + + let cli = Cli::parse(); + cli.run().await; } From aa4140b5327c4bae573acf10015144ce54f595b2 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 23 Mar 2022 16:58:16 -0400 Subject: [PATCH 62/77] adding test server: post expected (to, from) get or get as posted --- Cargo.toml | 10 +++- src/rest-api.rs | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/rest-api.rs diff --git a/Cargo.toml b/Cargo.toml index 8287297..c8625ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,17 @@ name = "cryptoscript" version = "0.1.0" edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "cryptoscript" +path = "src/main.rs" + +[[bin]] +name = "rest-api" +path = "src/rest-api.rs" [dependencies] +actix-web = { version = "4.0.1", default-features = false, features = ["macros"] } + clap = { version = "3.1.6", features = ["derive"] } enumset = { version = "1.0.8", features = ["serde"] } futures = { version = "0.3.21", features = ["executor", "thread-pool"] } diff --git a/src/rest-api.rs b/src/rest-api.rs new file mode 100644 index 0000000..75c0e9b --- /dev/null +++ b/src/rest-api.rs @@ -0,0 +1,128 @@ +use std::time::SystemTime; +use std::sync::{Arc, Mutex}; + +use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; +use indexmap::IndexMap; +use serde_json::{Map, Value}; +use serde::{Deserialize, Serialize}; + +// TODO: +// - post a new api w/ +// + request json +// + response json +// + rate limit +// - get an api: +// + require posted request json +// + return posted response json +// + enforce rate limit +// - get all apis as list +// - top level: link to /apis and provide example + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +struct Api { + request: Value, + response: Value, + rate_limit_seconds: u64, + last_api_call: Option, +} + +#[derive(Clone, Debug)] +struct AppState { + apis: Arc>>, +} + +impl AppState { + fn new() -> Self { + Self { + apis: Arc::new(Mutex::new(IndexMap::new())), + } + } + + // panics if Mutex lock fails + fn api(&mut self, name: String, api: Api) { + self.apis.lock().unwrap().insert(name, api); + } +} + +#[get("/")] +async fn index() -> impl Responder { + let body_str = r#" + Routes: + - / + - /apis + "#; + HttpResponse::Ok().body(body_str) +} + +#[get("/apis")] +async fn index_apis(data: web::Data) -> impl Responder { + let json_body: Result, String> = data.apis + .lock() + .map_err(|e| format!("{}", e)) + .and_then(|x| { x + .clone() + .into_iter() + .map(|(x, y)| Ok((x, serde_json::to_value(y).map_err(|e| format!("{}", e))?))) + .collect::, String>>() + }); + let pretty_json = serde_json::to_string_pretty(&json_body.unwrap()).unwrap(); + // HttpResponse::Ok().json(json_body) + HttpResponse::Ok().body(pretty_json) +} + +#[get("/apis/{api_id}")] +async fn get_api(path: web::Path, data: web::Data) -> impl Responder { + let path_str: String = path.into_inner(); + match data.apis.lock().map_err(|e| format!("{}", e)) { + Ok(apis) => { + let json_response = (*apis).get(&path_str) + .ok_or_else(|| format!("API not found: {:?}", path_str)); + HttpResponse::Ok().json(json_response) + }, + Err(e) => + HttpResponse::NotFound().body(format!("GET /apis/{} failed:\n{}", path_str, e)), + } +} + +// #[post("/apis/{api_id}")] +// async fn echo(req_body: String) -> impl Responder { +// HttpResponse::Ok().body(req_body) +// } + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let server_root = "127.0.0.1"; + let server_port = 8080; + let server_address = format!("http://{}:{}", server_root, server_port); + println!("Starting server.."); + println!("- {} root", server_address); + println!("- {}/apis API's root", server_address); + + let mut app_state = AppState::new(); + app_state.api("got_null".to_string(), Api { + request: Value::Null, + response: Value::String("Got null?".to_string()), + rate_limit_seconds: 1, + last_api_call: None, + }); + + app_state.api("got_number".to_string(), Api { + request: Value::Number(From::from(0u8)), + response: Value::String("Got 0, as expected!".to_string()), + rate_limit_seconds: 1, + last_api_call: None, + }); + + HttpServer::new(move || { + App::new() + .app_data(web::Data::new(app_state.clone())) + .service(index) + .service(index_apis) + .service(get_api) + // .route("/hey", web::get().to(manual_hello)) + }) + .bind((server_root, server_port))? + .run() + .await +} + From f1c58c4b5e5095cf924888b65b732ea64ebc7924 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 25 Mar 2022 14:32:02 -0400 Subject: [PATCH 63/77] support rate limiting, extra debug info, for test api and begin implementing post --- src/json_template.rs | 15 +++++++++ src/rest-api.rs | 80 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/json_template.rs b/src/json_template.rs index 2551613..a1ac6d2 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -244,6 +244,21 @@ impl From for QueryError { } } +// display query +// rate limit +// debug caching +// cache per "api host" +// +// get variables from cli +// output specialized type +// calculate full type +// +// next: +// - stack var labels +// - execution traces (call graphs) +// - error type/handling +// - TEST + impl Query { pub async fn get_cached(self, variables: Map, cache_location: PathBuf) -> Result { if self.cached { diff --git a/src/rest-api.rs b/src/rest-api.rs index 75c0e9b..d9524a8 100644 --- a/src/rest-api.rs +++ b/src/rest-api.rs @@ -18,6 +18,9 @@ use serde::{Deserialize, Serialize}; // - get all apis as list // - top level: link to /apis and provide example +// TODO: +// - implement post + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] struct Api { request: Value, @@ -26,6 +29,36 @@ struct Api { last_api_call: Option, } +impl Api { + pub fn check_rate_limit(&self) -> Result<(), String> { + match self.last_api_call { + None => Ok(()), + Some(last_call_time) => { + let elapsed_seconds = last_call_time + .elapsed() + .map_err(|e| format!("internal SystemTime error: {:?}", e))? + .as_secs(); + if self.rate_limit_seconds <= elapsed_seconds { + Ok(()) + } else { + Err(format!("rate limit exceeded:\n{} seconds since last call, but need {} seconds", + elapsed_seconds, + self.rate_limit_seconds)) + } + }, + } + } + + pub fn called_now(&self) -> Self { + Api { + request: self.request.clone(), + response: self.response.clone(), + rate_limit_seconds: self.rate_limit_seconds, + last_api_call: Some(SystemTime::now()), + } + } +} + #[derive(Clone, Debug)] struct AppState { apis: Arc>>, @@ -38,7 +71,7 @@ impl AppState { } } - // panics if Mutex lock fails + /// panics if Mutex lock fails fn api(&mut self, name: String, api: Api) { self.apis.lock().unwrap().insert(name, api); } @@ -66,27 +99,50 @@ async fn index_apis(data: web::Data) -> impl Responder { .collect::, String>>() }); let pretty_json = serde_json::to_string_pretty(&json_body.unwrap()).unwrap(); - // HttpResponse::Ok().json(json_body) HttpResponse::Ok().body(pretty_json) } #[get("/apis/{api_id}")] -async fn get_api(path: web::Path, data: web::Data) -> impl Responder { +async fn get_api(path: web::Path, data: web::Data, query: web::Json) -> impl Responder { let path_str: String = path.into_inner(); match data.apis.lock().map_err(|e| format!("{}", e)) { - Ok(apis) => { - let json_response = (*apis).get(&path_str) - .ok_or_else(|| format!("API not found: {:?}", path_str)); - HttpResponse::Ok().json(json_response) + Ok(mut apis) => { + println!("DEBUG:\npath:\n{}\napis:\n{:?}\nquery\n{}", path_str, apis, query); + let json_response = apis.clone().get(&path_str) + .ok_or_else(|| format!("API not found: {:?}", path_str)) + .and_then(|api| api.check_rate_limit().map(|_| api)) + .and_then(|api| { + if api.request == query.clone() { + let new_api = api.called_now(); + apis.insert(path_str, new_api); + Ok(api.response.clone()) + } else { + Err(format!("unexpected request JSON, expected:\n{}", api.request)) + } + }); + match json_response { + Ok(response) => { + println!("response: {}", response); + HttpResponse::Ok().json(response) + }, + Err(ref e) => { + println!("error: {}", e); + HttpResponse::BadRequest().json((e.clone(), json_response, query)) + }, + } }, Err(e) => HttpResponse::NotFound().body(format!("GET /apis/{} failed:\n{}", path_str, e)), } } -// #[post("/apis/{api_id}")] -// async fn echo(req_body: String) -> impl Responder { -// HttpResponse::Ok().body(req_body) +#[post("/apis/{api_id}")] +async fn post_api(_path: web::Path, _data: web::Data, request: web::Json) -> impl Responder { + HttpResponse::Ok().json(request.into_inner()) +} + +// async fn post_api(path: ) -> impl Responder { + // HttpResponse::Ok().body(req_body) // } #[actix_web::main] @@ -101,7 +157,7 @@ async fn main() -> std::io::Result<()> { let mut app_state = AppState::new(); app_state.api("got_null".to_string(), Api { request: Value::Null, - response: Value::String("Got null?".to_string()), + response: Value::String("Got null!".to_string()), rate_limit_seconds: 1, last_api_call: None, }); @@ -119,7 +175,7 @@ async fn main() -> std::io::Result<()> { .service(index) .service(index_apis) .service(get_api) - // .route("/hey", web::get().to(manual_hello)) + .service(post_api) }) .bind((server_root, server_port))? .run() From f1a9f1d7852cc7b2ae88d25136ec966f66d5b5bd Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Mar 2022 15:54:35 -0400 Subject: [PATCH 64/77] wrapping up test server: post and get api works --- src/rest-api.rs | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/rest-api.rs b/src/rest-api.rs index d9524a8..c920e64 100644 --- a/src/rest-api.rs +++ b/src/rest-api.rs @@ -1,26 +1,23 @@ use std::time::SystemTime; use std::sync::{Arc, Mutex}; -use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; +use actix_web::{get, put, web, App, HttpResponse, HttpServer, Responder}; use indexmap::IndexMap; use serde_json::{Map, Value}; use serde::{Deserialize, Serialize}; // TODO: -// - post a new api w/ +// - put a new api w/ // + request json // + response json // + rate limit // - get an api: -// + require posted request json -// + return posted response json +// + require put request json +// + return put response json // + enforce rate limit // - get all apis as list // - top level: link to /apis and provide example -// TODO: -// - implement post - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] struct Api { request: Value, @@ -71,9 +68,16 @@ impl AppState { } } - /// panics if Mutex lock fails - fn api(&mut self, name: String, api: Api) { - self.apis.lock().unwrap().insert(name, api); + fn api(&self, name: String, api: Api) -> Result<(), String> { + println!("Adding API \"{}\":", name); + match serde_json::to_value(api.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { + Ok(json) => println!("{}", json), + Err(e) => println!("Printing API failed: {}", e), + } + self.apis.lock() + .map_err(|e| format!("Acquiring lock failed:\n{}", e))? + .insert(name, api); + Ok(()) } } @@ -117,7 +121,7 @@ async fn get_api(path: web::Path, data: web::Data, query: web: apis.insert(path_str, new_api); Ok(api.response.clone()) } else { - Err(format!("unexpected request JSON, expected:\n{}", api.request)) + Err(format!("unexpected request JSON, expected:\n \"{}\"", api.request)) } }); match json_response { @@ -136,15 +140,15 @@ async fn get_api(path: web::Path, data: web::Data, query: web: } } -#[post("/apis/{api_id}")] -async fn post_api(_path: web::Path, _data: web::Data, request: web::Json) -> impl Responder { - HttpResponse::Ok().json(request.into_inner()) +#[put("/apis/{api_id}")] +async fn put_api(path: web::Path, data: web::Data, request: web::Json) -> impl Responder { + match data.api(path.clone(), request.into_inner()) { + Ok(()) => HttpResponse::Ok() + .json(format!("API added: /apis/{}", path.clone())), + Err(e) => HttpResponse::InternalServerError().json(e), + } } -// async fn post_api(path: ) -> impl Responder { - // HttpResponse::Ok().body(req_body) -// } - #[actix_web::main] async fn main() -> std::io::Result<()> { let server_root = "127.0.0.1"; @@ -154,20 +158,20 @@ async fn main() -> std::io::Result<()> { println!("- {} root", server_address); println!("- {}/apis API's root", server_address); - let mut app_state = AppState::new(); + let app_state = AppState::new(); app_state.api("got_null".to_string(), Api { request: Value::Null, response: Value::String("Got null!".to_string()), rate_limit_seconds: 1, last_api_call: None, - }); + }).map_err(|e| std::io::Error::new(std::io::ErrorKind::WouldBlock, e))?; app_state.api("got_number".to_string(), Api { request: Value::Number(From::from(0u8)), response: Value::String("Got 0, as expected!".to_string()), rate_limit_seconds: 1, last_api_call: None, - }); + }).map_err(|e| std::io::Error::new(std::io::ErrorKind::WouldBlock, e))?; HttpServer::new(move || { App::new() @@ -175,7 +179,7 @@ async fn main() -> std::io::Result<()> { .service(index) .service(index_apis) .service(get_api) - .service(post_api) + .service(put_api) }) .bind((server_root, server_port))? .run() From 947f4205c7b302c5eefa75f2350f3ae1e4d2605b Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Mar 2022 16:58:07 -0400 Subject: [PATCH 65/77] support multiple queries and debug/fail-fast for queries --- Cargo.toml | 1 + src/cli.rs | 23 +++++++++++------- src/json_template.rs | 56 ++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 3 +-- src/rest-api.rs | 22 +++++++---------- 5 files changed, 77 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c8625ed..1ea70ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,5 +30,6 @@ serde_json = { version = "1.0.79", features = ["arbitrary_precision", "preserve_ sha2 = "0.9" sha3 = "0.9" tokio = { version = "1.17.0", features = ["macros", "rt-multi-thread"] } +tokio-stream = "0.1.8" thiserror = "1.0" typenum = "1.15.0" diff --git a/src/cli.rs b/src/cli.rs index 877bf94..282280b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,7 +5,7 @@ use crate::stack::{Stack}; use crate::types_scratch::{Instrs, StackInstructionError}; use crate::instruction::{InstructionError}; use crate::parse::{parse_json, ParseError}; -use crate::json_template::{Query, QueryError}; +use crate::json_template::{Query, QueryError, Queries}; // use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; // use cryptoscript::{Stack, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; @@ -23,9 +23,9 @@ use thiserror::Error; #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { - /// Query to run + /// Queries to run #[clap(short, long, parse(from_os_str), value_name = "FILE")] - query: PathBuf, + queries: PathBuf, /// Query cache json file #[clap(long, parse(from_os_str), value_name = "FILE")] @@ -119,10 +119,10 @@ impl From for CliError { } impl Cli { - pub fn parse_query(&self) -> Result { - let query_str = fs::read_to_string(self.query.clone())?; - let query: Query = serde_json::from_str(&query_str)?; - Ok(query) + pub fn parse_queries(&self) -> Result { + let queries_str = fs::read_to_string(self.queries.clone())?; + let queries: Queries = serde_json::from_str(&queries_str)?; + Ok(queries) } pub fn parse_code(&self) -> Result { @@ -149,8 +149,13 @@ impl Cli { stack.push_elem(input_json_value); let variables = serde_json::from_str(&self.variables)?; - let query_result = self.parse_query()?.run(variables, self.cache_location.clone()).await?; - stack.push_elem(query_result); + let queries_result = self.parse_queries()?.run(variables, self.cache_location.clone()).await?; + for query_result in queries_result { + stack.push_elem(query_result) + } + + println!("stack initialized:"); + stack.debug()?; println!("instructions:"); for instruction in &instructions.instrs { diff --git a/src/json_template.rs b/src/json_template.rs index a1ac6d2..ab62bdb 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -16,10 +16,11 @@ use serde_json::{Map, Number, Value}; use thiserror::Error; //// BEGIN query -use reqwest::Client; +use reqwest::{Client, Response}; use std::fs; use std::path::PathBuf; +use tokio_stream::{self as stream, StreamExt}; // use futures::executor::block_on; // use futures::executor::ThreadPool; // use futures::executor::LocalPool; @@ -207,6 +208,13 @@ pub enum QueryError { url: String, }, + #[error("Query::run: request failed:\nresponse:\n{response}")] + RequestFailed { + response: String, + // response: Arc, + // body: String, + }, + #[error("TValueRunError:\n{0:?}")] TValueRunError(TValueRunError), @@ -279,12 +287,20 @@ impl Query { } pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result { + println!("Running Query \"{}\" at \"{}\"", self.name, self.url); + + // println!("{}", // let pool = ThreadPool::new().unwrap(); // let mut pool = LocalPool::new(); // let mut rt = tokio::runtime::Runtime::new().unwrap(); // let future = async { /* ... */ }; let ran_template = self.clone().template.run(variables.clone())?; + match serde_json::to_value(ran_template.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { + Ok(json) => println!("{}\n", json), + Err(e) => println!("Printing query template failed: {}", e), + } + // let result_block = async { let result_block = { match self.clone().get_cached(variables, cache_location).await { @@ -293,19 +309,33 @@ impl Query { match self.query_type { QueryType::Get => { let client = Client::new(); - let result = client.get(self.url) + let response = client.get(self.url) .json(&ran_template) .send() .await .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - Ok(result.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) + + if response.status().is_success() { + Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) + } else { + let response_text = match response.text().await { + Ok(text) => text, + Err(e) => format!("error: \n{}", e), + }; + Err(QueryError::RequestFailed { + response: response_text, + }) + + } }, } }, } }; - result_block.map_err(|e| QueryError::ReqwestError(Arc::new(e))) + result_block + // .map_err(|e| QueryError::ReqwestError(Arc::new(e))) + // Ok(result_block) // Ok(result_block.wait()?) @@ -315,6 +345,24 @@ impl Query { // rt.block_on(result_block).map_err(|e| QueryError::ReqwestError(Arc::new(e))) } } + + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Queries { + queries: Vec, +} + +impl Queries { + pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result, QueryError> { + let mut result = Vec::with_capacity(self.queries.len()); + let mut stream = stream::iter(self.queries); + + while let Some(query) = stream.next().await { + result.push(query.run(variables.clone(), cache_location.clone()).await?) + } + Ok(result) + } +} //// END query diff --git a/src/main.rs b/src/main.rs index 2138d3e..8632225 100644 --- a/src/main.rs +++ b/src/main.rs @@ -326,10 +326,9 @@ async fn main() { println!(""); println!(""); - println!("Template test:"); + // println!("Template test:"); // ERC-20 token balance (currently) - // GET // https://api.etherscan.io/api // ?module=account diff --git a/src/rest-api.rs b/src/rest-api.rs index c920e64..3660941 100644 --- a/src/rest-api.rs +++ b/src/rest-api.rs @@ -6,27 +6,21 @@ use indexmap::IndexMap; use serde_json::{Map, Value}; use serde::{Deserialize, Serialize}; -// TODO: -// - put a new api w/ -// + request json -// + response json -// + rate limit -// - get an api: -// + require put request json -// + return put response json -// + enforce rate limit -// - get all apis as list -// - top level: link to /apis and provide example - +/// GET REST Api, located at 'apis/{name}' #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] struct Api { + /// Constant required request JSON request: Value, + /// Constant response JSON response: Value, + /// Number of seconds required between queries rate_limit_seconds: u64, + /// Time of last API call last_api_call: Option, } impl Api { + /// Fail if rate_limit_seconds > elapsed_seconds since last called_now pub fn check_rate_limit(&self) -> Result<(), String> { match self.last_api_call { None => Ok(()), @@ -46,6 +40,7 @@ impl Api { } } + /// Update last_api_call pub fn called_now(&self) -> Self { Api { request: self.request.clone(), @@ -56,6 +51,7 @@ impl Api { } } +/// All of the supported API's #[derive(Clone, Debug)] struct AppState { apis: Arc>>, @@ -68,6 +64,7 @@ impl AppState { } } + /// Add an API. Its path will be '/apis/name' fn api(&self, name: String, api: Api) -> Result<(), String> { println!("Adding API \"{}\":", name); match serde_json::to_value(api.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { @@ -185,4 +182,3 @@ async fn main() -> std::io::Result<()> { .run() .await } - From d72070d4998bb6fe382dc88f9c1cd34ab3019b8d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Mar 2022 18:35:12 -0400 Subject: [PATCH 66/77] json to template, support put in schemas, caching works, token gating example working with schemas --- src/cli.rs | 3 +- src/json_template.rs | 144 ++++++++++++++++++++----------- src/lib.rs | 3 + src/main.rs | 42 ++++++++- src/{rest-api.rs => rest_api.rs} | 11 ++- 5 files changed, 148 insertions(+), 55 deletions(-) rename src/{rest-api.rs => rest_api.rs} (95%) diff --git a/src/cli.rs b/src/cli.rs index 282280b..010d4d0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -149,7 +149,8 @@ impl Cli { stack.push_elem(input_json_value); let variables = serde_json::from_str(&self.variables)?; - let queries_result = self.parse_queries()?.run(variables, self.cache_location.clone()).await?; + let mut queries_result = self.parse_queries()?.run(variables, self.cache_location.clone()).await?; + queries_result.reverse(); for query_result in queries_result { stack.push_elem(query_result) } diff --git a/src/json_template.rs b/src/json_template.rs index ab62bdb..c409741 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -21,11 +21,6 @@ use std::fs; use std::path::PathBuf; use tokio_stream::{self as stream, StreamExt}; -// use futures::executor::block_on; -// use futures::executor::ThreadPool; -// use futures::executor::LocalPool; - -// use std::io; //// END query @@ -131,6 +126,19 @@ pub struct TValueRunError { } impl TValue { + pub fn from_json(json: Value) -> Self { + match json { + Value::Null => Self::Null, + Value::Bool(x) => Self::Bool(x), + Value::Number(x) => Self::Number(x), + Value::String(x) => Self::String(x), + Value::Array(x) => Self::Array(x.into_iter().map(|x| TValue::from_json(x)).collect()), + Value::Object(x) => Self::Object(TMap { + map: x.into_iter().map(|(x, y)| (x, TValue::from_json(y))).collect() + }), + } + } + pub fn to_json(&self) -> Result { serde_json::to_value(self) .map_err(|e| TValueError::SerdeJsonError(Arc::new(e))) @@ -167,11 +175,19 @@ pub enum TValueError { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Template { + // TODO: use impl instead of pub pub variables: Map, pub template: TValue, } impl Template { + pub fn from_json(json: Value) -> Self { + Template { + variables: Map::new(), + template: TValue::from_json(json), + } + } + pub fn run(self) -> Result { self.template.run(self.variables) } @@ -189,6 +205,7 @@ impl Template { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum QueryType { Get, + Put, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -252,69 +269,74 @@ impl From for QueryError { } } -// display query -// rate limit -// debug caching -// cache per "api host" -// -// get variables from cli -// output specialized type -// calculate full type -// -// next: -// - stack var labels -// - execution traces (call graphs) -// - error type/handling -// - TEST - impl Query { - pub async fn get_cached(self, variables: Map, cache_location: PathBuf) -> Result { + pub fn to_json(&self) -> Result { + Ok(serde_json::to_value(self)?) + } + + pub async fn get_cached(&self, variables: Map, cache_location: PathBuf) -> Result { if self.cached { + println!("Checking cache: {:?}", cache_location.clone()); let cache_str = fs::read_to_string(cache_location)?; let cache: Map = serde_json::from_str(&cache_str)?; let cache_index = format!("{:?}:{:?}", self.name, variables); cache.get(&cache_index).ok_or_else(|| { QueryError::NotCached { - name: self.name, - url: self.url, + name: self.name.clone(), + url: self.url.clone(), }}).map(|x| x.clone()) } else { Err(QueryError::NotCached { - name: self.name, - url: self.url, + name: self.name.clone(), + url: self.url.clone(), }) } } - pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result { - println!("Running Query \"{}\" at \"{}\"", self.name, self.url); - - // println!("{}", - // let pool = ThreadPool::new().unwrap(); - // let mut pool = LocalPool::new(); - // let mut rt = tokio::runtime::Runtime::new().unwrap(); - // let future = async { /* ... */ }; + pub async fn put_cached(&self, result: Value, variables: Map, cache_location: PathBuf) -> Result<(), QueryError> { + if self.cached { + // TODO: don't re-add to cache if fetched from get_cached + println!("Adding to cache: {:?}", cache_location.clone()); + let mut cache: Map = if cache_location.as_path().exists() { + let cache_str = fs::read_to_string(cache_location.clone())?; + serde_json::from_str(&cache_str)? + } else { + Map::new() + }; + let cache_index = format!("{:?}:{:?}", self.name, variables); + cache.insert(cache_index, result); + let cache_json = serde_json::to_string_pretty(&serde_json::to_value(cache).unwrap()).unwrap(); + fs::write(cache_location, cache_json)?; + Ok(()) + } else { + println!("Not cached"); + Ok(()) + } + } + pub async fn run(&self, variables: Map, cache_location: PathBuf) -> Result { + println!("Running Query \"{}\" at \"{}\"", self.name, self.url); let ran_template = self.clone().template.run(variables.clone())?; match serde_json::to_value(ran_template.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { Ok(json) => println!("{}\n", json), Err(e) => println!("Printing query template failed: {}", e), } - // let result_block = async { let result_block = { - match self.clone().get_cached(variables, cache_location).await { - Ok(result) => Ok(result), + match self.clone().get_cached(variables.clone(), cache_location.clone()).await { + Ok(result) => { + println!("Got cached result.."); + Ok(result) + }, Err(_e) => { match self.query_type { QueryType::Get => { let client = Client::new(); - let response = client.get(self.url) + let response = client.get(self.url.clone()) .json(&ran_template) .send() .await .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - if response.status().is_success() { Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) } else { @@ -325,24 +347,42 @@ impl Query { Err(QueryError::RequestFailed { response: response_text, }) + } + }, + QueryType::Put => { + let client = Client::new(); + let response = client.put(self.url.clone()) + .json(&ran_template) + .send() + .await + .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; + if response.status().is_success() { + Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) + } else { + let response_text = match response.text().await { + Ok(text) => text, + Err(e) => format!("error: \n{}", e), + }; + Err(QueryError::RequestFailed { + response: response_text, + }) } }, + } }, } }; - result_block - // .map_err(|e| QueryError::ReqwestError(Arc::new(e))) - - // Ok(result_block) - - // Ok(result_block.wait()?) - - // // pool.spawn_ok(result_block); - // // pool.run_until(result_block).map_err(|e| QueryError::ReqwestError(Arc::new(e))) - // rt.block_on(result_block).map_err(|e| QueryError::ReqwestError(Arc::new(e))) + // result_block. + match result_block { + Ok(result) => { + self.put_cached(result.clone(), variables, cache_location).await?; + Ok(result) + }, + Err(e) => Err(e), + } } } @@ -353,12 +393,16 @@ pub struct Queries { } impl Queries { - pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result, QueryError> { + pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result>, QueryError> { let mut result = Vec::with_capacity(self.queries.len()); let mut stream = stream::iter(self.queries); while let Some(query) = stream.next().await { - result.push(query.run(variables.clone(), cache_location.clone()).await?) + let query_result = query.run(variables.clone(), cache_location.clone()).await?; + let mut query_result_json = Map::new(); + query_result_json.insert("query".to_string(), query.to_json()?); + query_result_json.insert("result".to_string(), query_result); + result.push(query_result_json) } Ok(result) } diff --git a/src/lib.rs b/src/lib.rs index 7de91ad..89bc7d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,9 @@ mod parse; pub use parse::{parse, parse_json}; mod executor; pub use executor::Executor; + +mod rest_api; +pub use rest_api::Api; mod cli; pub use cli::Cli; diff --git a/src/main.rs b/src/main.rs index 8632225..5a40b8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use cryptoscript::{Cli}; use cryptoscript::{TMap, TValue, Template}; use cryptoscript::{Query, QueryType}; +use cryptoscript::{Api}; + use std::marker::PhantomData; // use indexmap::IndexMap; @@ -338,6 +340,38 @@ async fn main() { // &tag=latest // &apikey=YourApiKeyToken + let erc20_request_json = r#" + { + "module": "account", + "action": "tokenbalance", + "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", + "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + "tag": "latest", + "apikey": "4JGE3TQ3ZAGAM7IK86M24DY2H4EH1AIAZ" + } + "#; + let erc20_response_json = r#" + { + "status":"1", + "message":"OK", + "result":"135499" + } + "#; + let erc20_request = serde_json::from_str(erc20_request_json).unwrap(); + let erc20_response = serde_json::from_str(erc20_response_json).unwrap(); + + let erc20_rate_limit_seconds = 1; + let erc20_api: Api = Api::new(erc20_request, erc20_response, erc20_rate_limit_seconds); + let erc20_api_json: serde_json::Value = serde_json::to_value(erc20_api).unwrap(); + let erc20_api_template = Template::from_json(erc20_api_json); + let erc20_api_template_json = serde_json::to_string_pretty(&serde_json::to_value(erc20_api_template.clone()).unwrap()).unwrap(); + println!("ERC-20:"); + println!("{}", erc20_api_template_json); + println!(""); + println!(""); + + + let mut variables = Map::new(); variables.insert("contractaddress".to_string(), Value::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())); variables.insert("address".to_string(), Value::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())); @@ -356,13 +390,13 @@ async fn main() { query_parameters.insert("apikey".to_string(), TValue::Var("apikey".to_string())); template.insert("parameters".to_string(), TValue::Object(query_parameters.clone())); - let _full_template = Template { + let full_template = Template { variables: variables, template: TValue::Object(template), }; - // let json_template = serde_json::to_string_pretty(&serde_json::to_value(full_template.clone()).unwrap()).unwrap(); - // println!("{}", json_template); + let json_template = serde_json::to_string_pretty(&serde_json::to_value(full_template.clone()).unwrap()).unwrap(); + println!("{}", json_template); // { // "variables": { @@ -414,6 +448,8 @@ async fn main() { // let json_query = serde_json::to_string_pretty(&serde_json::to_value(query.clone()).unwrap()).unwrap(); // println!("{}", json_query); + + let cli = Cli::parse(); cli.run().await; } diff --git a/src/rest-api.rs b/src/rest_api.rs similarity index 95% rename from src/rest-api.rs rename to src/rest_api.rs index 3660941..381d47d 100644 --- a/src/rest-api.rs +++ b/src/rest_api.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; /// GET REST Api, located at 'apis/{name}' #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct Api { +pub struct Api { /// Constant required request JSON request: Value, /// Constant response JSON @@ -20,6 +20,15 @@ struct Api { } impl Api { + pub fn new(request: Value, response: Value, rate_limit_seconds: u64) -> Self { + Api { + request: request, + response: response, + rate_limit_seconds: rate_limit_seconds, + last_api_call: None, + } + } + /// Fail if rate_limit_seconds > elapsed_seconds since last called_now pub fn check_rate_limit(&self) -> Result<(), String> { match self.last_api_call { From b162cbd80162016dd813510689b7715a96962be4 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Mon, 28 Mar 2022 19:33:12 -0400 Subject: [PATCH 67/77] add support for monomorphic typing, split out Query into own module --- Cargo.toml | 2 +- src/cli.rs | 37 +++++-- src/elem.rs | 4 +- src/json_template.rs | 226 ------------------------------------------- src/lib.rs | 6 +- src/main.rs | 64 +++--------- src/query.rs | 222 ++++++++++++++++++++++++++++++++++++++++++ src/types.rs | 58 +++++++---- src/types_scratch.rs | 63 ++++++------ 9 files changed, 342 insertions(+), 340 deletions(-) create mode 100644 src/query.rs diff --git a/Cargo.toml b/Cargo.toml index 1ea70ab..2ce2b99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ path = "src/main.rs" [[bin]] name = "rest-api" -path = "src/rest-api.rs" +path = "src/rest_api.rs" [dependencies] actix-web = { version = "4.0.1", default-features = false, features = ["macros"] } diff --git a/src/cli.rs b/src/cli.rs index 010d4d0..8676bff 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,14 +1,11 @@ -// use crate::elem::{StackType}; +use crate::elem::{StackType}; use crate::stack::{Stack}; // use crate::restack::{Restack, RestackError}; // use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; use crate::types_scratch::{Instrs, StackInstructionError}; use crate::instruction::{InstructionError}; use crate::parse::{parse_json, ParseError}; -use crate::json_template::{Query, QueryError, Queries}; - -// use cryptoscript::{parse_json, Elem, ElemSymbol, Executor, Instruction, Instructions, Restack}; -// use cryptoscript::{Stack, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; +use crate::query::{QueryError, Queries}; use std::fs; use std::io; @@ -52,8 +49,11 @@ enum Commands { /// Parse only Parse, - // TODO: implement - // /// Type check only + /// Type check only (monomorphic) + TypeMono, + + // // TODO: implement + // /// Type check only (polymorphic) // Type, } @@ -141,6 +141,19 @@ impl Cli { } } + pub fn type_of_mono(&self) -> Result { + let instructions = self.parse_code()?; + let num_queries = self.parse_queries()?.len(); + + println!("instructions:"); + for instruction in &instructions.instrs { + println!("{:?}", instruction); + } + + println!(""); + Ok(instructions.type_of_mono(num_queries)?) + } + pub async fn parse_and_run_result(&self) -> Result<(), CliError> { let instructions = self.parse_code()?; let mut stack = Stack::new(); @@ -155,8 +168,8 @@ impl Cli { stack.push_elem(query_result) } - println!("stack initialized:"); - stack.debug()?; + // println!("stack initialized:"); + // stack.debug()?; println!("instructions:"); for instruction in &instructions.instrs { @@ -182,6 +195,12 @@ impl Cli { Err(e) => println!("parsing failed:\n{}", e), } }, + Some(Commands::TypeMono) => { + match self.type_of_mono() { + Ok(type_of) => println!("type:\n{}", type_of), + Err(e) => println!("parsing failed:\n{}", e), + } + }, } } } diff --git a/src/elem.rs b/src/elem.rs index 18be54c..f1e37c9 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -330,8 +330,8 @@ impl Elem { } impl ElemType { - fn from_locations(type_set: EnumSet, - locations: Vec) -> Self { + pub fn from_locations(type_set: EnumSet, + locations: Vec) -> Self { ElemType { type_set: type_set, info: locations.iter() diff --git a/src/json_template.rs b/src/json_template.rs index c409741..e00a42b 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -15,15 +15,6 @@ use indexmap::{IndexMap}; use serde_json::{Map, Number, Value}; use thiserror::Error; -//// BEGIN query -use reqwest::{Client, Response}; -use std::fs; -use std::path::PathBuf; - -use tokio_stream::{self as stream, StreamExt}; -//// END query - - #[derive(Clone, Debug, PartialEq, Eq)] pub struct TMap { map: IndexMap, @@ -193,220 +184,3 @@ impl Template { } } - - - - - - - - -//// BEGIN query -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum QueryType { - Get, - Put, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Query { - pub name: String, - pub url: String, - pub template: TValue, - pub cached: bool, - pub query_type: QueryType, -} - -#[derive(Clone, Debug, Error)] -pub enum QueryError { - #[error("Query::get_cached: not cached:\n{name:?}\n{url:?}")] - NotCached { - name: String, - url: String, - }, - - #[error("Query::run: request failed:\nresponse:\n{response}")] - RequestFailed { - response: String, - // response: Arc, - // body: String, - }, - - #[error("TValueRunError:\n{0:?}")] - TValueRunError(TValueRunError), - - #[error("ReqwestError:\n{0}")] - ReqwestError(Arc), - - #[error("StdIoError:\n{0}")] - StdIoError(Arc), - - #[error("SerdeJsonError:\n{0}")] - SerdeJsonError(Arc), -} - -impl From for QueryError { - fn from(error: TValueRunError) -> Self { - Self::TValueRunError(error) - } -} - -impl From for QueryError { - fn from(error: reqwest::Error) -> Self { - Self::ReqwestError(Arc::new(error)) - } -} - -impl From for QueryError { - fn from(error: std::io::Error) -> Self { - Self::StdIoError(Arc::new(error)) - } -} - -impl From for QueryError { - fn from(error: serde_json::Error) -> Self { - Self::SerdeJsonError(Arc::new(error)) - } -} - -impl Query { - pub fn to_json(&self) -> Result { - Ok(serde_json::to_value(self)?) - } - - pub async fn get_cached(&self, variables: Map, cache_location: PathBuf) -> Result { - if self.cached { - println!("Checking cache: {:?}", cache_location.clone()); - let cache_str = fs::read_to_string(cache_location)?; - let cache: Map = serde_json::from_str(&cache_str)?; - let cache_index = format!("{:?}:{:?}", self.name, variables); - cache.get(&cache_index).ok_or_else(|| { - QueryError::NotCached { - name: self.name.clone(), - url: self.url.clone(), - }}).map(|x| x.clone()) - } else { - Err(QueryError::NotCached { - name: self.name.clone(), - url: self.url.clone(), - }) - } - } - - pub async fn put_cached(&self, result: Value, variables: Map, cache_location: PathBuf) -> Result<(), QueryError> { - if self.cached { - // TODO: don't re-add to cache if fetched from get_cached - println!("Adding to cache: {:?}", cache_location.clone()); - let mut cache: Map = if cache_location.as_path().exists() { - let cache_str = fs::read_to_string(cache_location.clone())?; - serde_json::from_str(&cache_str)? - } else { - Map::new() - }; - let cache_index = format!("{:?}:{:?}", self.name, variables); - cache.insert(cache_index, result); - let cache_json = serde_json::to_string_pretty(&serde_json::to_value(cache).unwrap()).unwrap(); - fs::write(cache_location, cache_json)?; - Ok(()) - } else { - println!("Not cached"); - Ok(()) - } - } - - pub async fn run(&self, variables: Map, cache_location: PathBuf) -> Result { - println!("Running Query \"{}\" at \"{}\"", self.name, self.url); - let ran_template = self.clone().template.run(variables.clone())?; - match serde_json::to_value(ran_template.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { - Ok(json) => println!("{}\n", json), - Err(e) => println!("Printing query template failed: {}", e), - } - - let result_block = { - match self.clone().get_cached(variables.clone(), cache_location.clone()).await { - Ok(result) => { - println!("Got cached result.."); - Ok(result) - }, - Err(_e) => { - match self.query_type { - QueryType::Get => { - let client = Client::new(); - let response = client.get(self.url.clone()) - .json(&ran_template) - .send() - .await - .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - if response.status().is_success() { - Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) - } else { - let response_text = match response.text().await { - Ok(text) => text, - Err(e) => format!("error: \n{}", e), - }; - Err(QueryError::RequestFailed { - response: response_text, - }) - } - }, - - QueryType::Put => { - let client = Client::new(); - let response = client.put(self.url.clone()) - .json(&ran_template) - .send() - .await - .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - if response.status().is_success() { - Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) - } else { - let response_text = match response.text().await { - Ok(text) => text, - Err(e) => format!("error: \n{}", e), - }; - Err(QueryError::RequestFailed { - response: response_text, - }) - } - }, - - } - }, - } - }; - - // result_block. - match result_block { - Ok(result) => { - self.put_cached(result.clone(), variables, cache_location).await?; - Ok(result) - }, - Err(e) => Err(e), - } - } -} - - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Queries { - queries: Vec, -} - -impl Queries { - pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result>, QueryError> { - let mut result = Vec::with_capacity(self.queries.len()); - let mut stream = stream::iter(self.queries); - - while let Some(query) = stream.next().await { - let query_result = query.run(variables.clone(), cache_location.clone()).await?; - let mut query_result_json = Map::new(); - query_result_json.insert("query".to_string(), query.to_json()?); - query_result_json.insert("result".to_string(), query_result); - result.push(query_result_json) - } - Ok(result) - } -} -//// END query - - diff --git a/src/lib.rs b/src/lib.rs index 89bc7d2..724e719 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,9 +10,9 @@ mod types; mod types_scratch; pub use types_scratch::{Instruction, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; mod json_template; -pub use json_template::{TMap, TValue, Template, Query, QueryType, QueryError}; -// pub use types::{Instruction, Instructions}; -// , demo_triple, demo_triple_with_tl_handles_intermediate_types, HList +pub use json_template::{TMap, TValue, TValueRunError, Template}; +mod query; +pub use query::{Query, QueryType, QueryError}; mod instruction; pub use instruction::{Instructions}; mod parse; diff --git a/src/main.rs b/src/main.rs index 5a40b8d..7b6643a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ use cryptoscript::{parse_json, Elem, ElemSymbol, Instruction, Instructions}; -use cryptoscript::{Restack, Stack, Instrs}; -use cryptoscript::{AssertTrue, Push, Lookup, UnpackJson, Index, StringEq, CheckEq}; +use cryptoscript::{Restack, Instrs}; +use cryptoscript::{AssertTrue, Push, Lookup, UnpackJson, Index, StringEq}; use cryptoscript::{Cli}; use cryptoscript::{TMap, TValue, Template}; -use cryptoscript::{Query, QueryType}; +// use cryptoscript::{Query, QueryType}; use cryptoscript::{Api}; @@ -364,12 +364,12 @@ async fn main() { let erc20_api: Api = Api::new(erc20_request, erc20_response, erc20_rate_limit_seconds); let erc20_api_json: serde_json::Value = serde_json::to_value(erc20_api).unwrap(); let erc20_api_template = Template::from_json(erc20_api_json); - let erc20_api_template_json = serde_json::to_string_pretty(&serde_json::to_value(erc20_api_template.clone()).unwrap()).unwrap(); - println!("ERC-20:"); - println!("{}", erc20_api_template_json); - println!(""); - println!(""); + let _erc20_api_template_json = serde_json::to_string_pretty(&serde_json::to_value(erc20_api_template.clone()).unwrap()).unwrap(); + // println!("ERC-20:"); + // println!("{}", erc20_api_template_json); + // println!(""); + // println!(""); let mut variables = Map::new(); @@ -390,53 +390,13 @@ async fn main() { query_parameters.insert("apikey".to_string(), TValue::Var("apikey".to_string())); template.insert("parameters".to_string(), TValue::Object(query_parameters.clone())); - let full_template = Template { + let _full_template = Template { variables: variables, template: TValue::Object(template), }; - let json_template = serde_json::to_string_pretty(&serde_json::to_value(full_template.clone()).unwrap()).unwrap(); - println!("{}", json_template); - - // { - // "variables": { - // "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", - // "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", - // "apikey": "YourApiKeyToken" - // }, - // "template": { - // "Object": { - // "type": { - // "String": "GET" - // }, - // "URL": { - // "String": "https://api.etherscan.io/api" - // }, - // "parameters": { - // "Object": { - // "module": { - // "String": "account" - // }, - // "action": { - // "String": "tokenbalance" - // }, - // "contractaddress": { - // "Var": "contractaddress" - // }, - // "address": { - // "Var": "address" - // }, - // "tag": { - // "String": "latest" - // }, - // "apikey": { - // "Var": "apikey" - // } - // } - // } - // } - // } - // } + // let json_template = serde_json::to_string_pretty(&serde_json::to_value(full_template.clone()).unwrap()).unwrap(); + // println!("{}", json_template); // let query = Query { // name: "erc20".to_string(), @@ -448,8 +408,6 @@ async fn main() { // let json_query = serde_json::to_string_pretty(&serde_json::to_value(query.clone()).unwrap()).unwrap(); // println!("{}", json_query); - - let cli = Cli::parse(); cli.run().await; } diff --git a/src/query.rs b/src/query.rs new file mode 100644 index 0000000..7e85a21 --- /dev/null +++ b/src/query.rs @@ -0,0 +1,222 @@ +use crate::json_template::{TValue, TValueRunError}; + +use std::fs; +use std::path::PathBuf; +use std::sync::{Arc}; + +use reqwest::{Client}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use tokio_stream::{self as stream, StreamExt}; +use thiserror::Error; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum QueryType { + Get, + Put, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Query { + pub name: String, + pub url: String, + pub template: TValue, + pub cached: bool, + pub query_type: QueryType, +} + +#[derive(Clone, Debug, Error)] +pub enum QueryError { + #[error("Query::get_cached: not cached:\n{name:?}\n{url:?}")] + NotCached { + name: String, + url: String, + }, + + #[error("Query::run: request failed:\nresponse:\n{response}")] + RequestFailed { + response: String, + }, + + #[error("TValueRunError:\n{0:?}")] + TValueRunError(TValueRunError), + + #[error("ReqwestError:\n{0}")] + ReqwestError(Arc), + + #[error("StdIoError:\n{0}")] + StdIoError(Arc), + + #[error("SerdeJsonError:\n{0}")] + SerdeJsonError(Arc), +} + +impl From for QueryError { + fn from(error: TValueRunError) -> Self { + Self::TValueRunError(error) + } +} + +impl From for QueryError { + fn from(error: reqwest::Error) -> Self { + Self::ReqwestError(Arc::new(error)) + } +} + +impl From for QueryError { + fn from(error: std::io::Error) -> Self { + Self::StdIoError(Arc::new(error)) + } +} + +impl From for QueryError { + fn from(error: serde_json::Error) -> Self { + Self::SerdeJsonError(Arc::new(error)) + } +} + +impl Query { + pub fn to_json(&self) -> Result { + Ok(serde_json::to_value(self)?) + } + + pub async fn get_cached(&self, variables: Map, cache_location: PathBuf) -> Result { + if self.cached { + println!("Checking cache: {:?}", cache_location.clone()); + let cache_str = fs::read_to_string(cache_location)?; + let cache: Map = serde_json::from_str(&cache_str)?; + let cache_index = format!("{:?}:{:?}", self.name, variables); + cache.get(&cache_index).ok_or_else(|| { + QueryError::NotCached { + name: self.name.clone(), + url: self.url.clone(), + }}).map(|x| x.clone()) + } else { + Err(QueryError::NotCached { + name: self.name.clone(), + url: self.url.clone(), + }) + } + } + + pub async fn put_cached(&self, result: Value, variables: Map, cache_location: PathBuf) -> Result<(), QueryError> { + if self.cached { + // TODO: don't re-add to cache if fetched from get_cached + println!("Adding to cache: {:?}", cache_location.clone()); + let mut cache: Map = if cache_location.as_path().exists() { + let cache_str = fs::read_to_string(cache_location.clone())?; + serde_json::from_str(&cache_str)? + } else { + Map::new() + }; + let cache_index = format!("{:?}:{:?}", self.name, variables); + cache.insert(cache_index, result); + let cache_json = serde_json::to_string_pretty(&serde_json::to_value(cache).unwrap()).unwrap(); + fs::write(cache_location, cache_json)?; + Ok(()) + } else { + println!("Not cached"); + Ok(()) + } + } + + pub async fn run(&self, variables: Map, cache_location: PathBuf) -> Result { + println!("Running Query \"{}\" at \"{}\"", self.name, self.url); + let ran_template = self.clone().template.run(variables.clone())?; + match serde_json::to_value(ran_template.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { + Ok(json) => println!("{}\n", json), + Err(e) => println!("Printing query template failed: {}", e), + } + + let result_block = { + match self.clone().get_cached(variables.clone(), cache_location.clone()).await { + Ok(result) => { + println!("Got cached result.."); + Ok(result) + }, + Err(_e) => { + match self.query_type { + QueryType::Get => { + let client = Client::new(); + let response = client.get(self.url.clone()) + .json(&ran_template) + .send() + .await + .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; + if response.status().is_success() { + Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) + } else { + let response_text = match response.text().await { + Ok(text) => text, + Err(e) => format!("error: \n{}", e), + }; + Err(QueryError::RequestFailed { + response: response_text, + }) + } + }, + + QueryType::Put => { + let client = Client::new(); + let response = client.put(self.url.clone()) + .json(&ran_template) + .send() + .await + .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; + if response.status().is_success() { + Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) + } else { + let response_text = match response.text().await { + Ok(text) => text, + Err(e) => format!("error: \n{}", e), + }; + Err(QueryError::RequestFailed { + response: response_text, + }) + } + }, + + } + }, + } + }; + + // result_block. + match result_block { + Ok(result) => { + self.put_cached(result.clone(), variables, cache_location).await?; + Ok(result) + }, + Err(e) => Err(e), + } + } +} + + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Queries { + queries: Vec, +} + +impl Queries { + pub fn len(&self) -> usize { + self.queries.len() + } + + pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result>, QueryError> { + let mut result = Vec::with_capacity(self.queries.len()); + let mut stream = stream::iter(self.queries); + + while let Some(query) = stream.next().await { + let query_result = query.run(variables.clone(), cache_location.clone()).await?; + let mut query_result_json = Map::new(); + query_result_json.insert("query".to_string(), query.to_json()?); + query_result_json.insert("result".to_string(), query_result); + result.push(query_result_json) + } + Ok(result) + } +} +//// END query + + diff --git a/src/types.rs b/src/types.rs index d7f87ac..9a40c33 100644 --- a/src/types.rs +++ b/src/types.rs @@ -501,10 +501,10 @@ impl Type { }) } - - pub fn specialize_to_input_stack(&mut self, stack_type: StackType) -> Result<(), TypeError> { + /// Returns output stack + pub fn specialize_to_input_stack(&mut self, stack_type: StackType) -> Result { if self.i_type.len() <= stack_type.len() { - let mut stack_type_iter = stack_type.into_iter(); + let mut stack_type_iter = stack_type.clone().into_iter(); for (type_id, elem_type) in self.i_type.clone().into_iter().zip(&mut stack_type_iter) { // TODO: elimate copy? let elem_type_copy = elem_type.clone(); @@ -519,11 +519,22 @@ impl Type { let type_id = self.context.push(elem_type); self.i_type.push(type_id); } - Ok(()) + // Ok(()) + + Ok(StackType { + types: self.o_type.clone().into_iter().map(|type_id| { + self.context.clone().get(&type_id, &|| ContextError::SpecializeToInputStack { + type_of: self.clone(), + stack_type: stack_type.clone(), + }) + }).collect::, ContextError>>() + .map_err(|e| TypeError::SpecializeToInputStackContextError(e))?, + }) + } else { Err(TypeError::SpecializeToInputStack { type_of: self.clone(), - stack_type: stack_type, + stack_type: stack_type.clone(), }) } } @@ -757,18 +768,24 @@ pub enum ContextError { #[error("Context::unify failed:\n xs: \n{xs:?}\n xi: \n{xi:?}\n yi: \n{yi:?}\n is_lhs: \n{is_lhs:?}\n")] Unify { - xs: Context, - xi: TypeId, - yi: TypeId, - is_lhs: bool, + xs: Context, + xi: TypeId, + yi: TypeId, + is_lhs: bool, }, #[error("Context::unify failed to unify ElemType's:\n\nxs:\n{xs}\n\nxi:\n{xi}\n\nyi:\n{yi}\n\nelem_error:\n{error}\n")] UnifyElemType { - xs: Context, - xi: TypeId, - yi: TypeId, - error: ElemTypeError, + xs: Context, + xi: TypeId, + yi: TypeId, + error: ElemTypeError, + }, + + #[error("Type::specialize_to_input_stack failed to resolve ElemType's:\ntype_of:\n{type_of}\n\nstack_type:\n{stack_type}")] + SpecializeToInputStack { + type_of: Type, + stack_type: StackType, }, #[error("Context::normalize_on building TypeIdMap failed: \n{0:?}\n")] @@ -785,7 +802,7 @@ impl From for ContextError { } -#[derive(Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error)] pub enum TypeError { #[error("Specialization error:\ntype_id:\n{type_id}\n\nelem_type:\n{elem_type}\n\ncontext:\n{context}\n\nerror:\n{error}")] Specialization { @@ -795,21 +812,24 @@ pub enum TypeError { error: ContextError, }, - #[error("NormalizeContextError {0}")] + #[error("NormalizeContextError\n{0}")] NormalizeContextError(ContextError), - #[error("ComposeContextError {0}")] + #[error("ComposeContextError\n{0}")] ComposeContextError(ContextError), - #[error("TypeError::update_type_id failed when updating the Context: {0}")] + #[error("TypeError::update_type_id failed when updating the Context:\n{0}")] UpdateTypeId(ContextError), - #[error("TypeError::compose disjoint_union {0}")] + #[error("TypeError::compose disjoint_union\n{0}")] ComposeDisjointUnion(ContextError), - #[error("Type::normalize applying TypeIdMap failed: {0:?}")] + #[error("Type::normalize applying TypeIdMap failed:\n{0}")] TypeIdMapError(TypeIdMapError), + #[error("Type::specialize_to_input_stack ContextError:\n{0}")] + SpecializeToInputStackContextError(ContextError), + // TODO: use StackType and Display instead of Vec #[error("Type::specialize_to_input_stack: stack_type shorter than expected:\n{type_of}\n{stack_type}")] SpecializeToInputStack { diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 413a3b5..c170cf4 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,7 +1,7 @@ use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, StackType, AnElem}; use crate::stack::{Stack, StackError}; use crate::restack::{Restack, RestackError}; -use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; +use crate::types::{Context, ContextError, Type, TypeError, Empty, AnError, Nil}; use std::cmp; use std::convert::TryFrom; @@ -95,13 +95,13 @@ pub enum ElemsPopError { error: Arc, }, - #[error("Elems::elem_type (Or): Set includes repeated type: {0:?}")] + #[error("Elems::elem_type (Or): Set includes repeated type:\n{0}")] ElemTypeError(ElemTypeError), #[error("::type_of(): ContextError when adding Tl type: {0:?}")] ReturnOrTl(Arc), - #[error("::type_of(): ContextError when adding type: {0:?}")] + #[error("::type_of(): ContextError when adding type:\n{0}")] ReturnOrContextError(ContextError), } @@ -930,6 +930,9 @@ pub enum StackInstructionError { stack_input: String, }, + #[error("Instrs::type_of_mono type error:\n{0}")] + TypeError(TypeError), + #[error("StackInstructionError::RestackError:\n{0}")] RestackError(RestackError), @@ -1003,31 +1006,9 @@ impl Instr { #[derive(Clone, Debug)] pub struct Instrs { - // TODO: replace Result with Either? pub instrs: Vec, } -// fn example_instrs() -> Instrs { -// Instrs { -// instrs: vec![ -// Arc::new(Concat {}), -// Arc::new(AssertTrue {}), -// Arc::new(Push { push: () }), -// Arc::new(HashSha256 {}), -// Arc::new(Slice {}), -// Arc::new(Index {}), -// Arc::new(ToJson {}), -// Arc::new(Lookup {}), -// Arc::new(UnpackJson { t: PhantomData::<()> }), -// Arc::new(StringToBytes {}), -// Arc::new(CheckLe {}), -// Arc::new(CheckLt {}), -// Arc::new(CheckEq {}) -// ], -// } -// } - - impl Instrs { pub fn new() -> Self { Instrs { @@ -1035,6 +1016,34 @@ impl Instrs { } } + /// Assuming an input stack of [Json, Json, ..] (num_input_json count), + /// what's the monomorphic type of Self? + pub fn type_of_mono(&self, num_input_json: usize) -> Result { + let mut stack_type = (0..num_input_json).map(|_| ElemType::from_locations(EnumSet::only(ElemSymbol::Json), vec![])).collect(); + for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { + println!("------------------------------------------------------------------------------------------"); + println!("line_no: {}", line_no); + println!("{:?}\n", instr_or_restack); + match instr_or_restack { + Instr::Instr(instr) => { + let mut instr_type = instr.type_of() + .map_err(|e| StackInstructionError::ElemsPopError(e))?; + println!("instr: {}\n", instr_type); + stack_type = instr_type.specialize_to_input_stack(stack_type) + .map_err(|e| StackInstructionError::TypeError(e))?; + }, + Instr::Restack(restack) => { + restack.run(&mut stack_type.types) + .map_err(|e| StackInstructionError::RestackError(e))? + }, + } + } + println!("------------------------------------------------------------------------------------------"); + println!("Finished running successfully.\n"); + println!("Final stack:"); + Ok(stack_type) + } + pub fn run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; @@ -1056,7 +1065,7 @@ impl Instrs { // .into_iter() // .map(|x| x.elem_type(vec![])) // .collect()) { - Ok(()) => println!("specialized: {}\n", mut_instr_type), + Ok(_) => println!("specialized: {}\n", mut_instr_type), Err(e) => println!("specialization failed:\n{}\n", e), } }, @@ -1484,7 +1493,7 @@ pub struct UnpackJson { pub struct UnpackJsonError {} impl AnError for UnpackJsonError {} -trait AJsonElem: AnElem { +pub trait AJsonElem: AnElem { fn to_value(self) -> Value; fn from_value(t: PhantomData, x: Value) -> Option where Self: Sized; } From 72bb43c51b4eb2d62d297d37561c961309930967 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 29 Mar 2022 10:35:49 -0400 Subject: [PATCH 68/77] split out into more modules, remove executor, fix query caching, cleanup --- src/an_elem.rs | 217 ++++++++ src/cli.rs | 47 +- src/elem.rs | 474 +---------------- src/elem_type.rs | 246 +++++++++ src/executor.rs | 119 ----- src/instruction.rs | 157 ------ src/json_template.rs | 7 - src/lib.rs | 23 +- src/location.rs | 41 ++ src/parse.rs | 4 +- src/query.rs | 99 ++-- src/stack.rs | 59 +- src/typed_instr.rs | 64 +++ src/typed_instrs.rs | 124 +++++ src/typed_instruction.rs | 94 ++++ src/typed_instructions.rs | 755 ++++++++++++++++++++++++++ src/types.rs | 74 +-- src/types_scratch.rs | 1004 +---------------------------------- src/untyped_instruction.rs | 36 ++ src/untyped_instructions.rs | 54 ++ 20 files changed, 1774 insertions(+), 1924 deletions(-) create mode 100644 src/an_elem.rs create mode 100644 src/elem_type.rs delete mode 100644 src/executor.rs delete mode 100644 src/instruction.rs create mode 100644 src/location.rs create mode 100644 src/typed_instr.rs create mode 100644 src/typed_instrs.rs create mode 100644 src/typed_instruction.rs create mode 100644 src/typed_instructions.rs create mode 100644 src/untyped_instruction.rs create mode 100644 src/untyped_instructions.rs diff --git a/src/an_elem.rs b/src/an_elem.rs new file mode 100644 index 0000000..d075119 --- /dev/null +++ b/src/an_elem.rs @@ -0,0 +1,217 @@ +use crate::elem::{Elem, ElemSymbol}; + +use thiserror::Error; + +use std::fmt::Debug; +use std::marker::PhantomData; + +use enumset::EnumSet; +use serde_json::{Map, Number, Value}; + +pub trait AnElem: Clone + Debug + PartialEq { + // TODO: rename + + // fn elem_symbol(t: PhantomData) -> ElemType; + fn elem_symbol(t: PhantomData) -> EnumSet; + fn to_elem(self) -> Elem; + fn from_elem(t: PhantomData, x: Elem) -> Result; +} + +impl AnElem for Elem { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::all() + } + + fn to_elem(self) -> Elem { + self + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + Ok(x) + } +} + + +impl AnElem for () { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Unit) + } + + fn to_elem(self) -> Elem { + Elem::Unit + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Unit => Ok(()), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for bool { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Bool) + } + + fn to_elem(self) -> Elem { + Elem::Bool(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Bool(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Number { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Number) + } + + fn to_elem(self) -> Elem { + Elem::Number(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Number(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Vec { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Bytes) + } + + fn to_elem(self) -> Elem { + Elem::Bytes(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Bytes(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for String { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::String) + } + + fn to_elem(self) -> Elem { + Elem::String(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::String(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Vec { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Array) + } + + fn to_elem(self) -> Elem { + Elem::Array(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Array(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Map { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Object) + } + + fn to_elem(self) -> Elem { + Elem::Object(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Object(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + +impl AnElem for Value { + fn elem_symbol(_t: PhantomData) -> EnumSet { + EnumSet::only(ElemSymbol::Json) + } + + fn to_elem(self) -> Elem { + Elem::Json(self) + } + + fn from_elem(_t: PhantomData, x: Elem) -> Result { + let elem_symbol = ::elem_symbol(PhantomData); + match x { + Elem::Json(y) => Ok(y), + _ => Err(AnElemError::UnexpectedElemType { + expected: elem_symbol, + found: x, + }), + } + } +} + + +#[derive(Clone, Debug, Error)] +pub enum AnElemError { + #[error("AnElem::from_elem: element popped from the stack\n\n{found}\n\nwasn't the expected type:\n{expected:?}")] + UnexpectedElemType { + expected: EnumSet, + found: Elem, + }, + + #[error(" as AnElem>::from_elem: {e_hd:?}\n{e_tl:?}")] + PopOr { + e_hd: Box, + e_tl: Box, + }, +} diff --git a/src/cli.rs b/src/cli.rs index 8676bff..f9af7cc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,9 +1,9 @@ -use crate::elem::{StackType}; +use crate::elem_type::{StackType}; use crate::stack::{Stack}; -// use crate::restack::{Restack, RestackError}; -// use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; -use crate::types_scratch::{Instrs, StackInstructionError}; -use crate::instruction::{InstructionError}; +use crate::types_scratch::{ElemsPopError}; +use crate::untyped_instruction::{InstructionError}; +use crate::typed_instruction::{StackInstructionError}; +use crate::typed_instrs::{Instrs}; use crate::parse::{parse_json, ParseError}; use crate::query::{QueryError, Queries}; @@ -13,7 +13,6 @@ use std::path::PathBuf; use std::sync::Arc; use clap::{Parser, Subcommand}; -// use clap::derive; use serde_json::Value; use thiserror::Error; @@ -52,8 +51,7 @@ enum Commands { /// Type check only (monomorphic) TypeMono, - // // TODO: implement - // /// Type check only (polymorphic) + // // TODO: implement /// Type check only (polymorphic) // Type, } @@ -64,6 +62,9 @@ pub enum CliError { input_path: Option, }, + #[error("ElemsPopError:\n{0}")] + ElemsPopError(ElemsPopError), + #[error("QueryError:\n{0}")] QueryError(QueryError), @@ -83,6 +84,12 @@ pub enum CliError { SerdeJsonError(Arc), } +impl From for CliError { + fn from(error: ElemsPopError) -> Self { + Self::ElemsPopError(error) + } +} + impl From for CliError { fn from(error: QueryError) -> Self { Self::QueryError(error) @@ -144,13 +151,7 @@ impl Cli { pub fn type_of_mono(&self) -> Result { let instructions = self.parse_code()?; let num_queries = self.parse_queries()?.len(); - - println!("instructions:"); - for instruction in &instructions.instrs { - println!("{:?}", instruction); - } - - println!(""); + instructions.debug()?; Ok(instructions.type_of_mono(num_queries)?) } @@ -167,15 +168,6 @@ impl Cli { for query_result in queries_result { stack.push_elem(query_result) } - - // println!("stack initialized:"); - // stack.debug()?; - - println!("instructions:"); - for instruction in &instructions.instrs { - println!("{:?}", instruction); - } - println!(""); Ok(instructions.run(&mut stack)?) } @@ -191,14 +183,17 @@ impl Cli { None => self.parse_and_run().await, Some(Commands::Parse) => { match self.parse_code() { - Ok(parsed) => println!("parsed:\n{:?}", parsed), + Ok(parsed) => { + parsed.debug() + .unwrap_or_else(|e| println!("Instrs::debug() failed:\n{}", e)) + }, Err(e) => println!("parsing failed:\n{}", e), } }, Some(Commands::TypeMono) => { match self.type_of_mono() { Ok(type_of) => println!("type:\n{}", type_of), - Err(e) => println!("parsing failed:\n{}", e), + Err(e) => println!("type-mono failed:\n{}", e), } }, } diff --git a/src/elem.rs b/src/elem.rs index f1e37c9..a6e272a 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -1,19 +1,14 @@ use crate::arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; -use crate::stack::{Location}; - -use thiserror::Error; use std::cmp; use std::fmt; use std::fmt::{Debug, Display, Formatter}; -use std::marker::PhantomData; -use std::iter::{FromIterator, IntoIterator}; - -use serde::{Deserialize, Serialize}; -use serde_json::{Map, Number, Value}; +use std::iter::IntoIterator; use enumset::{EnumSet, EnumSetType}; use quickcheck::{empty_shrinker, Arbitrary, Gen}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { @@ -127,6 +122,7 @@ impl Arbitrary for Elem { symbol.arbitrary_contents(g) } + // TODO: shrink fn shrink(&self) -> Box> { empty_shrinker() // let self_copy = self.clone(); @@ -220,465 +216,3 @@ impl Elem { } } - - - - - -/* #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] */ -/* pub enum BaseElemType { */ -/* Any, */ -/* Concat, */ -/* Index, */ -/* Slice, */ -/* ElemSymbol(ElemSymbol), */ -/* } */ - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct ElemTypeInfo { - /* base_elem_type: BaseElemType, */ - location: Location, -} - -// TODO: make fields private? -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct ElemType { - pub type_set: EnumSet, - pub info: Vec, -} - -// Formatting: -// ``` -// ElemType { -// type_set: {A, B, C}, -// info: _, -// } -// ``` -// -// Results in: -// ``` -// {A, B, C} -// ``` -impl Display for ElemType { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, - "{{{}}}", - self.type_set.iter() - .fold(String::new(), - |memo, x| { - let x_str: &'static str = From::from(x); - if memo == "" { - x_str.to_string() - } else { - memo + ", " + &x_str.to_string() - } - } - )) - } -} - -#[cfg(test)] -mod elem_type_display_tests { - use super::*; - - #[test] - fn test_empty() { - let elem_type = ElemType { - type_set: EnumSet::empty(), - info: vec![], - }; - assert_eq!("{}", format!("{}", elem_type)); - } - - #[test] - fn test_singleton() { - for elem_symbol in EnumSet::all().iter() { - let elem_type = ElemType { - type_set: EnumSet::only(elem_symbol), - info: vec![], - }; - assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), - format!("{}", elem_type)); - } - } - - #[test] - fn test_all() { - assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", - format!("{}", ElemType::any(vec![]))); - } -} - -impl ElemSymbol { - pub fn elem_type(&self, locations: Vec) -> ElemType { - ElemType { - type_set: EnumSet::only(*self), - info: locations.iter() - .map(|&location| - ElemTypeInfo { - // base_elem_type: BaseElemType::ElemSymbol(*self), - location: location, - }).collect(), - } - } -} - -impl Elem { - pub fn elem_type(&self, locations: Vec) -> ElemType { - self.symbol().elem_type(locations) - } -} - -impl ElemType { - pub fn from_locations(type_set: EnumSet, - locations: Vec) -> Self { - ElemType { - type_set: type_set, - info: locations.iter() - .map(|&location| - ElemTypeInfo { - location: location, - }).collect(), - } - } - - pub fn any(locations: Vec) -> Self { - Self::from_locations( - EnumSet::all(), - locations) - } - - pub fn union(&self, other: Self) -> Result { - let both = self.type_set.union(other.type_set); - let mut both_info = self.info.clone(); - both_info.append(&mut other.info.clone()); - Ok(ElemType { - type_set: both, - info: both_info, - }) - } - - pub fn unify(&self, other: Self) -> Result { - let both = self.type_set.intersection(other.type_set); - if both.is_empty() { - Err(ElemTypeError::UnifyEmpty { - lhs: self.clone(), - rhs: other.clone(), - }) - } else { - let mut both_info = self.info.clone(); - both_info.append(&mut other.info.clone()); - Ok(ElemType { - type_set: both, - info: both_info, - }) - } - } -} - -#[derive(Clone, Debug, PartialEq, Error)] -pub enum ElemTypeError { - #[error("ElemType::unify applied to non-intersecting types:\nlhs:\n{lhs}\nrhs:\n{rhs}")] - UnifyEmpty { - lhs: ElemType, - rhs: ElemType, - }, -} - - - -// BEGIN DebugAsDisplay -#[derive(Clone, PartialEq, Eq)] -struct DebugAsDisplay -where - T: Display, -{ - t: T, -} - -impl Display for DebugAsDisplay -where - T: Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self.t) - } -} - -impl Debug for DebugAsDisplay -where - T: Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self.t) - } -} -// END DebugAsDisplay - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StackType { - pub types: Vec, -} - -impl IntoIterator for StackType { - type Item = ElemType; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.types.into_iter() - } -} - -impl FromIterator for StackType { - fn from_iter(iter: T) -> Self - where - T: IntoIterator, - { - StackType { - types: FromIterator::from_iter(iter), - } - } -} - -impl StackType { - pub fn len(&self) -> usize { - self.types.len() - } - - pub fn push(&mut self, elem_type: ElemType) -> () { - self.types.insert(0, elem_type) - } - - pub fn push_n(&mut self, elem_type: ElemType, count: usize) -> () { - for _index in 0..count { - self.push(elem_type.clone()) - } - } -} - -// Uses DebugAsDisplay to eliminate '"' around strings: -// ["{Number}", "{Array, Object}"] -> [{Number}, {Array, Object}] -impl Display for StackType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.debug_list() - .entries(self.types - .iter() - .map(|x| DebugAsDisplay { t: format!("{}", x) })) - .finish()?; - Ok(()) - } -} - - - - - - -pub trait AnElem: Clone + std::fmt::Debug + PartialEq { - // TODO: rename - - // fn elem_symbol(t: PhantomData) -> ElemType; - fn elem_symbol(t: PhantomData) -> EnumSet; - fn to_elem(self) -> Elem; - fn from_elem(t: PhantomData, x: Elem) -> Result; -} - -impl AnElem for Elem { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::all() - } - - fn to_elem(self) -> Elem { - self - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - Ok(x) - } -} - - -impl AnElem for () { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Unit) - } - - fn to_elem(self) -> Elem { - Elem::Unit - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Unit => Ok(()), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for bool { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Bool) - } - - fn to_elem(self) -> Elem { - Elem::Bool(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Bool(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Number { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Number) - } - - fn to_elem(self) -> Elem { - Elem::Number(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Number(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Vec { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Bytes) - } - - fn to_elem(self) -> Elem { - Elem::Bytes(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Bytes(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for String { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::String) - } - - fn to_elem(self) -> Elem { - Elem::String(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::String(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Vec { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Array) - } - - fn to_elem(self) -> Elem { - Elem::Array(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Array(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Map { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Object) - } - - fn to_elem(self) -> Elem { - Elem::Object(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Object(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - -impl AnElem for Value { - fn elem_symbol(_t: PhantomData) -> EnumSet { - EnumSet::only(ElemSymbol::Json) - } - - fn to_elem(self) -> Elem { - Elem::Json(self) - } - - fn from_elem(_t: PhantomData, x: Elem) -> Result { - let elem_symbol = ::elem_symbol(PhantomData); - match x { - Elem::Json(y) => Ok(y), - _ => Err(AnElemError::UnexpectedElemType { - expected: elem_symbol, - found: x, - }), - } - } -} - - -#[derive(Clone, Debug, Error)] -pub enum AnElemError { - #[error("AnElem::from_elem: element popped from the stack\n\n{found}\n\nwasn't the expected type:\n{expected:?}")] - UnexpectedElemType { - expected: EnumSet, - found: Elem, - }, - - #[error(" as AnElem>::from_elem: {e_hd:?}\n{e_tl:?}")] - PopOr { - e_hd: Box, - e_tl: Box, - }, -} - diff --git a/src/elem_type.rs b/src/elem_type.rs new file mode 100644 index 0000000..4413713 --- /dev/null +++ b/src/elem_type.rs @@ -0,0 +1,246 @@ +use crate::location::{Location}; +use crate::elem::{Elem, ElemSymbol}; + +use thiserror::Error; + +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::iter::{FromIterator, IntoIterator}; + +use enumset::{EnumSet}; +use serde::{Deserialize, Serialize}; + + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemTypeInfo { + location: Location, +} + +// TODO: make fields private? +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ElemType { + pub type_set: EnumSet, + pub info: Vec, +} + +// Formatting: +// ``` +// ElemType { +// type_set: {A, B, C}, +// info: _, +// } +// ``` +// +// Results in: +// ``` +// {A, B, C} +// ``` +impl Display for ElemType { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{{{}}}", + self.type_set.iter() + .fold(String::new(), + |memo, x| { + let x_str: &'static str = From::from(x); + if memo == "" { + x_str.to_string() + } else { + memo + ", " + &x_str.to_string() + } + } + )) + } +} + +#[cfg(test)] +mod elem_type_display_tests { + use super::*; + + #[test] + fn test_empty() { + let elem_type = ElemType { + type_set: EnumSet::empty(), + info: vec![], + }; + assert_eq!("{}", format!("{}", elem_type)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + assert_eq!(format!("{{{}}}", Into::<&'static str>::into(elem_symbol)), + format!("{}", elem_type)); + } + } + + #[test] + fn test_all() { + assert_eq!("{Unit, Bool, Number, Bytes, String, Array, Object, JSON}", + format!("{}", ElemType::any(vec![]))); + } +} + +impl ElemSymbol { + pub fn elem_type(&self, locations: Vec) -> ElemType { + ElemType { + type_set: EnumSet::only(*self), + info: locations.iter() + .map(|&location| + ElemTypeInfo { + location: location, + }).collect(), + } + } +} + +impl Elem { + pub fn elem_type(&self, locations: Vec) -> ElemType { + self.symbol().elem_type(locations) + } +} + +impl ElemType { + pub fn from_locations(type_set: EnumSet, + locations: Vec) -> Self { + ElemType { + type_set: type_set, + info: locations.iter() + .map(|&location| + ElemTypeInfo { + location: location, + }).collect(), + } + } + + pub fn any(locations: Vec) -> Self { + Self::from_locations( + EnumSet::all(), + locations) + } + + pub fn union(&self, other: Self) -> Result { + let both = self.type_set.union(other.type_set); + let mut both_info = self.info.clone(); + both_info.append(&mut other.info.clone()); + Ok(ElemType { + type_set: both, + info: both_info, + }) + } + + pub fn unify(&self, other: Self) -> Result { + let both = self.type_set.intersection(other.type_set); + if both.is_empty() { + Err(ElemTypeError::UnifyEmpty { + lhs: self.clone(), + rhs: other.clone(), + }) + } else { + let mut both_info = self.info.clone(); + both_info.append(&mut other.info.clone()); + Ok(ElemType { + type_set: both, + info: both_info, + }) + } + } +} + +#[derive(Clone, Debug, PartialEq, Error)] +pub enum ElemTypeError { + #[error("ElemType::unify applied to non-intersecting types:\nlhs:\n{lhs}\nrhs:\n{rhs}")] + UnifyEmpty { + lhs: ElemType, + rhs: ElemType, + }, +} + + + +// BEGIN DebugAsDisplay +#[derive(Clone, PartialEq, Eq)] +struct DebugAsDisplay +where + T: Display, +{ + t: T, +} + +impl Display for DebugAsDisplay +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.t) + } +} + +impl Debug for DebugAsDisplay +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{}", self.t) + } +} +// END DebugAsDisplay + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StackType { + pub types: Vec, +} + +impl IntoIterator for StackType { + type Item = ElemType; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.types.into_iter() + } +} + +impl FromIterator for StackType { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + StackType { + types: FromIterator::from_iter(iter), + } + } +} + +impl StackType { + pub fn len(&self) -> usize { + self.types.len() + } + + pub fn push(&mut self, elem_type: ElemType) -> () { + self.types.insert(0, elem_type) + } + + pub fn push_n(&mut self, elem_type: ElemType, count: usize) -> () { + for _index in 0..count { + self.push(elem_type.clone()) + } + } +} + +// Uses DebugAsDisplay to eliminate '"' around strings: +// ["{Number}", "{Array, Object}"] -> [{Number}, {Array, Object}] +impl Display for StackType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_list() + .entries(self.types + .iter() + .map(|x| DebugAsDisplay { t: format!("{}", x) })) + .finish()?; + Ok(()) + } +} + diff --git a/src/executor.rs b/src/executor.rs deleted file mode 100644 index 6687609..0000000 --- a/src/executor.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::restack::{Restack, RestackError}; -use crate::elem::{Elem}; -// use crate::types::{Instruction, Instructions}; -// use crate::instruction::{Instruction, Instructions}; -use thiserror::Error; - -#[derive(Debug, Default)] -pub struct Executor { - stack: Vec, -} - -impl Executor { - // pub fn consume(&mut self, expressions: Instructions) -> Result<(), ExecError> { - // self.debug()?; - // for expr in expressions { - // println!("------------------------------------------------------------------------------------------"); - // println!("#: {:?}", expr); - // match expr { - // Instruction::Restack(restack) => self.restack(restack)?, - // Instruction::Push(elem) => self.push(elem), - - // Instruction::AssertTrue => self.pop()?.assert_true()?, - - // Instruction::HashSha256 => self.pop_push(Elem::sha256)?, - // Instruction::ToJson => self.pop_push(Elem::to_json)?, - // Instruction::UnpackJson(elem_symbol) => self.pop_push(|x| x.unpack_json(elem_symbol))?, - // Instruction::StringToBytes => self.pop_push(Elem::string_to_bytes)?, - - // Instruction::CheckLe => self.pop_push2(Elem::check_le)?, - // Instruction::CheckLt => self.pop_push2(Elem::check_lt)?, - // Instruction::CheckEq => self.pop_push2(Elem::check_eq)?, - // Instruction::Concat => self.pop_push2(Elem::concat)?, - // Instruction::Index => self.pop_push2(Elem::index)?, - // Instruction::Lookup => self.pop_push2(Elem::lookup)?, - - // Instruction::Slice => self.pop_push3(Elem::slice)?, - // } - // self.debug()?; - // } - // Ok(()) - // } - - // pub fn debug(&self) -> Result<(), ExecError> { - // println!("------------------------------------------------------------------------------------------"); - // for stack_elem in &self.stack { - // println!("------------------------------"); - // println!("{}", serde_json::to_string_pretty(stack_elem)?) - // } - // Ok(()) - // } - - fn restack(&mut self, restack: Restack) -> Result<(), ExecError> { - restack.run(&mut self.stack)?; - Ok(()) - } - - // // TODO: since pop can fail, require passing debug info to it - // // (so we know what we were expecting) - // fn pop(&mut self) -> Result { - // let result = self.stack.get(0).ok_or_else(|| ExecError::EmptyStack).map(|x|x.clone())?; - // self.stack = self.stack.drain(1..).collect(); - // Ok(result.clone()) - // } - - // pub fn push(&mut self, elem: Elem) { - // let mut memo = vec![elem]; - // memo.append(&mut self.stack.clone()); - // self.stack = memo; - // } - - // pub fn pop_push(&mut self, f: impl Fn(Elem) -> Result) -> Result<(), ExecError> { - // let one = self.pop()?; - // self.push(f(one)?); - // Ok(()) - // } - - // pub fn pop_push2(&mut self, f: impl Fn(Elem, Elem) -> Result) -> Result<(), ExecError> { - // let one = self.pop()?; - // let other = self.pop()?; - // self.push(f(one, other)?); - // Ok(()) - // } - - // pub fn pop_push3(&mut self, f: impl Fn(Elem, Elem, Elem) -> Result) -> Result<(), ExecError> { - // let first = self.pop()?; - // let second = self.pop()?; - // let third = self.pop()?; - // self.push(f(first, second, third)?); - // Ok(()) - // } -} - -#[derive(Debug, Error)] -pub enum ExecError { - // #[error("ElemError({0:?})")] - // ElemError(ElemError), - #[error("tried to pop from an empty stack")] - EmptyStack, - #[error("restack failed: {0}")] - RestackExecError(RestackError), -} - -// impl From for ExecError { -// fn from(error: ElemError) -> Self { -// ExecError::ElemError(error) -// } -// } - -// impl From for ExecError { -// fn from(error: serde_json::Error) -> Self { -// ExecError::ElemError(ElemError::ToFromJsonFailed(format!("{}", error))) -// } -// } - -impl From for ExecError { - fn from (error: RestackError) -> Self { - ExecError::RestackExecError(error) - } -} diff --git a/src/instruction.rs b/src/instruction.rs deleted file mode 100644 index d01c650..0000000 --- a/src/instruction.rs +++ /dev/null @@ -1,157 +0,0 @@ -use crate::restack::{Restack, RestackError}; -use crate::elem::{ElemSymbol, ElemType}; -use crate::stack::{LineNo}; -use crate::types::{TypeId, Context, Type, TypeError}; -use crate::types_scratch::{StackInstructionError, Instruction, Instr, Instrs, - Push, HashSha256, CheckLe, CheckLt, CheckEq, - StringEq, BytesEq, Concat, Slice, Index, Lookup, AssertTrue, ToJson, UnpackJson, StringToBytes}; - -// use std::collections::BTreeMap; -// use std::cmp; -// use std::fmt; -// use std::fmt::{Display, Formatter}; -// // use std::alloc::string; -// use std::marker::PhantomData; -use std::sync::Arc; -use std::marker::PhantomData; - -// use enumset::{EnumSet, enum_set}; -use serde::{Deserialize, Serialize}; -use serde_json::{Map, Number, Value}; -use thiserror::Error; - - -#[derive(Clone, Debug, Error)] -pub enum InstructionError { - #[error("Instruction::to_instr UnpackJson does not support: {elem_symbol:?}")] - UnpackJson { - elem_symbol: ElemSymbol, - } -} - -impl Instruction { - pub fn to_instr(self) -> Result { - match self { - Self::Push(elem) => Ok(Instr::Instr(Arc::new(Push { push: elem }))), - Self::Restack(restack) => Ok(Instr::Restack(restack.clone())), - Self::HashSha256 => Ok(Instr::Instr(Arc::new(HashSha256 {}))), - Self::CheckLe => Ok(Instr::Instr(Arc::new(CheckLe {}))), - Self::CheckLt => Ok(Instr::Instr(Arc::new(CheckLt {}))), - Self::CheckEq => Ok(Instr::Instr(Arc::new(CheckEq {}))), - Self::StringEq => Ok(Instr::Instr(Arc::new(StringEq {}))), - Self::BytesEq => Ok(Instr::Instr(Arc::new(BytesEq {}))), - Self::Concat => Ok(Instr::Instr(Arc::new(Concat {}))), - Self::Slice => Ok(Instr::Instr(Arc::new(Slice {}))), - Self::Index => Ok(Instr::Instr(Arc::new(Index {}))), - Self::Lookup => Ok(Instr::Instr(Arc::new(Lookup {}))), - Self::AssertTrue => Ok(Instr::Instr(Arc::new(AssertTrue {}))), - Self::ToJson => Ok(Instr::Instr(Arc::new(ToJson {}))), - Self::UnpackJson(elem_symbol) => { - match elem_symbol { - ElemSymbol::Unit => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::<()> }))), - ElemSymbol::Bool => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), - ElemSymbol::Number => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), - ElemSymbol::String => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), - ElemSymbol::Array => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::> }))), - ElemSymbol::Object => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::> }))), - _ => Err(InstructionError::UnpackJson { - elem_symbol: elem_symbol, - }) - } - }, - Self::StringToBytes => Ok(Instr::Instr(Arc::new(StringToBytes {}))), - } - } -} - -#[derive(Debug, Error)] -pub enum InstructionTypeError { - // TODO: move to instruction:: - #[error("Instruction::type_of resulted in an error involving: {instruction:?};\n {error:?}")] - InstructionTypeOfDetail { - instruction: Instruction, - error: Box, - }, - - // TODO: move to instruction:: - #[error("Instructions::type_of called on an empty Vec of Instruction's")] - InstructionsTypeOfEmpty, - - // TODO: move to instruction:: - #[error("Instructions::type_of resulted in an error on line: {line_no:?};\n {error:?}")] - InstructionsTypeOfLineNo { - line_no: usize, - error: Box, - }, - - // TODO: move to instruction:: - #[error("Instruction::type_of resulted in restack error: {0:?}")] - InstructionTypeOfRestack(RestackError), - -} - -impl Restack { - // TODO: fix locations: out locations are mislabeled as in locations - pub fn type_of(&self, line_no: LineNo) -> Result { - let mut context = Context::new(); - let mut restack_type: Vec = (0..self.restack_depth) - .map(|x| context.push(ElemType::any(vec![line_no.in_at(x)]))) - .collect(); - let i_type = restack_type.clone(); - self.run(&mut restack_type)?; - Ok(Type { - context: context, - i_type: i_type, - o_type: restack_type, - }) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -pub struct Instructions { - pub instructions: Vec, -} - -impl IntoIterator for Instructions { - type Item = Instruction; - type IntoIter = as std::iter::IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.instructions.into_iter() - } -} - - -impl Instructions { - pub fn to_instrs(self) -> Result { - Ok(Instrs { - instrs: self.into_iter().map(|x| x.to_instr()).collect::, InstructionError>>()?, - }) - } -} - -impl Instrs { - pub fn to_instructions(self) -> Result { - Ok(Instructions { - instructions: self.instrs.into_iter().map(|x| x.to_instruction()).collect::, StackInstructionError>>()?, - }) - } -} - -// Test program #1: [] -> [] -// -// Instruction::Push(Elem::Bool(true)), -// Instruction::Restack(Restack::id()), -// Instruction::AssertTrue, - -// Test program #2 -// -// ∀ (t0 ∊ {JSON}), -// ∀ (t1 ∊ {JSON}), -// ∀ (t2 ∊ {Object}), -// [t1] -> -// [t0, t2, t1] -// -// Instruction::Push(Elem::Json(Default::default())), -// Instruction::UnpackJson(ElemSymbol::Object), -// Instruction::Restack(Restack::dup()), diff --git a/src/json_template.rs b/src/json_template.rs index e00a42b..e06c1ab 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -1,10 +1,4 @@ -// use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, StackType, AnElem}; -// use crate::stack::{Stack, StackError}; -// use crate::restack::{Restack, RestackError}; -// use crate::types::{Context, ContextError, Type, Empty, AnError, Nil}; - use std::fmt; -// use std::fmt::Debug; use std::sync::{Arc}; use std::marker::PhantomData; @@ -183,4 +177,3 @@ impl Template { self.template.run(self.variables) } } - diff --git a/src/lib.rs b/src/lib.rs index 724e719..70a5cfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,21 +4,34 @@ mod arbitrary; pub use arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; mod elem; pub use elem::{Elem, ElemSymbol}; +mod elem_type; +pub use elem_type::{ElemType, StackType}; +mod an_elem; +pub use an_elem::{AnElem, AnElemError}; +mod location; +pub use location::{LineNo}; mod stack; pub use stack::{Stack, StackError}; mod types; mod types_scratch; -pub use types_scratch::{Instruction, Instrs, AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; mod json_template; pub use json_template::{TMap, TValue, TValueRunError, Template}; mod query; pub use query::{Query, QueryType, QueryError}; -mod instruction; -pub use instruction::{Instructions}; +mod untyped_instruction; +pub use untyped_instruction::{Instruction}; +mod untyped_instructions; +pub use untyped_instructions::{Instructions}; +mod typed_instruction; +pub use typed_instruction::{IsInstructionT}; +mod typed_instructions; +pub use typed_instructions::{AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; +mod typed_instr; +pub use typed_instr::Instr; +mod typed_instrs; +pub use typed_instrs::Instrs; mod parse; pub use parse::{parse, parse_json}; -mod executor; -pub use executor::Executor; mod rest_api; pub use rest_api::Api; diff --git a/src/location.rs b/src/location.rs new file mode 100644 index 0000000..1667deb --- /dev/null +++ b/src/location.rs @@ -0,0 +1,41 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct LineNo { + pub line_no: usize, +} + +impl From for LineNo { + fn from(line_no: usize) -> Self { + LineNo { + line_no: line_no, + } + } +} + +pub type ArgumentIndex = usize; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Location { + line_no: LineNo, + argument_index: ArgumentIndex, + is_input: bool, +} + +impl LineNo { + pub fn in_at(&self, argument_index: usize) -> Location { + Location { + line_no: *self, + argument_index: argument_index, + is_input: true, + } + } + + pub fn out_at(&self, argument_index: usize) -> Location { + Location { + line_no: *self, + argument_index: argument_index, + is_input: false, + } + } +} diff --git a/src/parse.rs b/src/parse.rs index 4bfb0d3..509bf07 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -10,8 +10,8 @@ /// digit hexadecimal number. use crate::elem::{Elem}; -use crate::types_scratch::{Instruction}; -use crate::instruction::{Instructions}; +use crate::untyped_instruction::{Instruction}; +use crate::untyped_instructions::{Instructions}; use std::str::FromStr; diff --git a/src/query.rs b/src/query.rs index 7e85a21..4a4c6fc 100644 --- a/src/query.rs +++ b/src/query.rs @@ -7,8 +7,8 @@ use std::sync::{Arc}; use reqwest::{Client}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; -use tokio_stream::{self as stream, StreamExt}; use thiserror::Error; +use tokio_stream::{self as stream, StreamExt}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum QueryType { @@ -101,7 +101,6 @@ impl Query { pub async fn put_cached(&self, result: Value, variables: Map, cache_location: PathBuf) -> Result<(), QueryError> { if self.cached { - // TODO: don't re-add to cache if fetched from get_cached println!("Adding to cache: {:?}", cache_location.clone()); let mut cache: Map = if cache_location.as_path().exists() { let cache_str = fs::read_to_string(cache_location.clone())?; @@ -127,72 +126,46 @@ impl Query { Ok(json) => println!("{}\n", json), Err(e) => println!("Printing query template failed: {}", e), } - - let result_block = { - match self.clone().get_cached(variables.clone(), cache_location.clone()).await { - Ok(result) => { - println!("Got cached result.."); - Ok(result) - }, - Err(_e) => { - match self.query_type { - QueryType::Get => { - let client = Client::new(); - let response = client.get(self.url.clone()) - .json(&ran_template) - .send() - .await - .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - if response.status().is_success() { - Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) - } else { - let response_text = match response.text().await { - Ok(text) => text, - Err(e) => format!("error: \n{}", e), - }; - Err(QueryError::RequestFailed { - response: response_text, - }) - } - }, - - QueryType::Put => { - let client = Client::new(); - let response = client.put(self.url.clone()) - .json(&ran_template) - .send() - .await - .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - if response.status().is_success() { - Ok(response.json().await.map_err(|e| QueryError::ReqwestError(Arc::new(e)))?) - } else { - let response_text = match response.text().await { - Ok(text) => text, - Err(e) => format!("error: \n{}", e), - }; - Err(QueryError::RequestFailed { - response: response_text, - }) - } - }, - - } - }, - } - }; - - // result_block. - match result_block { + match self.clone().get_cached(variables.clone(), cache_location.clone()).await { Ok(result) => { - self.put_cached(result.clone(), variables, cache_location).await?; + println!("Got cached result..\n"); Ok(result) }, - Err(e) => Err(e), + Err(_e) => { + let client = Client::new(); + let request_builder = match self.query_type { + QueryType::Get => { + client.get(self.url.clone()) + }, + QueryType::Put => { + client.put(self.url.clone()) + }, + }; + let response = request_builder + .json(&ran_template) + .send() + .await + .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; + if response.status().is_success() { + let result: Value = response.json() + .await + .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; + self.put_cached(result.clone(), variables, cache_location).await?; + Ok(result) + } else { + let response_text = response.text() + .await + .unwrap_or_else(|e| format!("error: \n{}", e)); + Err(QueryError::RequestFailed { + response: response_text, + }) + } + }, } } } - +/// An ordered series of Queries #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Queries { queries: Vec, @@ -206,7 +179,6 @@ impl Queries { pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result>, QueryError> { let mut result = Vec::with_capacity(self.queries.len()); let mut stream = stream::iter(self.queries); - while let Some(query) = stream.next().await { let query_result = query.run(variables.clone(), cache_location.clone()).await?; let mut query_result_json = Map::new(); @@ -217,6 +189,3 @@ impl Queries { Ok(result) } } -//// END query - - diff --git a/src/stack.rs b/src/stack.rs index 4411594..47680f2 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,26 +1,18 @@ -// use crate::restack::{RestackError}; -use crate::elem::{Elem, StackType, AnElem, AnElemError, ElemSymbol}; - -// use std::collections::BTreeMap; -// use std::cmp; -// use std::iter::{FromIterator}; +use crate::elem::{Elem, ElemSymbol}; +use crate::elem_type::{StackType}; +use crate::an_elem::{AnElem, AnElemError}; +use crate::location::{LineNo}; use std::fmt; use std::fmt::{Display, Formatter}; -// // use std::alloc::string; -// use std::marker::PhantomData; -// use std::sync::Arc; use std::marker::PhantomData; use enumset::{EnumSet}; use serde::{Deserialize, Serialize}; -// use serde_json::{Map, Number, Value}; use thiserror::Error; use generic_array::{GenericArray, ArrayLength}; use typenum::marker_traits::Unsigned; - -// TODO: use for execution // TODO: pub field needed? #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Stack { @@ -61,7 +53,6 @@ impl Stack { pub fn push(&mut self, elem: Elem) { let mut memo = vec![elem]; - // memo.append(&mut self.stack.clone()); memo.append(&mut self.stack); self.stack = memo; } @@ -104,7 +95,6 @@ impl Stack { } - #[derive(Clone, Debug, Error)] pub enum StackError { #[error("Stack::pop: tried to pop from an empty stack")] @@ -136,44 +126,3 @@ impl From for StackError { Self::AnElemError(x) } } - -// TODO: relocate LineNo, ArgumentIndex, Location -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct LineNo { - pub line_no: usize, -} - -impl From for LineNo { - fn from(line_no: usize) -> Self { - LineNo { - line_no: line_no, - } - } -} - -pub type ArgumentIndex = usize; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Location { - line_no: LineNo, - argument_index: ArgumentIndex, - is_input: bool, -} - -impl LineNo { - pub fn in_at(&self, argument_index: usize) -> Location { - Location { - line_no: *self, - argument_index: argument_index, - is_input: true, - } - } - - pub fn out_at(&self, argument_index: usize) -> Location { - Location { - line_no: *self, - argument_index: argument_index, - is_input: false, - } - } -} diff --git a/src/typed_instr.rs b/src/typed_instr.rs new file mode 100644 index 0000000..d7cc23e --- /dev/null +++ b/src/typed_instr.rs @@ -0,0 +1,64 @@ +use crate::elem::{ElemSymbol}; +use crate::restack::{Restack}; +use crate::untyped_instruction::{Instruction, InstructionError}; +use crate::typed_instruction::{IsStackInstruction, StackInstructionError}; +use crate::typed_instructions::{AssertTrue, Lookup, Concat, Slice, Push, + StringEq, BytesEq, ToJson, Index, CheckLe, CheckLt, CheckEq, HashSha256, + StringToBytes, UnpackJson}; + +use std::marker::PhantomData; +use std::fmt::Debug; +use std::sync::{Arc}; + +use serde_json::{Map, Number, Value}; + +#[derive(Clone, Debug)] +pub enum Instr { + Instr(Arc), + Restack(Restack), +} + +impl Instr { + pub fn to_instruction(&self) -> Result { + match self { + Self::Instr(instr) => instr.to_instruction(), + Self::Restack(restack) => Ok(Instruction::Restack(restack.clone())), + } + } +} + +impl Instruction { + pub fn to_instr(self) -> Result { + match self { + Self::Push(elem) => Ok(Instr::Instr(Arc::new(Push { push: elem }))), + Self::Restack(restack) => Ok(Instr::Restack(restack.clone())), + Self::HashSha256 => Ok(Instr::Instr(Arc::new(HashSha256 {}))), + Self::CheckLe => Ok(Instr::Instr(Arc::new(CheckLe {}))), + Self::CheckLt => Ok(Instr::Instr(Arc::new(CheckLt {}))), + Self::CheckEq => Ok(Instr::Instr(Arc::new(CheckEq {}))), + Self::StringEq => Ok(Instr::Instr(Arc::new(StringEq {}))), + Self::BytesEq => Ok(Instr::Instr(Arc::new(BytesEq {}))), + Self::Concat => Ok(Instr::Instr(Arc::new(Concat {}))), + Self::Slice => Ok(Instr::Instr(Arc::new(Slice {}))), + Self::Index => Ok(Instr::Instr(Arc::new(Index {}))), + Self::Lookup => Ok(Instr::Instr(Arc::new(Lookup {}))), + Self::AssertTrue => Ok(Instr::Instr(Arc::new(AssertTrue {}))), + Self::ToJson => Ok(Instr::Instr(Arc::new(ToJson {}))), + Self::UnpackJson(elem_symbol) => { + match elem_symbol { + ElemSymbol::Unit => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::<()> }))), + ElemSymbol::Bool => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), + ElemSymbol::Number => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), + ElemSymbol::String => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData:: }))), + ElemSymbol::Array => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::> }))), + ElemSymbol::Object => Ok(Instr::Instr(Arc::new(UnpackJson { t: PhantomData::> }))), + _ => Err(InstructionError::UnpackJson { + elem_symbol: elem_symbol, + }) + } + }, + Self::StringToBytes => Ok(Instr::Instr(Arc::new(StringToBytes {}))), + } + } +} + diff --git a/src/typed_instrs.rs b/src/typed_instrs.rs new file mode 100644 index 0000000..65bf17f --- /dev/null +++ b/src/typed_instrs.rs @@ -0,0 +1,124 @@ +use crate::elem::{ElemSymbol}; +use crate::elem_type::{ElemType, StackType}; +use crate::stack::{Stack}; +use crate::restack::{Restack}; +use crate::types_scratch::{ElemsPopError}; +use crate::typed_instruction::{IsStackInstruction, StackInstructionError}; +use crate::typed_instr::{Instr}; + +use std::fmt::Debug; +use std::sync::{Arc}; + +use enumset::EnumSet; + +#[derive(Clone, Debug)] +pub struct Instrs { + pub instrs: Vec, +} + +impl Instrs { + pub fn new() -> Self { + Instrs { + instrs: vec![], + } + } + + pub fn debug(&self) -> Result<(), ElemsPopError> { + println!("instructions:"); + for (line_no, instruction) in self.instrs.iter().enumerate() { + println!("#{:?}:", line_no); + match instruction { + Instr::Instr(instr) => { + println!("{:?}", instr); + println!("{}\n", instr.type_of()?); + }, + Instr::Restack(restack) => { + println!("{:?}", restack); + println!("{}\n", + restack + .type_of(From::from(line_no)) + .map_err(|e| ElemsPopError::RestackError(e))?); + }, + } + } + println!("--------------------------------------------------------------------------------"); + println!(""); + Ok(()) + } + + /// Assuming an input stack of [Json, Json, ..] (num_input_json count), + /// what's the monomorphic type of Self? + pub fn type_of_mono(&self, num_input_json: usize) -> Result { + let mut stack_type = (0..num_input_json).map(|_| ElemType::from_locations(EnumSet::only(ElemSymbol::Json), vec![])).collect(); + for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { + println!("------------------------------------------------------------------------------------------"); + println!("line_no: {}", line_no); + println!("{:?}\n", instr_or_restack); + match instr_or_restack { + Instr::Instr(instr) => { + let mut instr_type = instr.type_of() + .map_err(|e| StackInstructionError::ElemsPopError(e))?; + println!("instr: {}\n", instr_type); + stack_type = instr_type.specialize_to_input_stack(stack_type) + .map_err(|e| StackInstructionError::TypeError(e))?; + }, + Instr::Restack(restack) => { + restack.run(&mut stack_type.types) + .map_err(|e| StackInstructionError::RestackError(e))? + }, + } + } + println!("------------------------------------------------------------------------------------------"); + println!("Finished running successfully.\n"); + println!("Final stack:"); + Ok(stack_type) + } + + pub fn run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { + for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { + stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; + println!("------------------------------------------------------------------------------------------"); + println!("line_no: {}", line_no); + println!("{:?}\n", instr_or_restack); + match instr_or_restack { + Instr::Instr(instr) => { + println!(""); + stack.debug_type(); + match instr.type_of() { + Ok(instr_type) => { + println!("instr: {}\n", instr_type); + let mut mut_instr_type = instr_type.clone(); + match mut_instr_type + .specialize_to_input_stack(stack.type_of()) { + Ok(_) => println!("specialized: {}\n", mut_instr_type), + Err(e) => println!("specialization failed:\n{}\n", e), + } + }, + Err(e) => println!("instr type_of errror: {}\n", e), + } + println!(""); + instr.stack_run(stack)? + }, + Instr::Restack(restack) => { + restack.run(&mut stack.stack) + .map_err(|e| StackInstructionError::RestackError(e))? + }, + } + } + println!("------------------------------------------------------------------------------------------"); + println!("Finished running successfully.\n"); + println!("Final stack:"); + stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; + Ok(()) + } + + pub fn instr(&mut self, instr: impl IsStackInstruction + 'static) -> () { + self.instrs.push(Instr::Instr(Arc::new(instr))) + } + + pub fn restack(&mut self, restack: Restack) -> () { + self.instrs.push(Instr::Restack(restack)) + } +} + + diff --git a/src/typed_instruction.rs b/src/typed_instruction.rs new file mode 100644 index 0000000..0ba690a --- /dev/null +++ b/src/typed_instruction.rs @@ -0,0 +1,94 @@ +use crate::elem::{ElemSymbol}; +use crate::stack::{Stack}; +use crate::restack::{RestackError}; +use crate::types::{Type, TypeError, AnError}; +use crate::types_scratch::{ElemsPopError}; +use crate::types_scratch::{IOList, IsList}; +use crate::untyped_instruction::{Instruction}; + +use std::marker::PhantomData; +use std::fmt::Debug; +use std::sync::{Arc}; + +use thiserror::Error; + +pub trait IsInstructionT: Debug { + type IO: IOList; + type Error: AnError; + + fn to_instruction(&self) -> Result; + fn name(x: PhantomData) -> String; + fn run(&self, x: &Self::IO) -> Result<(), Self::Error>; +} + +#[derive(Clone, Debug, Error)] +pub enum StackInstructionError { + #[error("StackInstructionError::ElemsPopError:\n{0}")] + ElemsPopError(ElemsPopError), + + #[error("RawStackInstructionError:\n{0}")] + RawStackInstructionError(String), + + #[error("MissingOutput:\n{instruction}\n\n{stack_input}")] + // TODO: more granular error typing + MissingOutput { + instruction: String, + stack_input: String, + }, + + #[error("Instrs::type_of_mono type error:\n{0}")] + TypeError(TypeError), + + #[error("StackInstructionError::RestackError:\n{0}")] + RestackError(RestackError), + + #[error("StackInstructionError::DebugJsonError:\n{0}")] + DebugJsonError(Arc), + + #[error("UnpackJsonNotSingleton:\n{first_value:?}\n{second_value:?}")] + UnpackJsonNotSingleton { + first_value: Option, + second_value: Option, + }, + +} + +pub trait IsStackInstruction: Debug { + fn to_instruction(&self) -> Result; + fn name(&self) -> String; + fn type_of(&self) -> Result; + fn stack_run(&self, stack: &mut Stack) -> Result<(), StackInstructionError>; +} + +impl IsStackInstruction for T +where + T: IsInstructionT, +{ + fn to_instruction(&self) -> Result { + self.to_instruction() + } + + fn name(&self) -> String { + IsInstructionT::name(PhantomData::) + } + + fn type_of(&self) -> Result { + IOList::type_of(PhantomData::<::IO>) + } + + fn stack_run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { + let stack_input = &IsList::pop(PhantomData::<::IO>, stack) + .map_err(|e| StackInstructionError::ElemsPopError(e))?; + self.run(stack_input) + .map_err(|e| StackInstructionError::RawStackInstructionError(format!("{:?}", e)))?; + let output_value = stack_input + .returning() + .ok_or_else(|| StackInstructionError::MissingOutput { + instruction: format!("{:?}", self), + stack_input: format!("{:?}", stack_input), + })?; + stack.push(output_value); + Ok(()) + } +} + diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs new file mode 100644 index 0000000..7140ee5 --- /dev/null +++ b/src/typed_instructions.rs @@ -0,0 +1,755 @@ +use crate::elem::Elem; +use crate::an_elem::{AnElem}; +use crate::types::{Empty, AnError, Nil}; +use crate::types_scratch::{AllElems, all_elems_untyped, Singleton, Cons, ReturnSingleton, ConsOut, Or, ReturnOr, IsList}; +use crate::untyped_instruction::{Instruction}; +use crate::typed_instruction::{IsInstructionT, StackInstructionError}; + +use std::cmp; +use std::convert::TryFrom; +use std::marker::PhantomData; +use std::fmt::Debug; +use std::sync::Arc; +use std::string::FromUtf8Error; + +use generic_array::typenum::{U0, U1, U2}; +use serde_json::{Map, Number, Value}; +use thiserror::Error; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Concat {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ConcatError {} +impl AnError for ConcatError {} + +// TODO: add string! +// (Self::String(x), Self::String(y)) => { +// Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) +// .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) +// }, +// +// bytes, array, object +impl IsInstructionT for Concat { + type IO = ConsOut, U2, + ReturnOr, U2, + ReturnSingleton, U2>>>, Nil>; + type Error = ConcatError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::Concat) + } + + fn name(_x: PhantomData) -> String { + "concat".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let y = x.clone().hd(); + match y { + ReturnOr::Left { array, returning } => { + let lhs = &array[0]; + let rhs = &array[1]; + returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); + }, + ReturnOr::Right(ReturnOr::Left { array, returning }) => { + let lhs = &array[0]; + let rhs = &array[1]; + returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); + }, + ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton, returning })) => { + let lhs = &singleton.array[0]; + let rhs = &singleton.array[1]; + returning.returning(lhs.into_iter().chain(rhs.into_iter()).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); + }, + } + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct AssertTrue {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct AssertTrueError {} +impl AnError for AssertTrueError {} + +impl IsInstructionT for AssertTrue { + type IO = ConsOut, Nil>; + // TODO: replace w/ Empty + type Error = AssertTrueError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::AssertTrue) + } + + fn name(_x: PhantomData) -> String { + "assert_true".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let array = x.clone().hd().singleton.array; + let returning = x.clone().hd().returning; + if array[0] { + returning.returning(true); + Ok(()) + } else { + Err(AssertTrueError {}) + } + } +} + + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Push { + pub push: T, +} + +impl IsInstructionT for Push { + type IO = ConsOut, Nil>; + type Error = Empty; + + fn to_instruction(&self) -> Result { + Ok(Instruction::Push(self.push.clone().to_elem())) + } + + fn name(_x: PhantomData) -> String { + format!("push_{:?}", AnElem::elem_symbol(PhantomData::)) + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + x.clone().hd().returning.returning(self.push.clone()); + Ok(()) + } +} + + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct HashSha256 {} +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct HashSha256Error {} +impl AnError for HashSha256Error {} + +impl IsInstructionT for HashSha256 { + type IO = ConsOut, U1>, Nil>; + type Error = Empty; + + fn to_instruction(&self) -> Result { + Ok(Instruction::HashSha256) + } + + fn name(_x: PhantomData) -> String { + "sha256".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let array = x.clone().hd().singleton.array; + let returning = x.clone().hd().returning; + returning.returning(super::sha256(&array[0])); + Ok(()) + } +} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Slice {} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SliceError { + OffsetNotU64(Number), + + LengthNotU64(Number), + + Overflow { + offset: Number, + length: Number, + }, + + TooShort { + offset: usize, + length: usize, + iterable: String, + }, + + FromUtf8Error(FromUtf8Error), +} + +impl From for SliceError { + fn from(error: FromUtf8Error) -> Self { + Self::FromUtf8Error(error) + } +} + +impl AnError for SliceError {} + +// bytes, string, array, object +impl IsInstructionT for Slice { + type IO = ConsOut, U1, + ReturnOr, U1, + ReturnSingleton, U1>>>>, + Cons, Nil>>; + type Error = SliceError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::Slice) + } + + fn name(_x: PhantomData) -> String { + "slice".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let y = x.clone().hd(); + let offset_length = x.clone().tl().hd().array; + let offset = &offset_length[0]; + let length = &offset_length[1]; + let u_offset = offset.as_u64() + .ok_or_else(|| SliceError::OffsetNotU64(offset.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| SliceError::Overflow { offset: offset.clone(), length: length.clone() }))?; + let u_length = length.as_u64() + .ok_or_else(|| SliceError::LengthNotU64(length.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| SliceError::Overflow { offset: offset.clone(), length: length.clone() }))?; + let u_offset_plus_length = u_offset.checked_add(u_length) + .ok_or_else(|| SliceError::Overflow { offset: offset.clone(), length: length.clone() })?; + match y.clone() { + ReturnOr::Left { array, returning } => { + let iterable = &array[0]; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(()) + } else { + returning.returning(iterable.into_iter().skip(u_offset).take(u_length).copied().collect()); + Ok(()) + } + }, + ReturnOr::Right(ReturnOr::Left { array, returning }) => { + let iterable = &array[0]; + if iterable.len() < u_offset_plus_length { + Err(()) + } else { + returning.returning(String::from_utf8(Vec::from(iterable.clone()).into_iter().skip(u_offset).take(u_length).collect())?); + Ok(()) + } + }, + ReturnOr::Right(ReturnOr::Right(ReturnOr::Left { array, returning })) => { + let iterable = &array[0]; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(()) + } else { + returning.returning(iterable.into_iter().skip(u_offset).take(u_length).cloned().collect()); + Ok(()) + } + }, + ReturnOr::Right(ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton: Singleton { array }, returning }))) => { + let iterable = &array[0]; + if iterable.clone().into_iter().count() < u_offset_plus_length { + Err(()) + } else { + returning.returning(iterable.into_iter().skip(u_offset).take(u_length).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); + Ok(()) + } + }, + }.map_err(|_e| { + SliceError::TooShort { + offset: u_offset, + length: u_length, + // TODO: better error + iterable: format!("{:?}", y), + } + }) + } +} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Index {} +#[derive(Clone, Debug, PartialEq, Eq, Error)] +pub enum IndexError { + #[error("Index: index not valid u64: {0:?}")] + IndexNotU64(Number), + + #[error("Index: index not valid usize: {0:?}")] + Overflow(Number), + + #[error("Index: iterable: {iterable:?}\nis too short for index: {index:?}")] + TooShort { + index: usize, + iterable: String, + }, +} +impl AnError for IndexError {} + +// bytes, array, object +impl IsInstructionT for Index { + type IO = ConsOut, + Cons, + Cons, U1, + Singleton, U1>>, Nil>>>; + type Error = IndexError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::Index) + } + + fn name(_x: PhantomData) -> String { + "index".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let index = x.clone().tl().hd().array[0].clone(); + let y = &x.clone().tl().tl().hd(); + let u_index = index.as_u64() + .ok_or_else(|| IndexError::IndexNotU64(index.clone())) + .and_then(|x| usize::try_from(x).map_err(|_| IndexError::Overflow(index.clone())))?; + + let result = match y.clone() { + Or::Left(array) => { + array[0] + .clone() + .into_iter() + .skip(u_index) + .next() + }, + Or::Right(Singleton { array }) => { + array[0] + .clone() + .into_iter() + .skip(u_index) + .next() + .map(|(_x, y)| y) + }, + }.ok_or_else(|| { + IndexError::TooShort { + index: u_index, + // TODO: better error + iterable: format!("{:?}", y), + } + })?; + returning.returning(result); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ToJson {} +#[derive(Clone, Debug)] +pub struct ToJsonError { + input: Elem, + error: Arc, +} +impl AnError for ToJsonError {} + +impl IsInstructionT for ToJson { + type IO = ConsOut, Cons, Nil>>; + type Error = ToJsonError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::ToJson) + } + + fn name(_x: PhantomData) -> String { + "to_json".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let z = array[0].clone(); + returning.returning(serde_json::to_value(z.clone()) + .map_err(move |e| ToJsonError { + input: z, + error: Arc::new(e), + })?); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Lookup {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LookupError { + key: String, + map: Map, +} +impl AnError for LookupError {} + +impl IsInstructionT for Lookup { + type IO = ConsOut, + Cons, + Cons, U1>, Nil>>>; + type Error = LookupError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::Lookup) + } + + fn name(_x: PhantomData) -> String { + "lookup".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let key = &x.clone().tl().hd().array[0]; + let map = &x.clone().tl().tl().hd().array[0]; + returning.returning(map.get(key) + .ok_or_else(|| LookupError { + key: key.clone(), + map: map.clone(), + })?.clone()); + Ok(()) + } +} + + +#[derive(Debug)] +pub struct UnpackJson { + pub t: PhantomData, +} +#[derive(Debug)] +pub struct UnpackJsonError {} +impl AnError for UnpackJsonError {} + +pub trait AJsonElem: AnElem { + fn to_value(self) -> Value; + fn from_value(t: PhantomData, x: Value) -> Option where Self: Sized; +} + +impl AJsonElem for () { + fn to_value(self) -> Value { + Value::Null + } + + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { + match x { + Value::Null => Some(()), + _ => None, + } + } +} + +impl AJsonElem for bool { + fn to_value(self) -> Value { + Value::Bool(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { + match x { + Value::Bool(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for Number { + fn to_value(self) -> Value { + Value::Number(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { + match x { + Value::Number(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for String { + fn to_value(self) -> Value { + Value::String(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { + match x { + Value::String(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for Vec { + fn to_value(self) -> Value { + Value::Array(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { + match x { + Value::Array(y) => Some(y), + _ => None, + } + } +} + +impl AJsonElem for Map { + fn to_value(self) -> Value { + Value::Object(self) + } + + fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { + match x { + Value::Object(y) => Some(y), + _ => None, + } + } +} + +impl IsInstructionT for UnpackJson { + type IO = ConsOut, + Cons, Nil>>; + type Error = UnpackJsonError; + + fn to_instruction(&self) -> Result { + let mut symbol_set = ::elem_symbol(PhantomData).into_iter(); + match (symbol_set.next(), symbol_set.next()) { + (Some(elem_symbol), None) => Ok(Instruction::UnpackJson(elem_symbol)), + (x, y) => Err(StackInstructionError::UnpackJsonNotSingleton { + first_value: x, + second_value: y, + }), + } + } + + fn name(_x: PhantomData) -> String { + "unpack_json".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let json = &x.clone().tl().hd().array[0]; + let result = + AJsonElem::from_value(PhantomData::, json.clone()) + .ok_or_else(|| UnpackJsonError {})?; + returning.returning(result); + Ok(()) + } +} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct StringToBytes {} + +impl IsInstructionT for StringToBytes { + type IO = ConsOut, U0>, Cons, Nil>>; + type Error = Empty; + + fn to_instruction(&self) -> Result { + Ok(Instruction::StringToBytes) + } + + fn name(_x: PhantomData) -> String { + "string_to_bytes".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let in_str = &x.clone().tl().hd().array[0]; + returning.returning(in_str.clone().into_bytes()); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CheckLe {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CheckLeError { + lhs: Elem, + rhs: Elem, +} +impl AnError for CheckLeError {} + +impl IsInstructionT for CheckLe { + type IO = ConsOut, Cons, Nil>>; + type Error = CheckLeError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::CheckLe) + } + + fn name(_x: PhantomData) -> String { + "check_le".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| CheckLeError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Less => true, + cmp::Ordering::Equal => true, + cmp::Ordering::Greater => false, + }; + returning.returning(result); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CheckLt {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CheckLtError { + lhs: Elem, + rhs: Elem, +} +impl AnError for CheckLtError {} + +impl IsInstructionT for CheckLt { + type IO = ConsOut, Cons, Nil>>; + type Error = CheckLtError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::CheckLt) + } + + fn name(_x: PhantomData) -> String { + "check_lt".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| CheckLtError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Less => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} + + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CheckEq {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CheckEqError { + lhs: Elem, + rhs: Elem, +} +impl AnError for CheckEqError {} + +impl IsInstructionT for CheckEq { + type IO = ConsOut, Cons, Nil>>; + type Error = CheckEqError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::CheckEq) + } + + fn name(_x: PhantomData) -> String { + "check_eq".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let y = &x.clone().tl().hd(); + let array = all_elems_untyped(y); + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| CheckEqError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Equal => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct StringEq {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StringEqError { + lhs: String, + rhs: String, +} +impl AnError for StringEqError {} + +impl IsInstructionT for StringEq { + type IO = ConsOut, Cons, Nil>>; + type Error = StringEqError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::StringEq) + } + + fn name(_x: PhantomData) -> String { + "check_eq".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let array = &x.clone().tl().hd().array; + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| StringEqError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Equal => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct BytesEq {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BytesEqError { + lhs: Vec, + rhs: Vec, +} +impl AnError for BytesEqError {} + +impl IsInstructionT for BytesEq { + type IO = ConsOut, Cons, U2>, Nil>>; + type Error = BytesEqError; + + fn to_instruction(&self) -> Result { + Ok(Instruction::BytesEq) + } + + fn name(_x: PhantomData) -> String { + "check_eq".to_string() + } + + fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { + let returning = x.clone().hd().returning; + let array = &x.clone().tl().hd().array; + let lhs = array[0].clone(); + let rhs = array[1].clone(); + let cmp_result = lhs.partial_cmp(&rhs) + .ok_or_else(|| BytesEqError { + lhs: lhs, + rhs: rhs + })?; + let result = match cmp_result { + cmp::Ordering::Equal => true, + _ => false, + }; + returning.returning(result); + Ok(()) + } +} + diff --git a/src/types.rs b/src/types.rs index 9a40c33..1f11611 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,21 +1,15 @@ -// use crate::restack::{RestackError}; -use crate::elem::{Elem, ElemType, ElemTypeError, StackType}; +use crate::restack::{Restack, RestackError}; +use crate::location::{LineNo}; +use crate::elem::{Elem}; +use crate::elem_type::{ElemType, ElemTypeError, StackType}; -// Stack, StackError, LineNo, -// use crate::stack::{Location}; - -use std::collections::BTreeMap; use std::cmp; - -use std::fmt; +use std::collections::BTreeMap; use std::fmt::{Display, Formatter}; -// use std::alloc::string; +use std::fmt; use std::marker::PhantomData; use std::sync::Arc; -// use enumset::{EnumSet, enum_set}; -// use serde::{Deserialize, Serialize}; -// use serde_json::{Map, Number, Value}; use thiserror::Error; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -28,7 +22,7 @@ impl Empty { } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Nil { } +pub struct Nil {} impl Iterator for Nil { type Item = Elem; @@ -216,6 +210,7 @@ impl TypeId { } } +// TODO: relocate #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Context { context: BTreeMap, @@ -638,6 +633,7 @@ impl Type { // ``` impl Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + // TODO: fix normalize // let self_normalized = self.normalize().map_err(|_| fmt::Error)?; let self_normalized = self; write!(f, @@ -707,24 +703,6 @@ mod type_display_tests { } - -#[derive(Clone, Debug, PartialEq, Error)] -pub enum TypeIdMapError { - #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] - GetUnknownTypeId { - index: TypeId, - location: usize, - type_map: TypeIdMap, - }, - - #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] - PushExists { - from: TypeId, - to: TypeId, - map: TypeIdMap, - }, -} - #[derive(Clone, Debug, PartialEq, Error)] pub enum ContextError { #[error("Context::get applied to a TypeId: \n{index:?}\n, not in the Context: \n{context:?}\n, error: \n{error:?}\n")] @@ -879,3 +857,37 @@ impl TypeIdMap { } } +#[derive(Clone, Debug, PartialEq, Error)] +pub enum TypeIdMapError { + #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] + GetUnknownTypeId { + index: TypeId, + location: usize, + type_map: TypeIdMap, + }, + + #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] + PushExists { + from: TypeId, + to: TypeId, + map: TypeIdMap, + }, +} + +impl Restack { + // TODO: fix locations: out locations are mislabeled as in locations + pub fn type_of(&self, line_no: LineNo) -> Result { + let mut context = Context::new(); + let mut restack_type: Vec = (0..self.restack_depth) + .map(|x| context.push(ElemType::any(vec![line_no.in_at(x)]))) + .collect(); + let i_type = restack_type.clone(); + self.run(&mut restack_type)?; + Ok(Type { + context: context, + i_type: i_type, + o_type: restack_type, + }) + } +} + diff --git a/src/types_scratch.rs b/src/types_scratch.rs index c170cf4..ccc7829 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,48 +1,35 @@ -use crate::elem::{Elem, ElemType, ElemTypeError, ElemSymbol, StackType, AnElem}; +use crate::restack::{RestackError}; +use crate::elem::{Elem, ElemSymbol}; +use crate::elem_type::{ElemType, ElemTypeError, StackType}; +use crate::an_elem::{AnElem}; use crate::stack::{Stack, StackError}; -use crate::restack::{Restack, RestackError}; -use crate::types::{Context, ContextError, Type, TypeError, Empty, AnError, Nil}; +use crate::types::{Context, ContextError, Type, Nil}; -use std::cmp; -use std::convert::TryFrom; -// use std::iter::FromIterator; use std::marker::PhantomData; -// use std::fmt; use std::fmt::Debug; use std::sync::{Arc, Mutex}; -use std::string::FromUtf8Error; use enumset::EnumSet; use generic_array::functional::FunctionalSequence; use generic_array::sequence::GenericSequence; -use generic_array::typenum::{U0, U1, U2}; +use generic_array::typenum::{U0}; use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; -use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; use thiserror::Error; use typenum::marker_traits::Unsigned; -// use generic_array::typenum::{B1}; -// use typenum::marker_traits::Unsigned; -// use typenum::type_operators::IsLess; - -// NEXT: -// - delete old IsInstruction -// -// - add typing info as with pop-stack -// + get typing up to parity -// + add special-case unifier for restack + IsInstructionT for testing? -// +// TODO: // - random type -> ~random inhabitant of the type -// - random typed program!? +// - random typed program? +// TODO: rename #[derive(Clone, Debug, PartialEq, Eq)] pub struct Singleton where T: AnElem, N: ArrayLength + Debug, { - array: GenericArray, + pub array: GenericArray, } impl IntoIterator for Singleton @@ -95,6 +82,9 @@ pub enum ElemsPopError { error: Arc, }, + #[error("Instr::run: ElemTypeError:\n{0}")] + RestackError(RestackError), + #[error("Elems::elem_type (Or): Set includes repeated type:\n{0}")] ElemTypeError(ElemTypeError), @@ -114,9 +104,6 @@ pub trait Elems: Clone + Debug + IntoIterator { // fn right(s: PhantomData, x: Self::Tl) -> Self; fn or) -> T, G: Fn(&Self::Tl) -> T>(&self, f: F, g: G) -> T; - // fn to_elems(self) -> Elem; - // fn from_elems(t: PhantomData, x: &mut Stack) -> Result; - fn pop(_x: PhantomData, stack: &mut Stack) -> Result where Self: Sized; @@ -236,7 +223,6 @@ where Right(U), } -// #[derive(Clone, Debug, PartialEq, Eq)] pub enum IterOr where T: AnElem, @@ -342,7 +328,7 @@ where {} // TODO: AnElem: &self -> AllElems -type AllElems = +pub type AllElems = Or<(), N, Or = Or, N, Singleton>>>>>>>; -fn all_elems_untyped(x: &AllElems) -> GenericArray +pub fn all_elems_untyped(x: &AllElems) -> GenericArray where N: Debug + ArrayLength<()> + @@ -404,8 +390,8 @@ where T: AnElem, N: ArrayLength + Debug, { - singleton: Singleton, - returning: Return, + pub singleton: Singleton, + pub returning: Return, } impl IntoIterator for ReturnSingleton @@ -550,7 +536,6 @@ where Or::Right(y) => Self::Right(y), } }) - } fn elem_type(_t: PhantomData) -> Result { @@ -876,963 +861,8 @@ where // let num_elem_type_hd = <::N as Unsigned>::to_usize(); let mut type_hd = IOElems::type_of(PhantomData::)?; let elem_type_tl = IsList::stack_type(PhantomData::)?; - type_hd.append_inputs(elem_type_tl); Ok(type_hd) } } - - - - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] -pub enum Instruction { - Push(Elem), - Restack(Restack), - HashSha256, - CheckLe, - CheckLt, - CheckEq, - StringEq, - BytesEq, - Concat, - Slice, - Index, - Lookup, - AssertTrue, - ToJson, - UnpackJson(ElemSymbol), - StringToBytes, -} - -pub trait IsInstructionT: Debug { - type IO: IOList; - type Error: AnError; - - fn to_instruction(&self) -> Result; - fn name(x: PhantomData) -> String; - fn run(&self, x: &Self::IO) -> Result<(), Self::Error>; -} - -#[derive(Clone, Debug, Error)] -pub enum StackInstructionError { - #[error("StackInstructionError::ElemsPopError:\n{0}")] - ElemsPopError(ElemsPopError), - - #[error("RawStackInstructionError:\n{0}")] - RawStackInstructionError(String), - - #[error("MissingOutput:\n{instruction}\n\n{stack_input}")] - // TODO: more granular error typing - MissingOutput { - instruction: String, - stack_input: String, - }, - - #[error("Instrs::type_of_mono type error:\n{0}")] - TypeError(TypeError), - - #[error("StackInstructionError::RestackError:\n{0}")] - RestackError(RestackError), - - #[error("StackInstructionError::DebugJsonError:\n{0}")] - DebugJsonError(Arc), - - #[error("UnpackJsonNotSingleton:\n{first_value:?}\n{second_value:?}")] - UnpackJsonNotSingleton { - first_value: Option, - second_value: Option, - }, - -} - -pub trait IsStackInstruction: Debug { - fn to_instruction(&self) -> Result; - fn name(&self) -> String; - fn type_of(&self) -> Result; - fn stack_run(&self, stack: &mut Stack) -> Result<(), StackInstructionError>; -} - -impl IsStackInstruction for T -where - T: IsInstructionT, -{ - fn to_instruction(&self) -> Result { - self.to_instruction() - } - - fn name(&self) -> String { - IsInstructionT::name(PhantomData::) - } - - fn type_of(&self) -> Result { - IOList::type_of(PhantomData::<::IO>) - } - - fn stack_run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { - let stack_input = &IsList::pop(PhantomData::<::IO>, stack) - .map_err(|e| StackInstructionError::ElemsPopError(e))?; - self.run(stack_input) - .map_err(|e| StackInstructionError::RawStackInstructionError(format!("{:?}", e)))?; - let output_value = stack_input - .returning() - .ok_or_else(|| StackInstructionError::MissingOutput { - instruction: format!("{:?}", self), - stack_input: format!("{:?}", stack_input), - })?; - stack.push(output_value); - Ok(()) - } -} - - - -#[derive(Clone, Debug)] -pub enum Instr { - Instr(Arc), - Restack(Restack), -} - -impl Instr { - pub fn to_instruction(&self) -> Result { - match self { - Self::Instr(instr) => instr.to_instruction(), - Self::Restack(restack) => Ok(Instruction::Restack(restack.clone())), - } - } -} - - -#[derive(Clone, Debug)] -pub struct Instrs { - pub instrs: Vec, -} - -impl Instrs { - pub fn new() -> Self { - Instrs { - instrs: vec![], - } - } - - /// Assuming an input stack of [Json, Json, ..] (num_input_json count), - /// what's the monomorphic type of Self? - pub fn type_of_mono(&self, num_input_json: usize) -> Result { - let mut stack_type = (0..num_input_json).map(|_| ElemType::from_locations(EnumSet::only(ElemSymbol::Json), vec![])).collect(); - for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { - println!("------------------------------------------------------------------------------------------"); - println!("line_no: {}", line_no); - println!("{:?}\n", instr_or_restack); - match instr_or_restack { - Instr::Instr(instr) => { - let mut instr_type = instr.type_of() - .map_err(|e| StackInstructionError::ElemsPopError(e))?; - println!("instr: {}\n", instr_type); - stack_type = instr_type.specialize_to_input_stack(stack_type) - .map_err(|e| StackInstructionError::TypeError(e))?; - }, - Instr::Restack(restack) => { - restack.run(&mut stack_type.types) - .map_err(|e| StackInstructionError::RestackError(e))? - }, - } - } - println!("------------------------------------------------------------------------------------------"); - println!("Finished running successfully.\n"); - println!("Final stack:"); - Ok(stack_type) - } - - pub fn run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { - for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { - stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; - println!("------------------------------------------------------------------------------------------"); - println!("line_no: {}", line_no); - println!("{:?}\n", instr_or_restack); - match instr_or_restack { - Instr::Instr(instr) => { - println!(""); - stack.debug_type(); - match instr.type_of() { - Ok(instr_type) => { - println!("instr: {}\n", instr_type); - let mut mut_instr_type = instr_type.clone(); - match mut_instr_type - .specialize_to_input_stack(stack.type_of()) { - // .clone() - // .stack - // .into_iter() - // .map(|x| x.elem_type(vec![])) - // .collect()) { - Ok(_) => println!("specialized: {}\n", mut_instr_type), - Err(e) => println!("specialization failed:\n{}\n", e), - } - }, - Err(e) => println!("instr type_of errror: {}\n", e), - } - println!(""); - instr.stack_run(stack)? - }, - Instr::Restack(restack) => { - restack.run(&mut stack.stack) - .map_err(|e| StackInstructionError::RestackError(e))? - }, - } - } - println!("------------------------------------------------------------------------------------------"); - println!("Finished running successfully.\n"); - println!("Final stack:"); - stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; - Ok(()) - } - - pub fn instr(&mut self, instr: impl IsStackInstruction + 'static) -> () { - self.instrs.push(Instr::Instr(Arc::new(instr))) - } - - pub fn restack(&mut self, restack: Restack) -> () { - self.instrs.push(Instr::Restack(restack)) - } -} - - - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Concat {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ConcatError {} -impl AnError for ConcatError {} - -// TODO: add string! -// (Self::String(x), Self::String(y)) => { -// Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) -// .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) -// }, -// -// bytes, array, object -impl IsInstructionT for Concat { - type IO = ConsOut, U2, - ReturnOr, U2, - ReturnSingleton, U2>>>, Nil>; - type Error = ConcatError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::Concat) - } - - fn name(_x: PhantomData) -> String { - "concat".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let y = x.clone().hd(); - match y { - ReturnOr::Left { array, returning } => { - let lhs = &array[0]; - let rhs = &array[1]; - returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); - }, - ReturnOr::Right(ReturnOr::Left { array, returning }) => { - let lhs = &array[0]; - let rhs = &array[1]; - returning.returning(lhs.into_iter().chain(rhs.into_iter()).cloned().collect()); - }, - ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton, returning })) => { - let lhs = &singleton.array[0]; - let rhs = &singleton.array[1]; - returning.returning(lhs.into_iter().chain(rhs.into_iter()).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); - }, - } - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct AssertTrue {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct AssertTrueError {} -impl AnError for AssertTrueError {} - -impl IsInstructionT for AssertTrue { - type IO = ConsOut, Nil>; - // TODO: replace w/ Empty - type Error = AssertTrueError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::AssertTrue) - } - - fn name(_x: PhantomData) -> String { - "assert_true".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let array = x.clone().hd().singleton.array; - let returning = x.clone().hd().returning; - if array[0] { - returning.returning(true); - Ok(()) - } else { - Err(AssertTrueError {}) - } - } -} - - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Push { - pub push: T, -} - -impl IsInstructionT for Push { - type IO = ConsOut, Nil>; - type Error = Empty; - - fn to_instruction(&self) -> Result { - Ok(Instruction::Push(self.push.clone().to_elem())) - } - - fn name(_x: PhantomData) -> String { - format!("push_{:?}", AnElem::elem_symbol(PhantomData::)) - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - x.clone().hd().returning.returning(self.push.clone()); - Ok(()) - } -} - - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct HashSha256 {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct HashSha256Error {} -impl AnError for HashSha256Error {} - -impl IsInstructionT for HashSha256 { - type IO = ConsOut, U1>, Nil>; - type Error = Empty; - - fn to_instruction(&self) -> Result { - Ok(Instruction::HashSha256) - } - - fn name(_x: PhantomData) -> String { - "sha256".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let array = x.clone().hd().singleton.array; - let returning = x.clone().hd().returning; - returning.returning(super::sha256(&array[0])); - Ok(()) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Slice {} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SliceError { - OffsetNotU64(Number), - - LengthNotU64(Number), - - Overflow { - offset: Number, - length: Number, - }, - - TooShort { - offset: usize, - length: usize, - iterable: String, - }, - - FromUtf8Error(FromUtf8Error), -} - -impl From for SliceError { - fn from(error: FromUtf8Error) -> Self { - Self::FromUtf8Error(error) - } -} - -impl AnError for SliceError {} - -// bytes, string, array, object -impl IsInstructionT for Slice { - type IO = ConsOut, U1, - ReturnOr, U1, - ReturnSingleton, U1>>>>, - Cons, Nil>>; - type Error = SliceError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::Slice) - } - - fn name(_x: PhantomData) -> String { - "slice".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let y = x.clone().hd(); - let offset_length = x.clone().tl().hd().array; - let offset = &offset_length[0]; - let length = &offset_length[1]; - let u_offset = offset.as_u64() - .ok_or_else(|| SliceError::OffsetNotU64(offset.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| SliceError::Overflow { offset: offset.clone(), length: length.clone() }))?; - let u_length = length.as_u64() - .ok_or_else(|| SliceError::LengthNotU64(length.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| SliceError::Overflow { offset: offset.clone(), length: length.clone() }))?; - let u_offset_plus_length = u_offset.checked_add(u_length) - .ok_or_else(|| SliceError::Overflow { offset: offset.clone(), length: length.clone() })?; - match y.clone() { - ReturnOr::Left { array, returning } => { - let iterable = &array[0]; - if iterable.clone().into_iter().count() < u_offset_plus_length { - Err(()) - } else { - returning.returning(iterable.into_iter().skip(u_offset).take(u_length).copied().collect()); - Ok(()) - } - }, - ReturnOr::Right(ReturnOr::Left { array, returning }) => { - let iterable = &array[0]; - if iterable.len() < u_offset_plus_length { - Err(()) - } else { - returning.returning(String::from_utf8(Vec::from(iterable.clone()).into_iter().skip(u_offset).take(u_length).collect())?); - Ok(()) - } - }, - ReturnOr::Right(ReturnOr::Right(ReturnOr::Left { array, returning })) => { - let iterable = &array[0]; - if iterable.clone().into_iter().count() < u_offset_plus_length { - Err(()) - } else { - returning.returning(iterable.into_iter().skip(u_offset).take(u_length).cloned().collect()); - Ok(()) - } - }, - ReturnOr::Right(ReturnOr::Right(ReturnOr::Right(ReturnSingleton { singleton: Singleton { array }, returning }))) => { - let iterable = &array[0]; - if iterable.clone().into_iter().count() < u_offset_plus_length { - Err(()) - } else { - returning.returning(iterable.into_iter().skip(u_offset).take(u_length).map(|xy| (xy.0.clone(), xy.1.clone())).collect()); - Ok(()) - } - }, - }.map_err(|_e| { - SliceError::TooShort { - offset: u_offset, - length: u_length, - // TODO: better error - iterable: format!("{:?}", y), - } - }) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Index {} -#[derive(Clone, Debug, PartialEq, Eq, Error)] -pub enum IndexError { - #[error("Index: index not valid u64: {0:?}")] - IndexNotU64(Number), - - #[error("Index: index not valid usize: {0:?}")] - Overflow(Number), - - #[error("Index: iterable: {iterable:?}\nis too short for index: {index:?}")] - TooShort { - index: usize, - iterable: String, - }, -} -impl AnError for IndexError {} - -// bytes, array, object -impl IsInstructionT for Index { - type IO = ConsOut, - Cons, - Cons, U1, - Singleton, U1>>, Nil>>>; - type Error = IndexError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::Index) - } - - fn name(_x: PhantomData) -> String { - "index".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let index = x.clone().tl().hd().array[0].clone(); - let y = &x.clone().tl().tl().hd(); - let u_index = index.as_u64() - .ok_or_else(|| IndexError::IndexNotU64(index.clone())) - .and_then(|x| usize::try_from(x).map_err(|_| IndexError::Overflow(index.clone())))?; - - let result = match y.clone() { - Or::Left(array) => { - array[0] - .clone() - .into_iter() - .skip(u_index) - .next() - }, - Or::Right(Singleton { array }) => { - array[0] - .clone() - .into_iter() - .skip(u_index) - .next() - .map(|(_x, y)| y) - }, - }.ok_or_else(|| { - IndexError::TooShort { - index: u_index, - // TODO: better error - iterable: format!("{:?}", y), - } - })?; - returning.returning(result); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ToJson {} -#[derive(Clone, Debug)] -pub struct ToJsonError { - input: Elem, - error: Arc, -} -impl AnError for ToJsonError {} - -impl IsInstructionT for ToJson { - type IO = ConsOut, Cons, Nil>>; - type Error = ToJsonError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::ToJson) - } - - fn name(_x: PhantomData) -> String { - "to_json".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); - let z = array[0].clone(); - returning.returning(serde_json::to_value(z.clone()) - .map_err(move |e| ToJsonError { - input: z, - error: Arc::new(e), - })?); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Lookup {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LookupError { - key: String, - map: Map, -} -impl AnError for LookupError {} - -impl IsInstructionT for Lookup { - type IO = ConsOut, - Cons, - Cons, U1>, Nil>>>; - type Error = LookupError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::Lookup) - } - - fn name(_x: PhantomData) -> String { - "lookup".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let key = &x.clone().tl().hd().array[0]; - let map = &x.clone().tl().tl().hd().array[0]; - returning.returning(map.get(key) - .ok_or_else(|| LookupError { - key: key.clone(), - map: map.clone(), - })?.clone()); - Ok(()) - } -} - - -#[derive(Debug)] -pub struct UnpackJson { - pub t: PhantomData, -} -#[derive(Debug)] -pub struct UnpackJsonError {} -impl AnError for UnpackJsonError {} - -pub trait AJsonElem: AnElem { - fn to_value(self) -> Value; - fn from_value(t: PhantomData, x: Value) -> Option where Self: Sized; -} - -impl AJsonElem for () { - fn to_value(self) -> Value { - Value::Null - } - - fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { - match x { - Value::Null => Some(()), - _ => None, - } - } -} - -impl AJsonElem for bool { - fn to_value(self) -> Value { - Value::Bool(self) - } - - fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { - match x { - Value::Bool(y) => Some(y), - _ => None, - } - } -} - -impl AJsonElem for Number { - fn to_value(self) -> Value { - Value::Number(self) - } - - fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { - match x { - Value::Number(y) => Some(y), - _ => None, - } - } -} - -impl AJsonElem for String { - fn to_value(self) -> Value { - Value::String(self) - } - - fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { - match x { - Value::String(y) => Some(y), - _ => None, - } - } -} - -impl AJsonElem for Vec { - fn to_value(self) -> Value { - Value::Array(self) - } - - fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { - match x { - Value::Array(y) => Some(y), - _ => None, - } - } -} - -impl AJsonElem for Map { - fn to_value(self) -> Value { - Value::Object(self) - } - - fn from_value(_t: PhantomData, x: Value) -> Option where Self: Sized { - match x { - Value::Object(y) => Some(y), - _ => None, - } - } -} - -impl IsInstructionT for UnpackJson { - type IO = ConsOut, - Cons, Nil>>; - type Error = UnpackJsonError; - - fn to_instruction(&self) -> Result { - let mut symbol_set = ::elem_symbol(PhantomData).into_iter(); - match (symbol_set.next(), symbol_set.next()) { - (Some(elem_symbol), None) => Ok(Instruction::UnpackJson(elem_symbol)), - (x, y) => Err(StackInstructionError::UnpackJsonNotSingleton { - first_value: x, - second_value: y, - }), - } - } - - fn name(_x: PhantomData) -> String { - "unpack_json".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let json = &x.clone().tl().hd().array[0]; - let result = - AJsonElem::from_value(PhantomData::, json.clone()) - .ok_or_else(|| UnpackJsonError {})?; - returning.returning(result); - Ok(()) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct StringToBytes {} - -impl IsInstructionT for StringToBytes { - type IO = ConsOut, U0>, Cons, Nil>>; - type Error = Empty; - - fn to_instruction(&self) -> Result { - Ok(Instruction::StringToBytes) - } - - fn name(_x: PhantomData) -> String { - "string_to_bytes".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let in_str = &x.clone().tl().hd().array[0]; - returning.returning(in_str.clone().into_bytes()); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct CheckLe {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CheckLeError { - lhs: Elem, - rhs: Elem, -} -impl AnError for CheckLeError {} - -impl IsInstructionT for CheckLe { - type IO = ConsOut, Cons, Nil>>; - type Error = CheckLeError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::CheckLe) - } - - fn name(_x: PhantomData) -> String { - "check_le".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); - let lhs = array[0].clone(); - let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| CheckLeError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Less => true, - cmp::Ordering::Equal => true, - cmp::Ordering::Greater => false, - }; - returning.returning(result); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct CheckLt {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CheckLtError { - lhs: Elem, - rhs: Elem, -} -impl AnError for CheckLtError {} - -impl IsInstructionT for CheckLt { - type IO = ConsOut, Cons, Nil>>; - type Error = CheckLtError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::CheckLt) - } - - fn name(_x: PhantomData) -> String { - "check_lt".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); - let lhs = array[0].clone(); - let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| CheckLtError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Less => true, - _ => false, - }; - returning.returning(result); - Ok(()) - } -} - - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct CheckEq {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CheckEqError { - lhs: Elem, - rhs: Elem, -} -impl AnError for CheckEqError {} - -impl IsInstructionT for CheckEq { - type IO = ConsOut, Cons, Nil>>; - type Error = CheckEqError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::CheckEq) - } - - fn name(_x: PhantomData) -> String { - "check_eq".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); - let lhs = array[0].clone(); - let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| CheckEqError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Equal => true, - _ => false, - }; - returning.returning(result); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct StringEq {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StringEqError { - lhs: String, - rhs: String, -} -impl AnError for StringEqError {} - -impl IsInstructionT for StringEq { - type IO = ConsOut, Cons, Nil>>; - type Error = StringEqError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::StringEq) - } - - fn name(_x: PhantomData) -> String { - "check_eq".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let array = &x.clone().tl().hd().array; - let lhs = array[0].clone(); - let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| StringEqError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Equal => true, - _ => false, - }; - returning.returning(result); - Ok(()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct BytesEq {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BytesEqError { - lhs: Vec, - rhs: Vec, -} -impl AnError for BytesEqError {} - -impl IsInstructionT for BytesEq { - type IO = ConsOut, Cons, U2>, Nil>>; - type Error = BytesEqError; - - fn to_instruction(&self) -> Result { - Ok(Instruction::BytesEq) - } - - fn name(_x: PhantomData) -> String { - "check_eq".to_string() - } - - fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { - let returning = x.clone().hd().returning; - let array = &x.clone().tl().hd().array; - let lhs = array[0].clone(); - let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| BytesEqError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Equal => true, - _ => false, - }; - returning.returning(result); - Ok(()) - } -} - diff --git a/src/untyped_instruction.rs b/src/untyped_instruction.rs new file mode 100644 index 0000000..bb7bae6 --- /dev/null +++ b/src/untyped_instruction.rs @@ -0,0 +1,36 @@ +use crate::elem::{Elem, ElemSymbol}; +use crate::restack::{Restack}; + +use std::fmt::Debug; + +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub enum Instruction { + Push(Elem), + Restack(Restack), + HashSha256, + CheckLe, + CheckLt, + CheckEq, + StringEq, + BytesEq, + Concat, + Slice, + Index, + Lookup, + AssertTrue, + ToJson, + UnpackJson(ElemSymbol), + StringToBytes, +} + +#[derive(Clone, Debug, Error)] +pub enum InstructionError { + #[error("Instruction::to_instr UnpackJson does not support: {elem_symbol:?}")] + UnpackJson { + elem_symbol: ElemSymbol, + } +} + diff --git a/src/untyped_instructions.rs b/src/untyped_instructions.rs new file mode 100644 index 0000000..f230efd --- /dev/null +++ b/src/untyped_instructions.rs @@ -0,0 +1,54 @@ +use crate::untyped_instruction::{Instruction, InstructionError}; +use crate::typed_instruction::StackInstructionError; +use crate::typed_instr::Instr; +use crate::typed_instrs::Instrs; + +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] +pub struct Instructions { + pub instructions: Vec, +} + +impl IntoIterator for Instructions { + type Item = Instruction; + type IntoIter = as std::iter::IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.instructions.into_iter() + } +} + +impl Instructions { + pub fn to_instrs(self) -> Result { + Ok(Instrs { + instrs: self.into_iter().map(|x| x.to_instr()).collect::, InstructionError>>()?, + }) + } +} + +impl Instrs { + pub fn to_instructions(self) -> Result { + Ok(Instructions { + instructions: self.instrs.into_iter().map(|x| x.to_instruction()).collect::, StackInstructionError>>()?, + }) + } +} + +// Test program #1: [] -> [] +// +// Instruction::Push(Elem::Bool(true)), +// Instruction::Restack(Restack::id()), +// Instruction::AssertTrue, + +// Test program #2 +// +// ∀ (t0 ∊ {JSON}), +// ∀ (t1 ∊ {JSON}), +// ∀ (t2 ∊ {Object}), +// [t1] -> +// [t0, t2, t1] +// +// Instruction::Push(Elem::Json(Default::default())), +// Instruction::UnpackJson(ElemSymbol::Object), +// Instruction::Restack(Restack::dup()), From 3fa18d4eeaaedf0bfd3eb62db8e1c59fec79eaac Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 13 Apr 2022 12:32:24 -0400 Subject: [PATCH 69/77] adding missing docs --- src/cli.rs | 14 ++++++++++++++ src/lib.rs | 2 ++ src/parse.rs | 4 ++++ src/rest_api.rs | 1 + src/typed_instr.rs | 9 ++++++++- src/typed_instrs.rs | 9 +++++++++ src/typed_instructions.rs | 3 +++ src/types.rs | 2 +- 8 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index f9af7cc..b3d27ec 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -16,6 +16,10 @@ use clap::{Parser, Subcommand}; use serde_json::Value; use thiserror::Error; +/// Command line interface arguments +/// +/// Runs the given code (parsed as JSON) on the given input (parsed as JSON) +/// and the queries (parsed as JSON into a template #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { @@ -39,10 +43,12 @@ pub struct Cli { #[clap(short, long, parse(from_os_str), value_name = "FILE")] input: Option, + /// Subcommand #[clap(subcommand)] command: Option, } +/// Command line interface subcommands (optional) #[derive(Subcommand)] enum Commands { /// Parse only @@ -126,17 +132,20 @@ impl From for CliError { } impl Cli { + /// Get queries from self.queries PathBuf and parse JSON pub fn parse_queries(&self) -> Result { let queries_str = fs::read_to_string(self.queries.clone())?; let queries: Queries = serde_json::from_str(&queries_str)?; Ok(queries) } + /// Get code from self.code PathBuf and parse JSON pub fn parse_code(&self) -> Result { let instructions_str = fs::read_to_string(self.code.clone())?; Ok(parse_json(&instructions_str)?.to_instrs()?) } + /// Get input from self.input PathBuf and parse JSON pub fn get_input(&self) -> Result { if let Some(input_path) = self.input.as_deref() { let input_str = fs::read_to_string(input_path)?; @@ -148,6 +157,7 @@ impl Cli { } } + /// Monomorphic type of input instructions pub fn type_of_mono(&self) -> Result { let instructions = self.parse_code()?; let num_queries = self.parse_queries()?.len(); @@ -155,6 +165,8 @@ impl Cli { Ok(instructions.type_of_mono(num_queries)?) } + /// Run Cli::parse_code, get input and queries, run queries, and run the + /// code on the resulting queries pub async fn parse_and_run_result(&self) -> Result<(), CliError> { let instructions = self.parse_code()?; let mut stack = Stack::new(); @@ -171,6 +183,7 @@ impl Cli { Ok(instructions.run(&mut stack)?) } + /// Run Cli::parse_and_run_result and print its result pub async fn parse_and_run(&self) -> () { match self.parse_and_run_result().await { Ok(()) => println!("successful!"), @@ -178,6 +191,7 @@ impl Cli { } } + /// Run a set of Cli arguments pub async fn run(&self) -> () { match self.command { None => self.parse_and_run().await, diff --git a/src/lib.rs b/src/lib.rs index 70a5cfb..2cd5636 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(missing_docs)] + mod restack; pub use restack::Restack; mod arbitrary; diff --git a/src/parse.rs b/src/parse.rs index 509bf07..c95ed45 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -17,6 +17,7 @@ use std::str::FromStr; use thiserror::Error; +/// Parse a list of Instruction's using serde_json::from_str pub fn parse_json(input: &str) -> Result { match serde_json::from_str(&input) { Err(serde_error) => Err(ParseError::SerdeJsonError(serde_error)), @@ -24,6 +25,8 @@ pub fn parse_json(input: &str) -> Result { } } +/// Parse a ";"-separated list of instructions, where individual Instruction's +/// are parsed with parse_instruction pub fn parse(input: &str) -> Result { Ok(Instructions { instructions: @@ -36,6 +39,7 @@ pub fn parse(input: &str) -> Result { }) } +/// Parse an individual Instruction fn parse_instruction(term: &str) -> Result { if let Some(rest) = term.strip_prefix("push") { return Ok(Instruction::Push(rest.trim().parse()?)); diff --git a/src/rest_api.rs b/src/rest_api.rs index 381d47d..9ba9816 100644 --- a/src/rest_api.rs +++ b/src/rest_api.rs @@ -20,6 +20,7 @@ pub struct Api { } impl Api { + /// Create a new Api that's never been called pub fn new(request: Value, response: Value, rate_limit_seconds: u64) -> Self { Api { request: request, diff --git a/src/typed_instr.rs b/src/typed_instr.rs index d7cc23e..182adcd 100644 --- a/src/typed_instr.rs +++ b/src/typed_instr.rs @@ -8,17 +8,22 @@ use crate::typed_instructions::{AssertTrue, Lookup, Concat, Slice, Push, use std::marker::PhantomData; use std::fmt::Debug; -use std::sync::{Arc}; +use std::sync::Arc; use serde_json::{Map, Number, Value}; +/// A dynamically-resolved IsStackInstruction or Restack #[derive(Clone, Debug)] pub enum Instr { + /// Dynamically-resolved IsStackInstruction Instr(Arc), + + /// Restack Restack(Restack), } impl Instr { + /// Convert an Instr (typed) to an Instruction (untyped) pub fn to_instruction(&self) -> Result { match self { Self::Instr(instr) => instr.to_instruction(), @@ -28,6 +33,8 @@ impl Instr { } impl Instruction { + /// Convert an Instruction to an Instr, only failing when UnpackJson is + /// applied to an ElemSymbol that doesn't represent valid JSON pub fn to_instr(self) -> Result { match self { Self::Push(elem) => Ok(Instr::Instr(Arc::new(Push { push: elem }))), diff --git a/src/typed_instrs.rs b/src/typed_instrs.rs index 65bf17f..bc55406 100644 --- a/src/typed_instrs.rs +++ b/src/typed_instrs.rs @@ -11,18 +11,22 @@ use std::sync::{Arc}; use enumset::EnumSet; +/// A list of Instr's. See Instr for more info #[derive(Clone, Debug)] pub struct Instrs { + /// A list of Instr's pub instrs: Vec, } impl Instrs { + /// A new empty list of Instr's pub fn new() -> Self { Instrs { instrs: vec![], } } + /// Print the list of Instr's for debugging pub fn debug(&self) -> Result<(), ElemsPopError> { println!("instructions:"); for (line_no, instruction) in self.instrs.iter().enumerate() { @@ -74,6 +78,9 @@ impl Instrs { Ok(stack_type) } + /// Run the list of individually-typed instructions. It can fail if adjacent + /// instructions have non-matching types, e.g. if "Push(true)" is + /// immediately followed by "UnpackJson". pub fn run(&self, stack: &mut Stack) -> Result<(), StackInstructionError> { for (line_no, instr_or_restack) in (&self.instrs).into_iter().enumerate() { stack.debug().map_err(|e| StackInstructionError::DebugJsonError(Arc::new(e)))?; @@ -112,10 +119,12 @@ impl Instrs { Ok(()) } + /// Push an instruction that IsStackInstruction onto the list of instructions pub fn instr(&mut self, instr: impl IsStackInstruction + 'static) -> () { self.instrs.push(Instr::Instr(Arc::new(instr))) } + /// Push a Restack onto the list of instructions pub fn restack(&mut self, restack: Restack) -> () { self.instrs.push(Instr::Restack(restack)) } diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs index 7140ee5..1255d0b 100644 --- a/src/typed_instructions.rs +++ b/src/typed_instructions.rs @@ -673,8 +673,11 @@ impl IsInstructionT for CheckEq { } } +/// input: [x: String, y: String] +/// output: [x == y: bool] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct StringEq {} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct StringEqError { lhs: String, diff --git a/src/types.rs b/src/types.rs index 1f11611..84113b7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -12,7 +12,7 @@ use std::sync::Arc; use thiserror::Error; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] pub enum Empty {} impl Empty { From e5b67219bfd8aa16c8119a556420309b489f6184 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 13 Apr 2022 13:51:48 -0400 Subject: [PATCH 70/77] implement Error for IsInstruction errors --- src/typed_instructions.rs | 86 ++++++++++++++++++--------------------- src/types.rs | 2 +- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs index 1255d0b..79e5014 100644 --- a/src/typed_instructions.rs +++ b/src/typed_instructions.rs @@ -1,4 +1,4 @@ -use crate::elem::Elem; +use crate::elem::{Elem, ElemSymbol}; use crate::an_elem::{AnElem}; use crate::types::{Empty, AnError, Nil}; use crate::types_scratch::{AllElems, all_elems_untyped, Singleton, Cons, ReturnSingleton, ConsOut, Or, ReturnOr, IsList}; @@ -12,14 +12,15 @@ use std::fmt::Debug; use std::sync::Arc; use std::string::FromUtf8Error; +use enumset::EnumSet; use generic_array::typenum::{U0, U1, U2}; use serde_json::{Map, Number, Value}; use thiserror::Error; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Concat {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ConcatError {} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] +pub enum ConcatError {} impl AnError for ConcatError {} // TODO: add string! @@ -68,13 +69,13 @@ impl IsInstructionT for Concat { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct AssertTrue {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] +#[error("AssertTrue: found false")] pub struct AssertTrueError {} impl AnError for AssertTrueError {} impl IsInstructionT for AssertTrue { type IO = ConsOut, Nil>; - // TODO: replace w/ Empty type Error = AssertTrueError; fn to_instruction(&self) -> Result { @@ -125,8 +126,8 @@ impl IsInstructionT for Push { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct HashSha256 {} -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct HashSha256Error {} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] +pub enum HashSha256Error {} impl AnError for HashSha256Error {} impl IsInstructionT for HashSha256 { @@ -153,23 +154,28 @@ impl IsInstructionT for HashSha256 { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Slice {} -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Error)] pub enum SliceError { + #[error("SliceError::OffsetNotU64: \n{0}")] OffsetNotU64(Number), + #[error("SliceError::LengthNotU64: \n{0}")] LengthNotU64(Number), + #[error("SliceError::Overflow: \noffset: {offset} \nlength: {length}")] Overflow { offset: Number, length: Number, }, + #[error("SliceError::TooShort: \noffset: {offset} \nlength: {length} \n{iterable}")] TooShort { offset: usize, length: usize, iterable: String, }, + #[error("SliceError::FromUtf8Error: \n{0}")] FromUtf8Error(FromUtf8Error), } @@ -332,7 +338,8 @@ impl IsInstructionT for Index { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ToJson {} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Error)] +#[error("ToJson failed with a serde_json error: \n{input} \n{error}")] pub struct ToJsonError { input: Elem, error: Arc, @@ -367,7 +374,8 @@ impl IsInstructionT for ToJson { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Lookup {} -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[error("Lookup failed, key not in map: \n{key:?} \n{map:?}")] pub struct LookupError { key: String, map: Map, @@ -406,8 +414,12 @@ impl IsInstructionT for Lookup { pub struct UnpackJson { pub t: PhantomData, } -#[derive(Debug)] -pub struct UnpackJsonError {} +#[derive(Debug, Error)] +#[error("UnpackJson failed to unpack JSON: \n{elem_symbol:?} \n{input}")] +pub struct UnpackJsonError { + elem_symbol: EnumSet, + input: Value, +} impl AnError for UnpackJsonError {} pub trait AJsonElem: AnElem { @@ -518,7 +530,10 @@ impl IsInstructionT for UnpackJson { let json = &x.clone().tl().hd().array[0]; let result = AJsonElem::from_value(PhantomData::, json.clone()) - .ok_or_else(|| UnpackJsonError {})?; + .ok_or_else(|| UnpackJsonError { + elem_symbol: AnElem::elem_symbol(PhantomData::), + input: json.clone(), + })?; returning.returning(result); Ok(()) } @@ -550,7 +565,8 @@ impl IsInstructionT for StringToBytes { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CheckLe {} -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[error("CheckLe applied to incomparable elements: \n{lhs:?}\n {rhs:?}\n")] pub struct CheckLeError { lhs: Elem, rhs: Elem, @@ -592,7 +608,8 @@ impl IsInstructionT for CheckLe { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CheckLt {} -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[error("CheckLt applied to incomparable elements: \n{lhs:?}\n {rhs:?}\n")] pub struct CheckLtError { lhs: Elem, rhs: Elem, @@ -634,7 +651,8 @@ impl IsInstructionT for CheckLt { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CheckEq {} -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Error)] +#[error("CheckEq applied to incomparable elements: \n{lhs:?}\n {rhs:?}\n")] pub struct CheckEqError { lhs: Elem, rhs: Elem, @@ -678,11 +696,8 @@ impl IsInstructionT for CheckEq { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct StringEq {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StringEqError { - lhs: String, - rhs: String, -} +#[derive(Clone, Debug, PartialEq, Eq, Error)] +pub enum StringEqError {} impl AnError for StringEqError {} impl IsInstructionT for StringEq { @@ -702,27 +717,15 @@ impl IsInstructionT for StringEq { let array = &x.clone().tl().hd().array; let lhs = array[0].clone(); let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| StringEqError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Equal => true, - _ => false, - }; - returning.returning(result); + returning.returning(lhs == rhs); Ok(()) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BytesEq {} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BytesEqError { - lhs: Vec, - rhs: Vec, -} +#[derive(Clone, Debug, PartialEq, Eq, Error)] +pub enum BytesEqError {} impl AnError for BytesEqError {} impl IsInstructionT for BytesEq { @@ -742,16 +745,7 @@ impl IsInstructionT for BytesEq { let array = &x.clone().tl().hd().array; let lhs = array[0].clone(); let rhs = array[1].clone(); - let cmp_result = lhs.partial_cmp(&rhs) - .ok_or_else(|| BytesEqError { - lhs: lhs, - rhs: rhs - })?; - let result = match cmp_result { - cmp::Ordering::Equal => true, - _ => false, - }; - returning.returning(result); + returning.returning(lhs == rhs); Ok(()) } } diff --git a/src/types.rs b/src/types.rs index 84113b7..dd8989e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -33,7 +33,7 @@ impl Iterator for Nil { } // TODO: add necessary traits, methods and rename to IsInstructionError or IsInstrError -pub trait AnError: std::fmt::Debug { +pub trait AnError: std::error::Error { // fn to_stack_error(&self, line_no: LineNo) -> StackError; } From fbfab007329593d5f2503dfcc1789041d559c03d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 13 Apr 2022 14:17:35 -0400 Subject: [PATCH 71/77] continuing to add docs, replace empty enums with Empty --- src/typed_instruction.rs | 14 +++++- src/typed_instructions.rs | 92 +++++++++++++++++++++++++------------ src/types.rs | 20 -------- src/untyped_instruction.rs | 2 + src/untyped_instructions.rs | 4 ++ 5 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/typed_instruction.rs b/src/typed_instruction.rs index 0ba690a..9db0276 100644 --- a/src/typed_instruction.rs +++ b/src/typed_instruction.rs @@ -1,7 +1,7 @@ use crate::elem::{ElemSymbol}; use crate::stack::{Stack}; use crate::restack::{RestackError}; -use crate::types::{Type, TypeError, AnError}; +use crate::types::{Type, TypeError}; use crate::types_scratch::{ElemsPopError}; use crate::types_scratch::{IOList, IsList}; use crate::untyped_instruction::{Instruction}; @@ -12,12 +12,22 @@ use std::sync::{Arc}; use thiserror::Error; +/// A typed instruction with explicit input, output, and error types pub trait IsInstructionT: Debug { + /// The input/output type of the instruction type IO: IOList; - type Error: AnError; + /// All possible errors that can result from running this instruction. + /// Empty can be used for none. + type Error: std::error::Error; + + /// Convert to an untyped instruction fn to_instruction(&self) -> Result; + + /// The String name of the Instruction fn name(x: PhantomData) -> String; + + /// Run the instruction, returning all results using the IOList interface fn run(&self, x: &Self::IO) -> Result<(), Self::Error>; } diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs index 79e5014..5ad9a70 100644 --- a/src/typed_instructions.rs +++ b/src/typed_instructions.rs @@ -1,6 +1,6 @@ use crate::elem::{Elem, ElemSymbol}; use crate::an_elem::{AnElem}; -use crate::types::{Empty, AnError, Nil}; +use crate::types::{Empty, Nil}; use crate::types_scratch::{AllElems, all_elems_untyped, Singleton, Cons, ReturnSingleton, ConsOut, Or, ReturnOr, IsList}; use crate::untyped_instruction::{Instruction}; use crate::typed_instruction::{IsInstructionT, StackInstructionError}; @@ -17,13 +17,14 @@ use generic_array::typenum::{U0, U1, U2}; use serde_json::{Map, Number, Value}; use thiserror::Error; + +/// forall T <- {Vec, Vec, Map} +/// input: [x: T, y: T] +/// output: [x.into_iter().chain(y.into_iter()).collect(): T] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Concat {} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] -pub enum ConcatError {} -impl AnError for ConcatError {} -// TODO: add string! +// TODO: add String! // (Self::String(x), Self::String(y)) => { // Ok(Self::String(String::from_utf8(Self::concat_generic(Vec::from(x.clone()), Vec::from(y.clone()))) // .map_err(|_| ElemError::ConcatInvalidUTF8 { lhs: x, rhs: y })?)) @@ -34,7 +35,7 @@ impl IsInstructionT for Concat { type IO = ConsOut, U2, ReturnOr, U2, ReturnSingleton, U2>>>, Nil>; - type Error = ConcatError; + type Error = Empty; fn to_instruction(&self) -> Result { Ok(Instruction::Concat) @@ -67,12 +68,16 @@ impl IsInstructionT for Concat { } } + +/// input: [x: Bool] +/// output: [x: Bool] +/// +/// Fails iff x is false #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct AssertTrue {} #[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] #[error("AssertTrue: found false")] pub struct AssertTrueError {} -impl AnError for AssertTrueError {} impl IsInstructionT for AssertTrue { type IO = ConsOut, Nil>; @@ -99,8 +104,12 @@ impl IsInstructionT for AssertTrue { } + +/// input: [] +/// output: [T] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Push { + /// The value to push pub push: T, } @@ -123,12 +132,10 @@ impl IsInstructionT for Push { } - +/// input: [Bytes] +/// output: [Bytes] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct HashSha256 {} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] -pub enum HashSha256Error {} -impl AnError for HashSha256Error {} impl IsInstructionT for HashSha256 { type IO = ConsOut, U1>, Nil>; @@ -150,7 +157,11 @@ impl IsInstructionT for HashSha256 { } } - +/// forall T <- {Vec, String, Vec, Map} +/// input: [offset: Number, length: Number, iterable: T] +/// output: [iterable: T] +/// +/// Fails if slice is missing or too big #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Slice {} @@ -185,8 +196,6 @@ impl From for SliceError { } } -impl AnError for SliceError {} - // bytes, string, array, object impl IsInstructionT for Slice { type IO = ConsOut, U1, @@ -266,6 +275,11 @@ impl IsInstructionT for Slice { } + +/// input: [index: Number, iterable: Iterator] +/// output: [iterable[index]: Value] +/// +/// Fails if index is missing or too big #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Index {} #[derive(Clone, Debug, PartialEq, Eq, Error)] @@ -282,7 +296,6 @@ pub enum IndexError { iterable: String, }, } -impl AnError for IndexError {} // bytes, array, object impl IsInstructionT for Index { @@ -336,6 +349,10 @@ impl IsInstructionT for Index { } } +/// input: [x] +/// output: [serde_json::to_value(x): Value] +/// +/// Fails if serde_json::to_value does #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ToJson {} #[derive(Clone, Debug, Error)] @@ -344,7 +361,6 @@ pub struct ToJsonError { input: Elem, error: Arc, } -impl AnError for ToJsonError {} impl IsInstructionT for ToJson { type IO = ConsOut, Cons, Nil>>; @@ -372,6 +388,10 @@ impl IsInstructionT for ToJson { } } +/// input: [key: String, map: Map] +/// output: [map.get(key): Value] +/// +/// Fails if key is missing #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Lookup {} #[derive(Clone, Debug, PartialEq, Eq, Error)] @@ -380,7 +400,6 @@ pub struct LookupError { key: String, map: Map, } -impl AnError for LookupError {} impl IsInstructionT for Lookup { type IO = ConsOut, @@ -410,8 +429,11 @@ impl IsInstructionT for Lookup { } +/// input: [x: Value] +/// output: [AJsonElem::from_value(PhantomData::, x): T] #[derive(Debug)] pub struct UnpackJson { + /// The target type of the Value to unpack pub t: PhantomData, } #[derive(Debug, Error)] @@ -420,7 +442,6 @@ pub struct UnpackJsonError { elem_symbol: EnumSet, input: Value, } -impl AnError for UnpackJsonError {} pub trait AJsonElem: AnElem { fn to_value(self) -> Value; @@ -540,6 +561,10 @@ impl IsInstructionT for UnpackJson { } +/// input: [x: String] +/// output: [x.into_bytes(): Vec] +/// +/// false if incomparable #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct StringToBytes {} @@ -563,6 +588,11 @@ impl IsInstructionT for StringToBytes { } } +/// forall T, +/// input: [x: T, y: T] +/// output: [x <= y : bool] +/// +/// false if incomparable #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CheckLe {} #[derive(Clone, Debug, PartialEq, Eq, Error)] @@ -571,7 +601,6 @@ pub struct CheckLeError { lhs: Elem, rhs: Elem, } -impl AnError for CheckLeError {} impl IsInstructionT for CheckLe { type IO = ConsOut, Cons, Nil>>; @@ -606,6 +635,11 @@ impl IsInstructionT for CheckLe { } } +/// forall T, +/// input: [x: T, y: T] +/// output: [x < y : bool] +/// +/// false if incomparable #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CheckLt {} #[derive(Clone, Debug, PartialEq, Eq, Error)] @@ -614,7 +648,6 @@ pub struct CheckLtError { lhs: Elem, rhs: Elem, } -impl AnError for CheckLtError {} impl IsInstructionT for CheckLt { type IO = ConsOut, Cons, Nil>>; @@ -649,6 +682,11 @@ impl IsInstructionT for CheckLt { } +/// forall T, +/// input: [x: T, y: T] +/// output: [x == y : bool] +/// +/// false if incomparable #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CheckEq {} #[derive(Clone, Debug, PartialEq, Eq, Error)] @@ -657,7 +695,6 @@ pub struct CheckEqError { lhs: Elem, rhs: Elem, } -impl AnError for CheckEqError {} impl IsInstructionT for CheckEq { type IO = ConsOut, Cons, Nil>>; @@ -696,13 +733,9 @@ impl IsInstructionT for CheckEq { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct StringEq {} -#[derive(Clone, Debug, PartialEq, Eq, Error)] -pub enum StringEqError {} -impl AnError for StringEqError {} - impl IsInstructionT for StringEq { type IO = ConsOut, Cons, Nil>>; - type Error = StringEqError; + type Error = Empty; fn to_instruction(&self) -> Result { Ok(Instruction::StringEq) @@ -722,15 +755,14 @@ impl IsInstructionT for StringEq { } } +/// input: [x: Vec, y: Vec] +/// output: [x == y: bool] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct BytesEq {} -#[derive(Clone, Debug, PartialEq, Eq, Error)] -pub enum BytesEqError {} -impl AnError for BytesEqError {} impl IsInstructionT for BytesEq { type IO = ConsOut, Cons, U2>, Nil>>; - type Error = BytesEqError; + type Error = Empty; fn to_instruction(&self) -> Result { Ok(Instruction::BytesEq) diff --git a/src/types.rs b/src/types.rs index dd8989e..1203b32 100644 --- a/src/types.rs +++ b/src/types.rs @@ -32,26 +32,6 @@ impl Iterator for Nil { } } -// TODO: add necessary traits, methods and rename to IsInstructionError or IsInstrError -pub trait AnError: std::error::Error { - // fn to_stack_error(&self, line_no: LineNo) -> StackError; -} - -// impl StackError { -// fn instruction_default(name: &str, error_str: &str, line_no: LineNo) -> Self { -// Self::RunInstruction { -// name: name.to_string(), -// error: error_str.to_string(), -// line_no: line_no, -// } -// } -// } - -impl AnError for Empty { - // fn to_stack_error(&self, _line_no: LineNo) -> StackError { - // self.absurd(PhantomData) - // } -} diff --git a/src/untyped_instruction.rs b/src/untyped_instruction.rs index bb7bae6..d234b93 100644 --- a/src/untyped_instruction.rs +++ b/src/untyped_instruction.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + use crate::elem::{Elem, ElemSymbol}; use crate::restack::{Restack}; diff --git a/src/untyped_instructions.rs b/src/untyped_instructions.rs index f230efd..dd5ed34 100644 --- a/src/untyped_instructions.rs +++ b/src/untyped_instructions.rs @@ -5,8 +5,10 @@ use crate::typed_instrs::Instrs; use serde::{Deserialize, Serialize}; +/// A list of untyped instructions #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Instructions { + /// A list of untyped instructions pub instructions: Vec, } @@ -20,6 +22,7 @@ impl IntoIterator for Instructions { } impl Instructions { + /// Convert to a list of typed instructions pub fn to_instrs(self) -> Result { Ok(Instrs { instrs: self.into_iter().map(|x| x.to_instr()).collect::, InstructionError>>()?, @@ -28,6 +31,7 @@ impl Instructions { } impl Instrs { + /// Convert to a list of untyped instructions pub fn to_instructions(self) -> Result { Ok(Instructions { instructions: self.instrs.into_iter().map(|x| x.to_instruction()).collect::, StackInstructionError>>()?, From 4ff47a8d73c36e486de22f0a6b99f29caa0cf647 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 13 Apr 2022 16:01:39 -0400 Subject: [PATCH 72/77] document queries and json_template, cleanup queries --- src/cli.rs | 13 +++-- src/json_template.rs | 59 ++++++++++++++++++--- src/main.rs | 16 +++--- src/query.rs | 120 ++++++++++++++++++++++++++++++------------- src/types.rs | 4 ++ 5 files changed, 155 insertions(+), 57 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index b3d27ec..6730f2d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,7 +5,7 @@ use crate::untyped_instruction::{InstructionError}; use crate::typed_instruction::{StackInstructionError}; use crate::typed_instrs::{Instrs}; use crate::parse::{parse_json, ParseError}; -use crate::query::{QueryError, Queries}; +use crate::query::{QueryError, QueryTemplates}; use std::fs; use std::io; @@ -23,7 +23,7 @@ use thiserror::Error; #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { - /// Queries to run + /// QueryTemplates to run #[clap(short, long, parse(from_os_str), value_name = "FILE")] queries: PathBuf, @@ -133,9 +133,9 @@ impl From for CliError { impl Cli { /// Get queries from self.queries PathBuf and parse JSON - pub fn parse_queries(&self) -> Result { + pub fn parse_queries(&self) -> Result { let queries_str = fs::read_to_string(self.queries.clone())?; - let queries: Queries = serde_json::from_str(&queries_str)?; + let queries: QueryTemplates = serde_json::from_str(&queries_str)?; Ok(queries) } @@ -175,7 +175,10 @@ impl Cli { stack.push_elem(input_json_value); let variables = serde_json::from_str(&self.variables)?; - let mut queries_result = self.parse_queries()?.run(variables, self.cache_location.clone()).await?; + let mut queries_result = self.parse_queries()? + .run(Arc::new(variables), + Arc::new(self.cache_location.clone())) + .await?; queries_result.reverse(); for query_result in queries_result { stack.push_elem(query_result) diff --git a/src/json_template.rs b/src/json_template.rs index e06c1ab..49b9770 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -9,18 +9,21 @@ use indexmap::{IndexMap}; use serde_json::{Map, Number, Value}; use thiserror::Error; +/// Map defined to be convenient to Serialize and Deserialize #[derive(Clone, Debug, PartialEq, Eq)] pub struct TMap { map: IndexMap, } impl TMap { + /// IndexMap::new pub fn new() -> Self { TMap { map: IndexMap::new(), } } + /// IndexMap::insert pub fn insert(&mut self, key: String, value: T) -> Option { self.map.insert(key, value) } @@ -92,17 +95,32 @@ where } } +/// serde_json::Value with Var's #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum TValue { + /// serde_json::Null Null, + + /// serde_json::Bool Bool(bool), + + /// serde_json::Number Number(Number), + + /// serde_json::String String(String), + + /// serde_json::Array with Var's Array(Vec), + + /// serde_json::Object with Var's Object(TMap), + + /// Named variable. See TValue::run for more detail Var(String), } +/// An error encountered during the execution of TValue::run #[derive(Clone, Debug, PartialEq, Eq)] pub struct TValueRunError { variable: String, @@ -111,6 +129,9 @@ pub struct TValueRunError { } impl TValue { + /// Convert from JSON, ignoring Var's + /// + /// Use Deserialize to convert including Var's pub fn from_json(json: Value) -> Self { match json { Value::Null => Self::Null, @@ -124,12 +145,16 @@ impl TValue { } } + /// Convert to JSON using derived Serialize instance pub fn to_json(&self) -> Result { serde_json::to_value(self) .map_err(|e| TValueError::SerdeJsonError(Arc::new(e))) } - /// Resolve all of the (Var)'s using the given (variables) + /// Resolve all of the (Var)'s using the given variables. + /// + /// For example, if the Map includes the association ("foo", "bar"), + /// all occurences of Var("foo") will be replaced with "bar". pub fn run(self, variables: Map) -> Result { let self_copy = self.clone(); match self { @@ -158,21 +183,41 @@ pub enum TValueError { SerdeJsonError(Arc), } +/// A template that inclues an associated set of variables #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Template { - // TODO: use impl instead of pub - pub variables: Map, - pub template: TValue, + /// Set of variables to resolve on the TValue + variables: Map, + + /// Template value + template: TValue, } impl Template { - pub fn from_json(json: Value) -> Self { - Template { + /// New template with an empty set of variables + pub fn new(template: TValue) -> Self { + Self { variables: Map::new(), - template: TValue::from_json(json), + template: template, } } + /// Set the given variable name to the given Value + pub fn set(&mut self, name: String, value: Value) -> () { + self.variables.insert(name, value); + } + + /// Deserialize the Template from JSON and instantiate an empty set of variables + pub fn from_json(json: Value) -> Self { + Self::new(TValue::from_json(json)) + + // Template { + // variables: Map::new(), + // template: + // } + } + + /// Run the TValue given the provided variables pub fn run(self) -> Result { self.template.run(self.variables) } diff --git a/src/main.rs b/src/main.rs index 7b6643a..755ecc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -372,10 +372,10 @@ async fn main() { // println!(""); - let mut variables = Map::new(); - variables.insert("contractaddress".to_string(), Value::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())); - variables.insert("address".to_string(), Value::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())); - variables.insert("apikey".to_string(), Value::String("YourApiKeyToken".to_string())); + // let mut variables = Map::new(); + // variables.insert("contractaddress".to_string(), Value::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())); + // variables.insert("address".to_string(), Value::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())); + // variables.insert("apikey".to_string(), Value::String("YourApiKeyToken".to_string())); let mut template = TMap::new(); template.insert("type".to_string(), TValue::String("GET".to_string())); @@ -390,10 +390,10 @@ async fn main() { query_parameters.insert("apikey".to_string(), TValue::Var("apikey".to_string())); template.insert("parameters".to_string(), TValue::Object(query_parameters.clone())); - let _full_template = Template { - variables: variables, - template: TValue::Object(template), - }; + let mut full_template = Template::new(TValue::Object(template)); + full_template.set("contractaddress".to_string(), Value::String("0x57d90b64a1a57749b0f932f1a3395792e12e7055".to_string())); + full_template.set("address".to_string(), Value::String("0xe04f27eb70e025b78871a2ad7eabe85e61212761".to_string())); + full_template.set("apikey".to_string(), Value::String("YourApiKeyToken".to_string())); // let json_template = serde_json::to_string_pretty(&serde_json::to_value(full_template.clone()).unwrap()).unwrap(); // println!("{}", json_template); diff --git a/src/query.rs b/src/query.rs index 4a4c6fc..aa66d7d 100644 --- a/src/query.rs +++ b/src/query.rs @@ -10,14 +10,19 @@ use serde_json::{Map, Value}; use thiserror::Error; use tokio_stream::{self as stream, StreamExt}; +/// HTTP request type #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum QueryType { + /// GET request Get, + /// PUT request Put, } +/// A Query template, see Query for additional fields required to run it. +/// This struct is deserialized from an input file by the CLI. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Query { +pub struct QueryTemplate { pub name: String, pub url: String, pub template: TValue, @@ -25,28 +30,38 @@ pub struct Query { pub query_type: QueryType, } +/// Error encountered when running a Query #[derive(Clone, Debug, Error)] pub enum QueryError { - #[error("Query::get_cached: not cached:\n{name:?}\n{url:?}")] + /// The value is not cached + #[error("Query::get_cached: value not cached:\n{name:?}\n{url:?}")] NotCached { + /// Query name name: String, + /// Request URL url: String, }, + /// Running the reqwest request failed #[error("Query::run: request failed:\nresponse:\n{response}")] RequestFailed { + /// Response pretty-printed JSON response: String, }, + /// Error when running query TValue #[error("TValueRunError:\n{0:?}")] TValueRunError(TValueRunError), + /// reqwest::Error #[error("ReqwestError:\n{0}")] ReqwestError(Arc), + /// std::io::Error #[error("StdIoError:\n{0}")] StdIoError(Arc), + /// serde_json::Error #[error("SerdeJsonError:\n{0}")] SerdeJsonError(Arc), } @@ -75,43 +90,67 @@ impl From for QueryError { } } -impl Query { +impl QueryTemplate { pub fn to_json(&self) -> Result { Ok(serde_json::to_value(self)?) } - pub async fn get_cached(&self, variables: Map, cache_location: PathBuf) -> Result { - if self.cached { - println!("Checking cache: {:?}", cache_location.clone()); - let cache_str = fs::read_to_string(cache_location)?; + pub fn to_query(self, variables: Arc>, cache_location: Arc) -> Query { + Query { + query_template: self, + variables: variables, + cache_location: cache_location, + } + } +} + +/// QueryTemplate with variables to instantiate it with and a cache location +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Query { + query_template: QueryTemplate, + variables: Arc>, + cache_location: Arc, +} + +impl Query { + /// Index string to cache this Query + pub fn cache_index(&self) -> String { + format!("{:?}:{:?}", self.query_template.name, self.variables) + } + + /// Get the Query::cache_index value at the given cache_location + pub async fn get_cached(&self) -> Result { + if self.query_template.cached { + println!("Checking cache: {:?}", self.cache_location.clone()); + let cache_str = fs::read_to_string((*self.cache_location).clone())?; let cache: Map = serde_json::from_str(&cache_str)?; - let cache_index = format!("{:?}:{:?}", self.name, variables); - cache.get(&cache_index).ok_or_else(|| { + cache.get(&self.cache_index()).ok_or_else(|| { QueryError::NotCached { - name: self.name.clone(), - url: self.url.clone(), + name: self.query_template.name.clone(), + url: self.query_template.url.clone(), }}).map(|x| x.clone()) } else { Err(QueryError::NotCached { - name: self.name.clone(), - url: self.url.clone(), + name: self.query_template.name.clone(), + url: self.query_template.url.clone(), }) } } - pub async fn put_cached(&self, result: Value, variables: Map, cache_location: PathBuf) -> Result<(), QueryError> { - if self.cached { - println!("Adding to cache: {:?}", cache_location.clone()); - let mut cache: Map = if cache_location.as_path().exists() { - let cache_str = fs::read_to_string(cache_location.clone())?; + /// Put the given result Value in the given cache_location at Query::cache_index, + /// overwriting any existing cached result + pub async fn put_cached(&self, result: Value) -> Result<(), QueryError> { + if self.query_template.cached { + println!("Adding to cache: {:?}", self.cache_location.clone()); + let mut cache: Map = if self.cache_location.as_path().exists() { + let cache_str = fs::read_to_string((*self.cache_location).clone())?; serde_json::from_str(&cache_str)? } else { Map::new() }; - let cache_index = format!("{:?}:{:?}", self.name, variables); - cache.insert(cache_index, result); + cache.insert(self.cache_index(), result); let cache_json = serde_json::to_string_pretty(&serde_json::to_value(cache).unwrap()).unwrap(); - fs::write(cache_location, cache_json)?; + fs::write((*self.cache_location).clone(), cache_json)?; Ok(()) } else { println!("Not cached"); @@ -119,26 +158,32 @@ impl Query { } } - pub async fn run(&self, variables: Map, cache_location: PathBuf) -> Result { - println!("Running Query \"{}\" at \"{}\"", self.name, self.url); - let ran_template = self.clone().template.run(variables.clone())?; + /// Run queries by: + /// 1. Instantiating the template with the given variables + /// 2. Converting the template to JSON + /// 3. Looking up the query in the cache + /// 4. If not found, dispatch along QueryType, sending using reqwest + /// 5. Cache response if successful + pub async fn run(&self) -> Result { + println!("Running Query \"{}\" at \"{}\"", self.query_template.name, self.query_template.url); + let ran_template = self.clone().query_template.template.run((*self.variables).clone())?; match serde_json::to_value(ran_template.clone()).and_then(|x| serde_json::to_string_pretty(&x)) { Ok(json) => println!("{}\n", json), Err(e) => println!("Printing query template failed: {}", e), } - match self.clone().get_cached(variables.clone(), cache_location.clone()).await { + match self.clone().get_cached().await { Ok(result) => { println!("Got cached result..\n"); Ok(result) }, Err(_e) => { let client = Client::new(); - let request_builder = match self.query_type { + let request_builder = match self.query_template.query_type { QueryType::Get => { - client.get(self.url.clone()) + client.get(self.query_template.url.clone()) }, QueryType::Put => { - client.put(self.url.clone()) + client.put(self.query_template.url.clone()) }, }; let response = request_builder @@ -150,7 +195,7 @@ impl Query { let result: Value = response.json() .await .map_err(|e| QueryError::ReqwestError(Arc::new(e)))?; - self.put_cached(result.clone(), variables, cache_location).await?; + self.put_cached(result.clone()).await?; Ok(result) } else { let response_text = response.text() @@ -165,24 +210,25 @@ impl Query { } } -/// An ordered series of Queries +/// An ordered series of QueryTemplates #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Queries { - queries: Vec, +pub struct QueryTemplates { + queries: Vec, } -impl Queries { +impl QueryTemplates { pub fn len(&self) -> usize { self.queries.len() } - pub async fn run(self, variables: Map, cache_location: PathBuf) -> Result>, QueryError> { + pub async fn run(self, variables: Arc>, cache_location: Arc) -> Result>, QueryError> { let mut result = Vec::with_capacity(self.queries.len()); let mut stream = stream::iter(self.queries); - while let Some(query) = stream.next().await { - let query_result = query.run(variables.clone(), cache_location.clone()).await?; + while let Some(query_template) = stream.next().await { + let query_json = query_template.to_json()?; + let query_result = query_template.to_query(variables.clone(), cache_location.clone()).run().await?; let mut query_result_json = Map::new(); - query_result_json.insert("query".to_string(), query.to_json()?); + query_result_json.insert("query".to_string(), query_json); query_result_json.insert("result".to_string(), query_result); result.push(query_result_json) } diff --git a/src/types.rs b/src/types.rs index 1203b32..c928dbf 100644 --- a/src/types.rs +++ b/src/types.rs @@ -855,6 +855,10 @@ pub enum TypeIdMapError { } impl Restack { + /// Calculate the Type of a Restack instruction + /// + /// In short, the input stack is [x_1, x_2, .. x_restack_depth] + /// and the output stack is self.restack(input_stack) // TODO: fix locations: out locations are mislabeled as in locations pub fn type_of(&self, line_no: LineNo) -> Result { let mut context = Context::new(); From 0b5f2a60692c74dc242a924eeb8dd2a8a694c30c Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 13 Apr 2022 16:45:33 -0400 Subject: [PATCH 73/77] add docs for an_elem, arbitrary, elem[_type], json_template, [re]stack, types_scratch --- src/an_elem.rs | 19 ++++++++++- src/arbitrary.rs | 6 ++++ src/elem.rs | 38 +++++++++++++++++++++ src/elem_type.rs | 29 +++++++++++++--- src/json_template.rs | 5 --- src/location.rs | 13 +++++++ src/restack.rs | 81 +++++++++++++++++++++++++------------------- src/stack.rs | 34 +++++++++++++++++-- src/types_scratch.rs | 3 +- 9 files changed, 179 insertions(+), 49 deletions(-) diff --git a/src/an_elem.rs b/src/an_elem.rs index d075119..6e568bf 100644 --- a/src/an_elem.rs +++ b/src/an_elem.rs @@ -8,12 +8,20 @@ use std::marker::PhantomData; use enumset::EnumSet; use serde_json::{Map, Number, Value}; +/// Valid Elem(ent) types +/// +/// TODO: make closed pub trait AnElem: Clone + Debug + PartialEq { - // TODO: rename + // TODO: rename? // fn elem_symbol(t: PhantomData) -> ElemType; + /// The ElemSymbol's associated with the Elem's that can form this type fn elem_symbol(t: PhantomData) -> EnumSet; + + /// Convert the Self to Elem by using one of Elem's constructors fn to_elem(self) -> Elem; + + /// Convert the given Elem to Self through pattern-matching fn from_elem(t: PhantomData, x: Elem) -> Result; } @@ -201,17 +209,26 @@ impl AnElem for Value { } +/// AnElem::from_elem errors #[derive(Clone, Debug, Error)] pub enum AnElemError { + /// AnElem::from_elem: element popped from the Stack wasn't the expected type #[error("AnElem::from_elem: element popped from the stack\n\n{found}\n\nwasn't the expected type:\n{expected:?}")] UnexpectedElemType { + /// ElemSymbol's expected to be popped from the Stack expected: EnumSet, + + /// Elem popped from the Stack found: Elem, }, + /// Converting Elem to Or failed #[error(" as AnElem>::from_elem: {e_hd:?}\n{e_tl:?}")] PopOr { + /// x in Or e_hd: Box, + + /// y in Or e_tl: Box, }, } diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 4d301ff..1d8fb01 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -2,8 +2,10 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; use quickcheck::{empty_shrinker, Arbitrary, Gen}; +/// Wrapped Number for Arbitrary generation #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ArbitraryNumber { + /// Wrapped Number pub number: Number, } @@ -56,8 +58,10 @@ impl Arbitrary for ArbitraryNumber { } +/// Wrapped Map, encoded as a Vec of (key, value) pairs, for Arbitrary generation #[derive(Clone, Debug, PartialEq, Eq)] pub struct ArbitraryMap { + /// Map encoded as a Vec of (key, value) pairs pub map: Vec<(String, Value)>, } @@ -81,8 +85,10 @@ impl Arbitrary for ArbitraryMap { } +/// Wrapped Value for Arbitrary generation #[derive(Clone, Debug, PartialEq, Eq)] pub struct ArbitraryValue { + /// Wrapped Value pub value: Value, } diff --git a/src/elem.rs b/src/elem.rs index a6e272a..497d835 100644 --- a/src/elem.rs +++ b/src/elem.rs @@ -10,15 +10,31 @@ use quickcheck::{empty_shrinker, Arbitrary, Gen}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Number, Value}; +/// An untyped Stack element #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Elem { + /// Unit value (i.e. the empty struct) Unit, + + /// A boolean Bool(bool), + + /// A JSON Number Number(Number), + + /// An array of bytes Bytes(Vec), + + /// A JSON String String(String), + + /// A JSON Array Array(Vec), + + /// A JSON Object Object(Map), + + /// A JSON Value Json(Value), } @@ -65,15 +81,31 @@ impl Display for Elem { // EnumSetType implies: Copy, PartialEq, Eq +/// Elem as an enum of unit (i.e. empty struct) variants #[derive(EnumSetType, Debug, PartialOrd, Ord, Serialize, Deserialize)] pub enum ElemSymbol { + /// Elem::Unit Unit, + + /// Elem::Bool Bool, + + /// Elem::Number Number, + + /// Elem::Bytes Bytes, + + /// Elem::String String, + + /// Elem::Array Array, + + /// Elem::Object Object, + + /// Elem::Json Json, } @@ -90,6 +122,10 @@ impl Arbitrary for ElemSymbol { } impl ElemSymbol { + /// Given a Gen, use this ElemSymbol as a template of an Elem, and fill it + /// with Arbitrary contents. + /// + /// x.arbitrary_contents(g1).symbol() == x.arbitrary_contents(g2).symbol() pub fn arbitrary_contents(&self, g: &mut Gen) -> Elem { match self { Self::Unit => Elem::Unit, @@ -207,10 +243,12 @@ mod elem_symbol_tests { } impl Elem { + /// ElemSymbol of this Elem pub fn symbol(&self) -> ElemSymbol { From::from(self) } + /// ElemSymbol String pub fn symbol_str(&self) -> &'static str { From::from(self.symbol()) } diff --git a/src/elem_type.rs b/src/elem_type.rs index 4413713..58a29ba 100644 --- a/src/elem_type.rs +++ b/src/elem_type.rs @@ -11,15 +11,23 @@ use enumset::{EnumSet}; use serde::{Deserialize, Serialize}; +/// ElemType metadata #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ElemTypeInfo { + /// Location of a variable associated with an ElemType location: Location, } // TODO: make fields private? +/// A set of ElemSymbol's representing a type, with included metadata +/// +/// E.g. {String, bool} represents the type that can be a String or a bool. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ElemType { + /// The set of ElemSymbol's making up this type pub type_set: EnumSet, + + /// Type metadata, for debugging, analysis, pretty printing pub info: Vec, } @@ -86,6 +94,7 @@ mod elem_type_display_tests { } impl ElemSymbol { + /// ElemType of a particular ElemSymbol pub fn elem_type(&self, locations: Vec) -> ElemType { ElemType { type_set: EnumSet::only(*self), @@ -99,12 +108,14 @@ impl ElemSymbol { } impl Elem { + /// ElemType of a particular Elem. See ElemSymbol::elem_type pub fn elem_type(&self, locations: Vec) -> ElemType { self.symbol().elem_type(locations) } } impl ElemType { + /// Construct from a type_set and Vec of Location's pub fn from_locations(type_set: EnumSet, locations: Vec) -> Self { ElemType { @@ -117,22 +128,27 @@ impl ElemType { } } + /// The type of any Elem pub fn any(locations: Vec) -> Self { Self::from_locations( EnumSet::all(), locations) } - pub fn union(&self, other: Self) -> Result { + /// Calculate the union of two ElemType's and append their metadata + pub fn union(&self, other: Self) -> Self { let both = self.type_set.union(other.type_set); let mut both_info = self.info.clone(); both_info.append(&mut other.info.clone()); - Ok(ElemType { + ElemType { type_set: both, info: both_info, - }) + } } + /// Unify two ElemType's by returning their intersection and combining their metadata + /// + /// Fails if their intersection is empty (i.e. if it results in an empty type) pub fn unify(&self, other: Self) -> Result { let both = self.type_set.intersection(other.type_set); if both.is_empty() { @@ -161,7 +177,7 @@ pub enum ElemTypeError { } - +// TODO: relocate // BEGIN DebugAsDisplay #[derive(Clone, PartialEq, Eq)] struct DebugAsDisplay @@ -190,8 +206,10 @@ where } // END DebugAsDisplay +/// The type of a Stack #[derive(Clone, Debug, PartialEq, Eq)] pub struct StackType { + /// List of types of the Stack, in the same order as any Stack of this type pub types: Vec, } @@ -216,14 +234,17 @@ impl FromIterator for StackType { } impl StackType { + /// Length of the StackType, equal to the length of any Stack of this type pub fn len(&self) -> usize { self.types.len() } + /// Push the given ElemType to the StackType pub fn push(&mut self, elem_type: ElemType) -> () { self.types.insert(0, elem_type) } + /// Push (count) copies of the given ElemType to the StackType pub fn push_n(&mut self, elem_type: ElemType, count: usize) -> () { for _index in 0..count { self.push(elem_type.clone()) diff --git a/src/json_template.rs b/src/json_template.rs index 49b9770..1e31971 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -210,11 +210,6 @@ impl Template { /// Deserialize the Template from JSON and instantiate an empty set of variables pub fn from_json(json: Value) -> Self { Self::new(TValue::from_json(json)) - - // Template { - // variables: Map::new(), - // template: - // } } /// Run the TValue given the provided variables diff --git a/src/location.rs b/src/location.rs index 1667deb..e15fe30 100644 --- a/src/location.rs +++ b/src/location.rs @@ -1,7 +1,9 @@ use serde::{Deserialize, Serialize}; +/// Line number, 0-indexed #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct LineNo { + /// Line number pub line_no: usize, } @@ -13,8 +15,11 @@ impl From for LineNo { } } +/// Index of an argument, e.g. Concat has two arguments with indices +/// [0, 1], in that order pub type ArgumentIndex = usize; +/// Location of an input or output #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Location { line_no: LineNo, @@ -23,6 +28,10 @@ pub struct Location { } impl LineNo { + /// From::from(2).in_at(3) is the position: + /// - line_no: 2 + /// - argument_index: 3 + /// - is_input: true pub fn in_at(&self, argument_index: usize) -> Location { Location { line_no: *self, @@ -31,6 +40,10 @@ impl LineNo { } } + /// From::from(2).out_at(3) is the position: + /// - line_no: 2 + /// - argument_index: 3 + /// - is_input: false pub fn out_at(&self, argument_index: usize) -> Location { Location { line_no: *self, diff --git a/src/restack.rs b/src/restack.rs index 7317384..aa5dfc3 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -3,41 +3,46 @@ use std::cmp; use serde::{Deserialize, Serialize}; use thiserror::Error; +// TODO: relocate to Stack module? +/// Stack index pub type StackIx = usize; -// Stack manipulation: -// - All stack manipulations: -// + dig -// + dug -// + dip -// + dup -// + swap -// + drop -// - they all boil down to: -// 1. drop inputs -// 2. replicate inputs -// 3. reorder inputs -// - which conveniently boils down to: -// + xs : [ old_stack_index ] -// + map (\x -> xs !! x) xs -// - successful iff all old_stack_index's < length stack -// - pretty-printing? +// TODO: pretty-printing? // + REQUIRED: constant compile-time choice of manipulations // + local: just print [x_old_stack_index_0, x_old_stack_index_1, ..] // + global: keep track of stack indices (always possible?) and print where it's from??? +/// Stack manipulation: +/// - All these stack manipulations: +/// + dig +/// + dug +/// + dip +/// + dup +/// + swap +/// + drop +/// - Boil down to: +/// 1. drop inputs +/// 2. replicate inputs +/// 3. reorder inputs +/// - Which conveniently boils down to: +/// + xs : [ old_stack_index ] +/// + map (\x -> xs !! x) xs +/// - Which is successful iff all old_stack_index's < stack.len() #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Restack { + /// Number of input stack elements to restack pub restack_depth: StackIx, + + /// Vector of output stack indices pub restack_vec: Vec, } impl Restack { - // (consumed_input_stack_size, produced_output_stack_size) + /// (consumed_input_stack_size, produced_output_stack_size) pub fn stack_io_counts(&self) -> (usize, usize) { (self.restack_depth, self.restack_vec.len()) } - // identity + /// Identity Restack, i.e. does nothing pub fn id() -> Self { Restack { restack_depth: 0, @@ -45,7 +50,7 @@ impl Restack { } } - // swap first two stack elements + /// swap first two stack elements pub fn swap() -> Self { Restack { restack_depth: 2, @@ -53,7 +58,7 @@ impl Restack { } } - // drop the first (n) stack elements + /// drop the first (n) stack elements pub fn drop_n(n: usize) -> Self { Restack { restack_depth: n, @@ -61,12 +66,12 @@ impl Restack { } } - // drop the first stack element + /// Drop the first stack element pub fn drop() -> Self { Self::drop_n(1) } - // duplicates the (ix)th value onto the top of the stack (0-indexed) + /// Duplicates the (ix)th value onto the top of the stack (0-indexed) pub fn dup_n(ix: usize) -> Self { Restack { restack_depth: ix+1, @@ -74,13 +79,14 @@ impl Restack { } } - // duplicates the 0th value onto the top of the stack (0-indexed) + /// Duplicates the 0th value onto the top of the stack (0-indexed) pub fn dup() -> Self { Self::dup_n(0) } - // pull the (ix)th element to the top of the stack - // dig 4 = { 5, [3, 0, 1, 2] } + /// Pull the (ix)th element to the top of the stack + /// + /// dig 4 = { 5, [3, 0, 1, 2] } pub fn dig(ix: usize) -> Self { Restack { restack_depth: ix+1, @@ -88,8 +94,9 @@ impl Restack { } } - // push the top of the stack to the (ix)th position - // dug 4 = { 5, [1, 2, 3, 0] } + /// Push the top of the stack to the (ix)th position + /// + /// dug 4 = { 5, [1, 2, 3, 0] } pub fn dug(ix: usize) -> Self { Restack { restack_depth: ix+1, @@ -97,7 +104,7 @@ impl Restack { } } - // restack a Stack + /// Restack a Stack. See Restack::is_valid_depth for validity checking before running pub fn run(&self, stack: &mut Vec) -> Result<(), RestackError> { if self.restack_depth <= stack.len() { let result = self.restack_vec.iter().map(|&restack_index| @@ -120,15 +127,21 @@ impl Restack { } } - // self.is_valid_depth() -> - // self.restack_depth <= xs.len() -> - // self.run(xs).is_ok() == true + /// If true, Restack::run must succeed on all inputs whose lengths are at + /// least as long as self.restack_depth + /// + /// self.is_valid_depth() -> + /// self.restack_depth <= xs.len() -> + /// self.run(xs).is_ok() == true pub fn is_valid_depth(&self) -> bool { !self.restack_vec.iter().any(|&restack_index| self.restack_depth <= restack_index) } - // NOTE: unchecked (run is_valid_depth on arguments for safe version) - // x.append(y).run(s) == x.run(y.run(s)) + /// Append two Restack's, i.e. compose them together: + /// + /// x.append(y).run(s) == x.run(y.run(s)) + /// + /// NOTE: inputs and result are unchecked (run is_valid_depth on arguments for safe version) pub fn append(&self, other: Self) -> Self { Restack { restack_depth: cmp::max(self.restack_depth, other.restack_depth), diff --git a/src/stack.rs b/src/stack.rs index 47680f2..5d8a355 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -14,8 +14,10 @@ use generic_array::{GenericArray, ArrayLength}; use typenum::marker_traits::Unsigned; // TODO: pub field needed? +/// A Stack of untyped Elem's #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] pub struct Stack { + /// Ordered list of untyped Elem's pub stack: Vec, } @@ -32,6 +34,7 @@ impl Display for Stack { impl Stack { + /// New empty Stack pub fn new() -> Self { Stack { stack: vec![], @@ -40,27 +43,32 @@ impl Stack { // TODO: since pop can fail, require passing debug info to it // (so we know what we were expecting) + /// Pop an Elem from the stack (remove 0th element) pub fn pop(&mut self) -> Result { let result = self.stack.get(0).ok_or_else(|| StackError::EmptyStack).map(|x|x.clone())?; self.stack = self.stack.drain(1..).collect(); Ok(result.clone()) } + /// Pop AnElem from the stack (remove 0th element) pub fn pop_elem(&mut self, _t: PhantomData) -> Result { let hd_elem = self.pop()?; Ok(::from_elem(PhantomData, hd_elem)?) } + /// Push an Elem onto the Stack (new 0th element) pub fn push(&mut self, elem: Elem) { let mut memo = vec![elem]; memo.append(&mut self.stack); self.stack = memo; } + /// Push AnElem onto the Stack (new 0th element) pub fn push_elem(&mut self, elem: impl AnElem) { self.push(elem.to_elem()) } + /// Pop a GenericArray from the Stack // TODO: reversed? pub fn pop_generic_array>(&mut self, _t: PhantomData, @@ -70,19 +78,22 @@ impl Stack { let hd_elem = self.pop()?; xs.push(AnElem::from_elem(PhantomData::, hd_elem)?) } - GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::TODO) + GenericArray::from_exact_iter(xs).ok_or_else(|| StackError::PopGenericArray) } + /// Type of the Stack's elements pub fn type_of(&self) -> StackType { StackType { types: self.stack.clone().into_iter().map(|x| x.elem_type(vec![])).collect(), } } + /// Debug a Stack's type pub fn debug_type(&self) -> () { println!("stack type:\n{}", self.type_of()) } + /// Debug a Stack, including its type pub fn debug(&self) -> Result<(), serde_json::Error> { self.debug_type(); println!("------------------------------------------------------------------------------------------"); @@ -95,30 +106,47 @@ impl Stack { } +/// Stack errors #[derive(Clone, Debug, Error)] pub enum StackError { + /// Stack::pop: tried to pop from an empty stack #[error("Stack::pop: tried to pop from an empty stack")] EmptyStack, + /// Stack::pop_elem error, i.e. type mismatch #[error("Stack:pop_elem threw an error from AnElem\n{0}")] AnElemError(AnElemError), + /// Elem found does not match expected ElemSymbol's #[error("pop: element popped from the stack {found:?} wasn't the expected type {expected:?} (remaining stack: {stack})")] UnexpectedElemTypeIn { + /// Expected ElemSymbol's expected: EnumSet, + + /// Elem found found: Elem, + + /// Stack popped from stack: Stack, }, + /// Running instruction resulted in an error (from IsInstructionT) #[error("Stack::run_instruction: instruction {name:?} produced error: {error:?}\non line number: {line_no:?}")] RunInstruction { + /// Instruction name name: String, + + /// Instruction error error: String, + + /// Instruction line number line_no: LineNo, }, - #[error("Stack::pop_generic_array: unimplemented")] - TODO, + // TODO: add error detail + /// GenericArray::from_exact_iter failed + #[error("Stack::pop_generic_array: failed during GenericArray::from_exact_iter")] + PopGenericArray, } impl From for StackError { diff --git a/src/types_scratch.rs b/src/types_scratch.rs index ccc7829..39e57a6 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -315,8 +315,7 @@ where info: vec![], }; let elem_type_tl = Elems::elem_type(PhantomData::)?; - elem_type_hd.union(elem_type_tl) - .map_err(|e| ElemsPopError::ElemTypeError(e)) + Ok(elem_type_hd.union(elem_type_tl)) } } From b4dbdf884f1b2ada148eaa58a25aa7babe6ff2ba Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Thu, 14 Apr 2022 14:43:26 -0400 Subject: [PATCH 74/77] linting and adding docs --- src/cli.rs | 16 +++--- src/elem_type.rs | 10 ++-- src/json_template.rs | 8 +-- src/lib.rs | 21 +++++--- src/parse.rs | 6 +-- src/query.rs | 19 +++++-- src/restack.rs | 2 +- src/stack.rs | 6 +-- src/typed_instr.rs | 4 +- src/typed_instrs.rs | 12 ++--- src/typed_instruction.rs | 12 ++--- src/typed_instructions.rs | 14 +++--- src/types.rs | 6 +-- src/types_scratch.rs | 100 ++++++++++++++++++++++++------------- src/untyped_instruction.rs | 4 +- 15 files changed, 144 insertions(+), 96 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 6730f2d..6b756a3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,9 +1,9 @@ -use crate::elem_type::{StackType}; -use crate::stack::{Stack}; -use crate::types_scratch::{ElemsPopError}; -use crate::untyped_instruction::{InstructionError}; -use crate::typed_instruction::{StackInstructionError}; -use crate::typed_instrs::{Instrs}; +use crate::elem_type::StackType; +use crate::stack::Stack; +use crate::types_scratch::ElemsPopError; +use crate::untyped_instruction::InstructionError; +use crate::typed_instruction::StackInstructionError; +use crate::typed_instrs::Instrs; use crate::parse::{parse_json, ParseError}; use crate::query::{QueryError, QueryTemplates}; @@ -20,7 +20,7 @@ use thiserror::Error; /// /// Runs the given code (parsed as JSON) on the given input (parsed as JSON) /// and the queries (parsed as JSON into a template -#[derive(Parser)] +#[derive(Debug, Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { /// QueryTemplates to run @@ -49,7 +49,7 @@ pub struct Cli { } /// Command line interface subcommands (optional) -#[derive(Subcommand)] +#[derive(Debug, Subcommand)] enum Commands { /// Parse only Parse, diff --git a/src/elem_type.rs b/src/elem_type.rs index 58a29ba..d068f9e 100644 --- a/src/elem_type.rs +++ b/src/elem_type.rs @@ -1,4 +1,4 @@ -use crate::location::{Location}; +use crate::location::Location; use crate::elem::{Elem, ElemSymbol}; use thiserror::Error; @@ -7,7 +7,7 @@ use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::iter::{FromIterator, IntoIterator}; -use enumset::{EnumSet}; +use enumset::EnumSet; use serde::{Deserialize, Serialize}; @@ -191,7 +191,7 @@ impl Display for DebugAsDisplay where T: Display, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "{}", self.t) } } @@ -200,7 +200,7 @@ impl Debug for DebugAsDisplay where T: Display, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "{}", self.t) } } @@ -255,7 +255,7 @@ impl StackType { // Uses DebugAsDisplay to eliminate '"' around strings: // ["{Number}", "{Array, Object}"] -> [{Number}, {Array, Object}] impl Display for StackType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { f.debug_list() .entries(self.types .iter() diff --git a/src/json_template.rs b/src/json_template.rs index 1e31971..4d944e2 100644 --- a/src/json_template.rs +++ b/src/json_template.rs @@ -1,11 +1,11 @@ -use std::fmt; -use std::sync::{Arc}; +use std::fmt::{self, Formatter}; +use std::sync::Arc; use std::marker::PhantomData; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::de::{Visitor, MapAccess}; -use indexmap::{IndexMap}; +use indexmap::IndexMap; use serde_json::{Map, Number, Value}; use thiserror::Error; @@ -62,7 +62,7 @@ where type Value = TMap; // Format a message stating what data this Visitor expects to receive. - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { // TODO: extend description formatter.write_str("TMap") } diff --git a/src/lib.rs b/src/lib.rs index 2cd5636..b376f32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ -#![warn(missing_docs)] +#![warn(missing_docs, elided_lifetimes_in_paths, explicit_outlives_requirements, keyword_idents, missing_copy_implementations, missing_debug_implementations, non_ascii_idents, noop_method_call, single_use_lifetimes, trivial_casts, trivial_numeric_casts, unreachable_pub, unused_crate_dependencies, unused_extern_crates, unused_import_braces, unused_lifetimes, unused_qualifications)] + +// #![warn(unused_results)] + +#![deny(unsafe_code, unsafe_op_in_unsafe_fn)] mod restack; -pub use restack::Restack; +pub use restack::{Restack, StackIx}; mod arbitrary; pub use arbitrary::{ArbitraryNumber, ArbitraryMap, ArbitraryValue}; mod elem; @@ -11,23 +15,24 @@ pub use elem_type::{ElemType, StackType}; mod an_elem; pub use an_elem::{AnElem, AnElemError}; mod location; -pub use location::{LineNo}; +pub use location::{ArgumentIndex, LineNo}; mod stack; pub use stack::{Stack, StackError}; mod types; mod types_scratch; +pub use types_scratch::{AllElems, IOList}; mod json_template; pub use json_template::{TMap, TValue, TValueRunError, Template}; mod query; -pub use query::{Query, QueryType, QueryError}; +pub use query::{QueryTemplate, QueryTemplates, Query, QueryType, QueryError}; mod untyped_instruction; -pub use untyped_instruction::{Instruction}; +pub use untyped_instruction::Instruction; mod untyped_instructions; -pub use untyped_instructions::{Instructions}; +pub use untyped_instructions::Instructions; mod typed_instruction; -pub use typed_instruction::{IsInstructionT}; +pub use typed_instruction::IsInstructionT; mod typed_instructions; -pub use typed_instructions::{AssertTrue, Push, Lookup, UnpackJson, Index, CheckEq, StringEq}; +pub use typed_instructions::{AssertTrue, Concat, Push, Lookup, UnpackJson, Index, CheckEq, BytesEq, StringEq, CheckLe, CheckLt, StringToBytes, ToJson, Slice, HashSha256}; mod typed_instr; pub use typed_instr::Instr; mod typed_instrs; diff --git a/src/parse.rs b/src/parse.rs index c95ed45..c6f490e 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -9,9 +9,9 @@ /// Where CHARS is any number of characters which aren't escaped double-quotes (\") and HEX is a 64 /// digit hexadecimal number. -use crate::elem::{Elem}; -use crate::untyped_instruction::{Instruction}; -use crate::untyped_instructions::{Instructions}; +use crate::elem::Elem; +use crate::untyped_instruction::Instruction; +use crate::untyped_instructions::Instructions; use std::str::FromStr; diff --git a/src/query.rs b/src/query.rs index aa66d7d..5970f0a 100644 --- a/src/query.rs +++ b/src/query.rs @@ -2,16 +2,16 @@ use crate::json_template::{TValue, TValueRunError}; use std::fs; use std::path::PathBuf; -use std::sync::{Arc}; +use std::sync::Arc; -use reqwest::{Client}; +use reqwest::Client; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use thiserror::Error; use tokio_stream::{self as stream, StreamExt}; /// HTTP request type -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum QueryType { /// GET request Get, @@ -23,10 +23,19 @@ pub enum QueryType { /// This struct is deserialized from an input file by the CLI. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct QueryTemplate { + /// Query name, used for caching, display, and is exposed in the result pub name: String, + + /// Query URL pub url: String, + + /// Query JSON template pub template: TValue, + + /// Whether the result should be cached pub cached: bool, + + /// HTTP request type pub query_type: QueryType, } @@ -91,10 +100,12 @@ impl From for QueryError { } impl QueryTemplate { + /// Convert to a Value pub fn to_json(&self) -> Result { Ok(serde_json::to_value(self)?) } + /// Convert to a Query with the given variables, cache_location, resp. pub fn to_query(self, variables: Arc>, cache_location: Arc) -> Query { Query { query_template: self, @@ -217,10 +228,12 @@ pub struct QueryTemplates { } impl QueryTemplates { + /// Number of queries pub fn len(&self) -> usize { self.queries.len() } + /// Run a list of QueryTemplate's, in series, and collect their results pub async fn run(self, variables: Arc>, cache_location: Arc) -> Result>, QueryError> { let mut result = Vec::with_capacity(self.queries.len()); let mut stream = stream::iter(self.queries); diff --git a/src/restack.rs b/src/restack.rs index aa5dfc3..d99bcf9 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -156,7 +156,7 @@ impl Restack { } -#[derive(Clone, Debug, PartialEq, Error)] +#[derive(Clone, Copy, Debug, PartialEq, Error)] pub enum RestackError { #[error("invalid Restack: restack_index = {restack_index:?} out of bounds for restack_depth = {restack_depth:?}")] StackIndexInvalid { diff --git a/src/stack.rs b/src/stack.rs index 5d8a355..7eb15eb 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -1,13 +1,13 @@ use crate::elem::{Elem, ElemSymbol}; -use crate::elem_type::{StackType}; +use crate::elem_type::StackType; use crate::an_elem::{AnElem, AnElemError}; -use crate::location::{LineNo}; +use crate::location::LineNo; use std::fmt; use std::fmt::{Display, Formatter}; use std::marker::PhantomData; -use enumset::{EnumSet}; +use enumset::EnumSet; use serde::{Deserialize, Serialize}; use thiserror::Error; use generic_array::{GenericArray, ArrayLength}; diff --git a/src/typed_instr.rs b/src/typed_instr.rs index 182adcd..bbed700 100644 --- a/src/typed_instr.rs +++ b/src/typed_instr.rs @@ -1,5 +1,5 @@ -use crate::elem::{ElemSymbol}; -use crate::restack::{Restack}; +use crate::elem::ElemSymbol; +use crate::restack::Restack; use crate::untyped_instruction::{Instruction, InstructionError}; use crate::typed_instruction::{IsStackInstruction, StackInstructionError}; use crate::typed_instructions::{AssertTrue, Lookup, Concat, Slice, Push, diff --git a/src/typed_instrs.rs b/src/typed_instrs.rs index bc55406..3dc02d4 100644 --- a/src/typed_instrs.rs +++ b/src/typed_instrs.rs @@ -1,13 +1,13 @@ -use crate::elem::{ElemSymbol}; +use crate::elem::ElemSymbol; use crate::elem_type::{ElemType, StackType}; -use crate::stack::{Stack}; -use crate::restack::{Restack}; -use crate::types_scratch::{ElemsPopError}; +use crate::stack::Stack; +use crate::restack::Restack; +use crate::types_scratch::ElemsPopError; use crate::typed_instruction::{IsStackInstruction, StackInstructionError}; -use crate::typed_instr::{Instr}; +use crate::typed_instr::Instr; use std::fmt::Debug; -use std::sync::{Arc}; +use std::sync::Arc; use enumset::EnumSet; diff --git a/src/typed_instruction.rs b/src/typed_instruction.rs index 9db0276..83e22a1 100644 --- a/src/typed_instruction.rs +++ b/src/typed_instruction.rs @@ -1,14 +1,14 @@ -use crate::elem::{ElemSymbol}; -use crate::stack::{Stack}; -use crate::restack::{RestackError}; +use crate::elem::ElemSymbol; +use crate::stack::Stack; +use crate::restack::RestackError; use crate::types::{Type, TypeError}; -use crate::types_scratch::{ElemsPopError}; +use crate::types_scratch::ElemsPopError; use crate::types_scratch::{IOList, IsList}; -use crate::untyped_instruction::{Instruction}; +use crate::untyped_instruction::Instruction; use std::marker::PhantomData; use std::fmt::Debug; -use std::sync::{Arc}; +use std::sync::Arc; use thiserror::Error; diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs index 5ad9a70..aefe97a 100644 --- a/src/typed_instructions.rs +++ b/src/typed_instructions.rs @@ -1,8 +1,8 @@ use crate::elem::{Elem, ElemSymbol}; -use crate::an_elem::{AnElem}; +use crate::an_elem::AnElem; use crate::types::{Empty, Nil}; -use crate::types_scratch::{AllElems, all_elems_untyped, Singleton, Cons, ReturnSingleton, ConsOut, Or, ReturnOr, IsList}; -use crate::untyped_instruction::{Instruction}; +use crate::types_scratch::{AllElems, Singleton, Cons, ReturnSingleton, ConsOut, Or, ReturnOr, IsList}; +use crate::untyped_instruction::Instruction; use crate::typed_instruction::{IsInstructionT, StackInstructionError}; use std::cmp; @@ -377,7 +377,7 @@ impl IsInstructionT for ToJson { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); + let array = y.all_elems_untyped(); let z = array[0].clone(); returning.returning(serde_json::to_value(z.clone()) .map_err(move |e| ToJsonError { @@ -617,7 +617,7 @@ impl IsInstructionT for CheckLe { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); + let array = y.all_elems_untyped(); let lhs = array[0].clone(); let rhs = array[1].clone(); let cmp_result = lhs.partial_cmp(&rhs) @@ -664,7 +664,7 @@ impl IsInstructionT for CheckLt { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); + let array = y.all_elems_untyped(); let lhs = array[0].clone(); let rhs = array[1].clone(); let cmp_result = lhs.partial_cmp(&rhs) @@ -711,7 +711,7 @@ impl IsInstructionT for CheckEq { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = all_elems_untyped(y); + let array = y.all_elems_untyped(); let lhs = array[0].clone(); let rhs = array[1].clone(); let cmp_result = lhs.partial_cmp(&rhs) diff --git a/src/types.rs b/src/types.rs index c928dbf..43b9de4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,6 @@ use crate::restack::{Restack, RestackError}; -use crate::location::{LineNo}; -use crate::elem::{Elem}; +use crate::location::LineNo; +use crate::elem::Elem; use crate::elem_type::{ElemType, ElemTypeError, StackType}; use std::cmp; @@ -612,7 +612,7 @@ impl Type { // [ti, tj, .., tk] // ``` impl Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { // TODO: fix normalize // let self_normalized = self.normalize().map_err(|_| fmt::Error)?; let self_normalized = self; diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 39e57a6..34d7a53 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,18 +1,18 @@ -use crate::restack::{RestackError}; +use crate::restack::RestackError; use crate::elem::{Elem, ElemSymbol}; use crate::elem_type::{ElemType, ElemTypeError, StackType}; -use crate::an_elem::{AnElem}; +use crate::an_elem::AnElem; use crate::stack::{Stack, StackError}; use crate::types::{Context, ContextError, Type, Nil}; use std::marker::PhantomData; -use std::fmt::Debug; +use std::fmt::{self, Debug, Formatter}; use std::sync::{Arc, Mutex}; use enumset::EnumSet; use generic_array::functional::FunctionalSequence; use generic_array::sequence::GenericSequence; -use generic_array::typenum::{U0}; +use generic_array::typenum::U0; use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; use serde_json::{Map, Number, Value}; use thiserror::Error; @@ -233,6 +233,21 @@ where Right(::IntoIter), } +impl Debug for IterOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, + ::IntoIter: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + match self { + Self::Left(x) => write!(f, "IterOr::Left({:?})", x), + Self::Right(x) => write!(f, "IterOr::Right({:?})", x), + } + } +} + impl Iterator for IterOr where T: AnElem, @@ -327,6 +342,7 @@ where {} // TODO: AnElem: &self -> AllElems +/// All possible Elem types, encoded using Or and Singleton. pub type AllElems = Or<(), N, Or = Or, N, Singleton>>>>>>>; -pub fn all_elems_untyped(x: &AllElems) -> GenericArray +impl AllElems where N: Debug + ArrayLength<()> + @@ -350,39 +366,37 @@ where ArrayLength + ArrayLength, { - match x { - Or::Left(array) => { - array.map(|_x| Elem::Unit) - }, - Or::Right(Or::Left(array)) => { - array.map(|&x| Elem::Bool(x)) - }, - Or::Right(Or::Right(Or::Left(array))) => { - array.map(|x| Elem::Number(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Left(array)))) => { - array.map(|x| Elem::Bytes(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))) => { - array.map(|x| Elem::String(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array)))))) => { - array.map(|x| Elem::Array(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))))) => { - array.map(|x| Elem::Object(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Singleton { array }))))))) => { - array.map(|x| Elem::Json(x.clone())) - }, + pub fn all_elems_untyped(&self) -> GenericArray { + match self { + Or::Left(array) => { + array.map(|_x| Elem::Unit) + }, + Or::Right(Or::Left(array)) => { + array.map(|&x| Elem::Bool(x)) + }, + Or::Right(Or::Right(Or::Left(array))) => { + array.map(|x| Elem::Number(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Left(array)))) => { + array.map(|x| Elem::Bytes(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))) => { + array.map(|x| Elem::String(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array)))))) => { + array.map(|x| Elem::Array(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))))) => { + array.map(|x| Elem::Object(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Singleton { array }))))))) => { + array.map(|x| Elem::Json(x.clone())) + }, + } } } - - - - #[derive(Clone, Debug)] pub struct ReturnSingleton where @@ -683,6 +697,18 @@ pub struct IterCons { tl: ::IntoIter, } +impl Debug for IterCons +where + T: Elems, + U: IsList, + ::IntoIter: Debug, + ::IntoIter: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "Cons {{\n hd: {:?},\n tl: {:?}\n}}", self.hd, self.tl) + } +} + impl IntoIterator for Cons { type Item = Elem; type IntoIter = IterCons; @@ -757,11 +783,15 @@ where { } - +/// Input-output type of an instruction pub trait IOList: IsList { + /// Returned IOElems type Return: IOElems; + /// Returned value, if set fn returning(&self) -> Option; + + /// IOList's define a complete input/output Type, with exacly one return value fn type_of(t: PhantomData) -> Result; } diff --git a/src/untyped_instruction.rs b/src/untyped_instruction.rs index d234b93..0c37bbd 100644 --- a/src/untyped_instruction.rs +++ b/src/untyped_instruction.rs @@ -1,7 +1,7 @@ #![allow(missing_docs)] use crate::elem::{Elem, ElemSymbol}; -use crate::restack::{Restack}; +use crate::restack::Restack; use std::fmt::Debug; @@ -28,7 +28,7 @@ pub enum Instruction { StringToBytes, } -#[derive(Clone, Debug, Error)] +#[derive(Clone, Copy, Debug, Error)] pub enum InstructionError { #[error("Instruction::to_instr UnpackJson does not support: {elem_symbol:?}")] UnpackJson { From 5c3fca67348c89bfc525073ee2c12cc1816d0afb Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 20 Apr 2022 16:23:40 -0400 Subject: [PATCH 75/77] splitting out Elems into more modules --- src/cli.rs | 2 +- src/elems.rs | 117 +++++++++++++ src/elems_all.rs | 67 ++++++++ src/elems_or.rs | 140 +++++++++++++++ src/elems_singleton.rs | 85 ++++++++++ src/lib.rs | 17 +- src/rest_api.rs | 6 +- src/typed_instrs.rs | 2 +- src/typed_instruction.rs | 2 +- src/typed_instructions.rs | 13 +- src/types_scratch.rs | 348 ++------------------------------------ 11 files changed, 458 insertions(+), 341 deletions(-) create mode 100644 src/elems.rs create mode 100644 src/elems_all.rs create mode 100644 src/elems_or.rs create mode 100644 src/elems_singleton.rs diff --git a/src/cli.rs b/src/cli.rs index 6b756a3..ddc4886 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,6 @@ use crate::elem_type::StackType; use crate::stack::Stack; -use crate::types_scratch::ElemsPopError; +use crate::elems::ElemsPopError; use crate::untyped_instruction::InstructionError; use crate::typed_instruction::StackInstructionError; use crate::typed_instrs::Instrs; diff --git a/src/elems.rs b/src/elems.rs new file mode 100644 index 0000000..a0b5aba --- /dev/null +++ b/src/elems.rs @@ -0,0 +1,117 @@ +use crate::restack::RestackError; +use crate::elem::{Elem, ElemSymbol}; +use crate::elem_type::{ElemType, ElemTypeError, StackType}; +use crate::an_elem::AnElem; +use crate::stack::{Stack, StackError}; +use crate::types::ContextError; + +use std::fmt::Debug; +use std::marker::PhantomData; +use std::sync::Arc; + +use enumset::EnumSet; +use generic_array::{GenericArray, ArrayLength}; +use thiserror::Error; + +// TODO: +// - random type -> ~random inhabitant of the type +// - random typed program? + +/// Errors thrown by Elems::pop +#[derive(Clone, Debug, Error)] +pub enum ElemsPopError { + /// "Elems::pop singleton: tried to pop an Elem that was not found:\nelem_symbol:\n{elem_symbol:?}\n\n{error}" + #[error("Elems::pop singleton: tried to pop an Elem that was not found:\nelem_symbol:\n{elem_symbol:?}\n\n{error}")] + PopSingleton { + /// Expected type set + elem_symbol: EnumSet, + /// Extended StackError + error: StackError, + }, + + /// "Elems::pop: tried to pop a set of Elem's that were not found:\n{hd_error}\n\n{tl_errors}" + #[error("Elems::pop: tried to pop a set of Elem's that were not found:\n{hd_error}\n\n{tl_errors}")] + Pop { + /// Self::Hd pop error + hd_error: Arc, + /// Self::Tl pop error + tl_errors: Arc, + }, + + /// "Elems::pop: generic_array internal error\n\nelem_set:\n{elem_set:?}\n\nvec:\n{vec:?}\n\nsize:\n{size}" + // TODO: add detail + #[error("Elems::pop: generic_array internal error\n\nelem_set:\n{elem_set:?}\n\nvec:\n{vec:?}\n\nsize:\n{size}")] + GenericArray { + /// Expected type set + elem_set: EnumSet, + /// Found Elem's + vec: Vec, + /// Expected size + size: usize, + }, + + /// "IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nelem_set:\n{elem_set}\n\nstack_type:\n{stack_type_of}\n\nerror:\n{error}" + #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nelem_set:\n{elem_set}\n\nstack_type:\n{stack_type_of}\n\nerror:\n{error}")] + IsListHd { + /// Stack found + stack_type: StackType, + /// Expected type + elem_set: ElemType, + /// Stack type found + stack_type_of: StackType, + /// Extended error + error: Arc, + }, + + /// "IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nstack_type_of:\n{stack_type_of}\n\nerror:\n{error}" + #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nstack_type_of:\n{stack_type_of}\n\nerror:\n{error}")] + IsListTl { + /// Stack found + stack_type: StackType, + /// Expected type + stack_type_of: StackType, + /// Extended error + error: Arc, + }, + + /// "Instr::run: ElemTypeError:\n{0}" + #[error("Instr::run: ElemTypeError:\n{0}")] + RestackError(RestackError), + + /// "Elems::elem_type (Or): Set includes repeated type:\n{0}" + #[error("Elems::elem_type (Or): Set includes repeated type:\n{0}")] + ElemTypeError(ElemTypeError), + + /// "::type_of(): ContextError when adding Tl type: {0:?}" + #[error("::type_of(): ContextError when adding Tl type: {0:?}")] + ReturnOrTl(Arc), + + /// "::type_of(): ContextError when adding type:\n{0}" + #[error("::type_of(): ContextError when adding type:\n{0}")] + ReturnOrContextError(ContextError), +} + +/// A set of Elem's with multiplicities, given by Self::N +pub trait Elems: Clone + Debug + IntoIterator { + /// Head Elem + type Hd: AnElem; + /// Multiplicity of the head Elem + type N: ArrayLength; + /// Tail Elems, or Nil + type Tl: Elems; + + // fn left(s: PhantomData, x: GenericArray) -> Self; + // fn right(s: PhantomData, x: Self::Tl) -> Self; + + /// Unpack Self given handlers for Self::Hd and Self::Tl + fn or) -> T, G: Fn(&Self::Tl) -> T>(&self, f: F, g: G) -> T; + + /// Pop Self from a mutable Stack + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized; + + /// Convert to an ElemType + fn elem_type(t: PhantomData) -> Result; +} + diff --git a/src/elems_all.rs b/src/elems_all.rs new file mode 100644 index 0000000..8cc0714 --- /dev/null +++ b/src/elems_all.rs @@ -0,0 +1,67 @@ +use crate::elem::Elem; +use crate::elems_singleton::Singleton; +use crate::elems_or::Or; + +use std::fmt::Debug; + +use serde_json::{Map, Number, Value}; +use generic_array::functional::FunctionalSequence; +use generic_array::{GenericArray, ArrayLength}; + + +// TODO: AnElem: &self -> AllElems +/// All possible Elem types, encoded using Or and Singleton. +pub type AllElems = + Or<(), N, + Or, N, + Or, N, + Or, N, + Singleton>>>>>>>; + +impl AllElems +where + N: Debug + + ArrayLength<()> + + ArrayLength + + ArrayLength + + ArrayLength> + + ArrayLength + + ArrayLength> + + ArrayLength> + + ArrayLength + + ArrayLength, +{ + /// Untype AllElems to Elem + pub fn untyped(&self) -> GenericArray { + match self { + Or::Left(array) => { + array.map(|_x| Elem::Unit) + }, + Or::Right(Or::Left(array)) => { + array.map(|&x| Elem::Bool(x)) + }, + Or::Right(Or::Right(Or::Left(array))) => { + array.map(|x| Elem::Number(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Left(array)))) => { + array.map(|x| Elem::Bytes(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))) => { + array.map(|x| Elem::String(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array)))))) => { + array.map(|x| Elem::Array(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))))) => { + array.map(|x| Elem::Object(x.clone())) + }, + Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Singleton { array }))))))) => { + array.map(|x| Elem::Json(x.clone())) + }, + } + } +} + diff --git a/src/elems_or.rs b/src/elems_or.rs new file mode 100644 index 0000000..6606ec0 --- /dev/null +++ b/src/elems_or.rs @@ -0,0 +1,140 @@ +use crate::stack::Stack; +use crate::elem::Elem; +use crate::elem_type::ElemType; +use crate::an_elem::AnElem; +use crate::elems_singleton::Singleton; +use crate::elems::{Elems, ElemsPopError}; + +use std::marker::PhantomData; +use std::fmt::{self, Debug, Formatter}; +use std::sync::Arc; + +use generic_array::{GenericArray, ArrayLength}; + + +/// Either AnElem with type T an multiplicity N or Elems U, i.e. +/// Or is equivalent to Result, U> with constraints +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Or +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + /// AnElem with type T and multiplicity N. Equivalent to Singleton unwrapped + Left(GenericArray), + /// Other Elems + Right(U), +} + +pub enum IterOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + Left( as IntoIterator>::IntoIter), + Right(::IntoIter), +} + +impl Debug for IterOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, + ::IntoIter: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + match self { + Self::Left(x) => write!(f, "IterOr::Left({:?})", x), + Self::Right(x) => write!(f, "IterOr::Right({:?})", x), + } + } +} + +impl Iterator for IterOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Item = Elem; + + fn next(&mut self) -> Option { + match self { + Self::Left(x) => x.next(), + Self::Right(x) => x.next(), + } + } +} + +impl IntoIterator for Or +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Item = Elem; + type IntoIter = IterOr; + + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Left(array) => IterOr::Left( + Singleton { + array: array, + }.into_iter() + ), + Self::Right(xs) => IterOr::Right(xs.into_iter()), + } + } +} + +impl Elems for Or +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Hd = T; + type N = N; + type Tl = U; + + // fn left(_s: PhantomData, x: GenericArray) -> Self { Self::Left(x) } + // fn right(_s: PhantomData, x: Self::Tl) -> Self { Self::Right(x) } + fn or) -> V, G: Fn(&Self::Tl) -> V>(&self, f: F, g: G) -> V { + match self { + Self::Left(x) => f(x), + Self::Right(x) => g(x), + } + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + match as Elems>::pop(PhantomData, stack) { + Ok(Singleton { array }) => Ok(Self::Left(array)), + Err(hd_error) => { + Elems::pop(PhantomData::, stack) + .map(|x| Self::Right(x)) + .map_err(|tl_errors| { + ElemsPopError::Pop { + hd_error: Arc::new(hd_error), + tl_errors: Arc::new(tl_errors), + } + }) + }, + } + } + + // TODO: add info + fn elem_type(_t: PhantomData) -> Result { + let elem_type_hd = ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }; + let elem_type_tl = Elems::elem_type(PhantomData::)?; + Ok(elem_type_hd.union(elem_type_tl)) + } +} + diff --git a/src/elems_singleton.rs b/src/elems_singleton.rs new file mode 100644 index 0000000..3f8216d --- /dev/null +++ b/src/elems_singleton.rs @@ -0,0 +1,85 @@ +use crate::stack::Stack; +use crate::elem::Elem; +use crate::elem_type::ElemType; +use crate::an_elem::AnElem; +use crate::elems::{Elems, ElemsPopError}; + +use std::fmt::Debug; +use std::marker::PhantomData; + +use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; +use typenum::marker_traits::Unsigned; + +// TODO: rename +/// AnElem with type T and multiplicity N +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + /// An array of AnElem with multiplicity N + pub array: GenericArray, +} + +impl IntoIterator for Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Item = Elem; + type IntoIter = std::iter::Map, fn(T) -> Elem>; + + fn into_iter(self) -> Self::IntoIter { + self.array.into_iter().map(AnElem::to_elem) + } +} + +impl Elems for Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Hd = T; + type N = N; + type Tl = Singleton; + + // fn left(_s: PhantomData, x: GenericArray) -> Self { Singleton { t: x, } } + // fn right(_s: PhantomData, x: Self::Tl) -> Self { x } + fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, _g: G) -> U { + f(&self.array) + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + let vec = (0..::to_usize()).map(|_array_ix| { + stack + .pop_elem(PhantomData::) + .map_err(|e| ElemsPopError::PopSingleton { + elem_symbol: AnElem::elem_symbol(PhantomData::), + error: e, + }) + }).collect::, ElemsPopError>>()?; + let array = GenericArray::from_exact_iter(vec.clone()).ok_or_else(|| { + ElemsPopError::GenericArray { + elem_set: AnElem::elem_symbol(PhantomData::), + vec: vec.into_iter().map(|x| x.to_elem()).collect(), + size: ::to_usize(), + } + })?; + Ok(Singleton { + array: array, + }) + } + + // TODO: add info + fn elem_type(_t: PhantomData) -> Result { + Ok(ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }) + } +} + diff --git a/src/lib.rs b/src/lib.rs index b376f32..be063c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,10 @@ -#![warn(missing_docs, elided_lifetimes_in_paths, explicit_outlives_requirements, keyword_idents, missing_copy_implementations, missing_debug_implementations, non_ascii_idents, noop_method_call, single_use_lifetimes, trivial_casts, trivial_numeric_casts, unreachable_pub, unused_crate_dependencies, unused_extern_crates, unused_import_braces, unused_lifetimes, unused_qualifications)] +//! Cryptoscript Rust Library +//! See cli for the command line interface + +#![warn(missing_docs, elided_lifetimes_in_paths, explicit_outlives_requirements, keyword_idents, missing_copy_implementations, missing_debug_implementations, non_ascii_idents, noop_method_call, single_use_lifetimes, trivial_casts, trivial_numeric_casts, unreachable_pub, unused_extern_crates, unused_import_braces, unused_lifetimes, unused_qualifications)] + +// #![warn(unused_crate_dependencies)] // #![warn(unused_results)] #![deny(unsafe_code, unsafe_op_in_unsafe_fn)] @@ -19,8 +24,16 @@ pub use location::{ArgumentIndex, LineNo}; mod stack; pub use stack::{Stack, StackError}; mod types; +mod elems; +pub use elems::{Elems, ElemsPopError}; +mod elems_singleton; +pub use elems_singleton::Singleton; +mod elems_or; +pub use elems_or::Or; +mod elems_all; +pub use elems_all::AllElems; mod types_scratch; -pub use types_scratch::{AllElems, IOList}; +pub use types_scratch::{IList, IOList}; mod json_template; pub use json_template::{TMap, TValue, TValueRunError, Template}; mod query; diff --git a/src/rest_api.rs b/src/rest_api.rs index 9ba9816..8c2d9e2 100644 --- a/src/rest_api.rs +++ b/src/rest_api.rs @@ -52,7 +52,7 @@ impl Api { /// Update last_api_call pub fn called_now(&self) -> Self { - Api { + Self { request: self.request.clone(), response: self.response.clone(), rate_limit_seconds: self.rate_limit_seconds, @@ -68,6 +68,10 @@ struct AppState { } impl AppState { + // NOTE: unclear why this non-dead code is detected as dead, + // perhaps because it's inside #[actix_web::main]? + #[allow(dead_code)] + /// New AppState with empty set of apis fn new() -> Self { Self { apis: Arc::new(Mutex::new(IndexMap::new())), diff --git a/src/typed_instrs.rs b/src/typed_instrs.rs index 3dc02d4..5e4bef1 100644 --- a/src/typed_instrs.rs +++ b/src/typed_instrs.rs @@ -2,7 +2,7 @@ use crate::elem::ElemSymbol; use crate::elem_type::{ElemType, StackType}; use crate::stack::Stack; use crate::restack::Restack; -use crate::types_scratch::ElemsPopError; +use crate::elems::ElemsPopError; use crate::typed_instruction::{IsStackInstruction, StackInstructionError}; use crate::typed_instr::Instr; diff --git a/src/typed_instruction.rs b/src/typed_instruction.rs index 83e22a1..997787c 100644 --- a/src/typed_instruction.rs +++ b/src/typed_instruction.rs @@ -2,7 +2,7 @@ use crate::elem::ElemSymbol; use crate::stack::Stack; use crate::restack::RestackError; use crate::types::{Type, TypeError}; -use crate::types_scratch::ElemsPopError; +use crate::elems::ElemsPopError; use crate::types_scratch::{IOList, IsList}; use crate::untyped_instruction::Instruction; diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs index aefe97a..d42b4fb 100644 --- a/src/typed_instructions.rs +++ b/src/typed_instructions.rs @@ -1,7 +1,10 @@ use crate::elem::{Elem, ElemSymbol}; use crate::an_elem::AnElem; use crate::types::{Empty, Nil}; -use crate::types_scratch::{AllElems, Singleton, Cons, ReturnSingleton, ConsOut, Or, ReturnOr, IsList}; +use crate::elems_singleton::Singleton; +use crate::elems_or::Or; +use crate::elems_all::AllElems; +use crate::types_scratch::{Cons, ReturnSingleton, ConsOut, ReturnOr, IsList}; use crate::untyped_instruction::Instruction; use crate::typed_instruction::{IsInstructionT, StackInstructionError}; @@ -377,7 +380,7 @@ impl IsInstructionT for ToJson { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = y.all_elems_untyped(); + let array = y.untyped(); let z = array[0].clone(); returning.returning(serde_json::to_value(z.clone()) .map_err(move |e| ToJsonError { @@ -617,7 +620,7 @@ impl IsInstructionT for CheckLe { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = y.all_elems_untyped(); + let array = y.untyped(); let lhs = array[0].clone(); let rhs = array[1].clone(); let cmp_result = lhs.partial_cmp(&rhs) @@ -664,7 +667,7 @@ impl IsInstructionT for CheckLt { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = y.all_elems_untyped(); + let array = y.untyped(); let lhs = array[0].clone(); let rhs = array[1].clone(); let cmp_result = lhs.partial_cmp(&rhs) @@ -711,7 +714,7 @@ impl IsInstructionT for CheckEq { fn run(&self, x: &Self::IO) -> Result<(), Self::Error> { let returning = x.clone().hd().returning; let y = &x.clone().tl().hd(); - let array = y.all_elems_untyped(); + let array = y.untyped(); let lhs = array[0].clone(); let rhs = array[1].clone(); let cmp_result = lhs.partial_cmp(&rhs) diff --git a/src/types_scratch.rs b/src/types_scratch.rs index 34d7a53..9e21570 100644 --- a/src/types_scratch.rs +++ b/src/types_scratch.rs @@ -1,115 +1,31 @@ -use crate::restack::RestackError; -use crate::elem::{Elem, ElemSymbol}; -use crate::elem_type::{ElemType, ElemTypeError, StackType}; +use crate::elem::Elem; +use crate::elem_type::{ElemType, StackType}; use crate::an_elem::AnElem; -use crate::stack::{Stack, StackError}; -use crate::types::{Context, ContextError, Type, Nil}; +use crate::stack::Stack; +use crate::types::{Context, Type, Nil}; +use crate::elems_singleton::Singleton; +use crate::elems_or::Or; +use crate::elems::{Elems, ElemsPopError}; use std::marker::PhantomData; use std::fmt::{self, Debug, Formatter}; use std::sync::{Arc, Mutex}; -use enumset::EnumSet; -use generic_array::functional::FunctionalSequence; use generic_array::sequence::GenericSequence; use generic_array::typenum::U0; -use generic_array::{GenericArray, GenericArrayIter, ArrayLength}; -use serde_json::{Map, Number, Value}; -use thiserror::Error; +use generic_array::{GenericArray, ArrayLength}; use typenum::marker_traits::Unsigned; -// TODO: -// - random type -> ~random inhabitant of the type -// - random typed program? +// TODO: split out +// - IOElems +// + IElems +// + Return +// - IOElems_singleton +// - IOElems_or +// - IsList +// - IOList +// + IOList_cons_out -// TODO: rename -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Singleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - pub array: GenericArray, -} - -impl IntoIterator for Singleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - type Item = Elem; - type IntoIter = std::iter::Map, fn(T) -> Elem>; - - fn into_iter(self) -> Self::IntoIter { - self.array.into_iter().map(AnElem::to_elem) - } -} - -#[derive(Clone, Debug, Error)] -pub enum ElemsPopError { - #[error("Elems::pop singleton: tried to pop an Elem that was not found:\nelem_symbol:\n{elem_symbol:?}\n\n{error}")] - PopSingleton { - elem_symbol: EnumSet, - error: StackError, - }, - - #[error("Elems::pop: tried to pop a set of Elem's that were not found:\n{hd_error}\n\n{tl_errors}")] - Pop { - hd_error: Arc, - tl_errors: Arc, - }, - - // TODO: add detail - #[error("Elems::pop: generic_array internal error\n\nelem_set:\n{elem_set:?}\n\nvec:\n{vec:?}\n\nsize:\n{size}")] - GenericArray { - elem_set: EnumSet, - vec: Vec, - size: usize, - }, - - #[error("IsList::pop (Cons, Hd): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nelem_set:\n{elem_set}\n\nstack_type:\n{stack_type_of}\n\nerror:\n{error}")] - IsListHd { - stack_type: StackType, - elem_set: ElemType, - stack_type_of: StackType, - error: Arc, - }, - - #[error("IsList::pop (Cons, Tl): tried to pop a set of Elem's that were not found:\nstack_type:\n{stack_type}\n\nstack_type_of:\n{stack_type_of}\n\nerror:\n{error}")] - IsListTl { - stack_type: StackType, - stack_type_of: StackType, - error: Arc, - }, - - #[error("Instr::run: ElemTypeError:\n{0}")] - RestackError(RestackError), - - #[error("Elems::elem_type (Or): Set includes repeated type:\n{0}")] - ElemTypeError(ElemTypeError), - - #[error("::type_of(): ContextError when adding Tl type: {0:?}")] - ReturnOrTl(Arc), - - #[error("::type_of(): ContextError when adding type:\n{0}")] - ReturnOrContextError(ContextError), -} - -pub trait Elems: Clone + Debug + IntoIterator { - type Hd: AnElem; - type N: ArrayLength; - type Tl: Elems; - - // fn left(s: PhantomData, x: GenericArray) -> Self; - // fn right(s: PhantomData, x: Self::Tl) -> Self; - fn or) -> T, G: Fn(&Self::Tl) -> T>(&self, f: F, g: G) -> T; - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result - where - Self: Sized; - - fn elem_type(t: PhantomData) -> Result; -} pub trait IElems: Elems {} @@ -155,185 +71,12 @@ pub trait IOElems: Elems { -impl Elems for Singleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - type Hd = T; - type N = N; - type Tl = Singleton; - - // fn left(_s: PhantomData, x: GenericArray) -> Self { Singleton { t: x, } } - // fn right(_s: PhantomData, x: Self::Tl) -> Self { x } - fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, _g: G) -> U { - f(&self.array) - } - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result - where - Self: Sized, - { - let vec = (0..::to_usize()).map(|_array_ix| { - stack - .pop_elem(PhantomData::) - .map_err(|e| ElemsPopError::PopSingleton { - elem_symbol: AnElem::elem_symbol(PhantomData::), - error: e, - }) - }).collect::, ElemsPopError>>()?; - let array = GenericArray::from_exact_iter(vec.clone()).ok_or_else(|| { - ElemsPopError::GenericArray { - elem_set: AnElem::elem_symbol(PhantomData::), - vec: vec.into_iter().map(|x| x.to_elem()).collect(), - size: ::to_usize(), - } - })?; - Ok(Singleton { - array: array, - }) - } - - // TODO: add info - fn elem_type(_t: PhantomData) -> Result { - Ok(ElemType { - type_set: AnElem::elem_symbol(PhantomData::), - info: vec![], - }) - } -} - - impl IElems for Singleton where T: AnElem, N: ArrayLength + Debug, {} - - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Or -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - Left(GenericArray), - Right(U), -} - -pub enum IterOr -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - Left( as IntoIterator>::IntoIter), - Right(::IntoIter), -} - -impl Debug for IterOr -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, - ::IntoIter: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - match self { - Self::Left(x) => write!(f, "IterOr::Left({:?})", x), - Self::Right(x) => write!(f, "IterOr::Right({:?})", x), - } - } -} - -impl Iterator for IterOr -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - type Item = Elem; - - fn next(&mut self) -> Option { - match self { - Self::Left(x) => x.next(), - Self::Right(x) => x.next(), - } - } -} - -impl IntoIterator for Or -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - type Item = Elem; - type IntoIter = IterOr; - - fn into_iter(self) -> Self::IntoIter { - match self { - Self::Left(array) => IterOr::Left( - Singleton { - array: array, - }.into_iter() - ), - Self::Right(xs) => IterOr::Right(xs.into_iter()), - } - } -} - -impl Elems for Or -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - type Hd = T; - type N = N; - type Tl = U; - - // fn left(_s: PhantomData, x: GenericArray) -> Self { Self::Left(x) } - // fn right(_s: PhantomData, x: Self::Tl) -> Self { Self::Right(x) } - fn or) -> V, G: Fn(&Self::Tl) -> V>(&self, f: F, g: G) -> V { - match self { - Self::Left(x) => f(x), - Self::Right(x) => g(x), - } - } - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result - where - Self: Sized, - { - match as Elems>::pop(PhantomData, stack) { - Ok(Singleton { array }) => Ok(Self::Left(array)), - Err(hd_error) => { - Elems::pop(PhantomData::, stack) - .map(|x| Self::Right(x)) - .map_err(|tl_errors| { - ElemsPopError::Pop { - hd_error: Arc::new(hd_error), - tl_errors: Arc::new(tl_errors), - } - }) - }, - } - } - - // TODO: add info - fn elem_type(_t: PhantomData) -> Result { - let elem_type_hd = ElemType { - type_set: AnElem::elem_symbol(PhantomData::), - info: vec![], - }; - let elem_type_tl = Elems::elem_type(PhantomData::)?; - Ok(elem_type_hd.union(elem_type_tl)) - } -} - impl IElems for Or where T: AnElem, @@ -341,61 +84,6 @@ where U: IElems, {} -// TODO: AnElem: &self -> AllElems -/// All possible Elem types, encoded using Or and Singleton. -pub type AllElems = - Or<(), N, - Or, N, - Or, N, - Or, N, - Singleton>>>>>>>; - -impl AllElems -where - N: Debug + - ArrayLength<()> + - ArrayLength + - ArrayLength + - ArrayLength> + - ArrayLength + - ArrayLength> + - ArrayLength> + - ArrayLength + - ArrayLength, -{ - pub fn all_elems_untyped(&self) -> GenericArray { - match self { - Or::Left(array) => { - array.map(|_x| Elem::Unit) - }, - Or::Right(Or::Left(array)) => { - array.map(|&x| Elem::Bool(x)) - }, - Or::Right(Or::Right(Or::Left(array))) => { - array.map(|x| Elem::Number(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Left(array)))) => { - array.map(|x| Elem::Bytes(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))) => { - array.map(|x| Elem::String(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array)))))) => { - array.map(|x| Elem::Array(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Left(array))))))) => { - array.map(|x| Elem::Object(x.clone())) - }, - Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Or::Right(Singleton { array }))))))) => { - array.map(|x| Elem::Json(x.clone())) - }, - } - } -} - #[derive(Clone, Debug)] pub struct ReturnSingleton @@ -769,7 +457,7 @@ impl IsList for Cons { - +/// IsList and defines inputs, but no outputs. See IOList for more info pub trait IList: IsList { } From edcb391e2a079134e315bb88b5811cfa85a08f58 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Fri, 29 Apr 2022 17:10:06 -0400 Subject: [PATCH 76/77] split out modules per major type and add missing docs --- .gitignore | 2 + src/an_elem_return.rs | 47 +++ src/elems.rs | 4 +- src/elems_input.rs | 25 ++ src/elems_input_output.rs | 28 ++ src/elems_input_output_or.rs | 142 +++++++ src/elems_input_output_singleton.rs | 107 +++++ src/elems_list.rs | 73 ++++ src/elems_list_cons.rs | 94 +++++ src/elems_list_input.rs | 19 + src/elems_list_input_output.rs | 47 +++ src/elems_list_input_output_cons.rs | 94 +++++ src/elems_list_nil.rs | 58 +++ src/elems_or.rs | 1 - src/lib.rs | 31 +- src/main.rs | 46 +- src/restack.rs | 16 +- src/typed_instruction.rs | 3 +- src/typed_instructions.rs | 9 +- src/types.rs | 634 ++++------------------------ src/types/context.rs | 396 +++++++++++++++++ src/types/empty.rs | 15 + src/types/type_id.rs | 59 +++ src/types/type_id/map.rs | 84 ++++ src/types_scratch.rs | 585 ------------------------- 25 files changed, 1435 insertions(+), 1184 deletions(-) create mode 100644 src/an_elem_return.rs create mode 100644 src/elems_input.rs create mode 100644 src/elems_input_output.rs create mode 100644 src/elems_input_output_or.rs create mode 100644 src/elems_input_output_singleton.rs create mode 100644 src/elems_list.rs create mode 100644 src/elems_list_cons.rs create mode 100644 src/elems_list_input.rs create mode 100644 src/elems_list_input_output.rs create mode 100644 src/elems_list_input_output_cons.rs create mode 100644 src/elems_list_nil.rs create mode 100644 src/types/context.rs create mode 100644 src/types/empty.rs create mode 100644 src/types/type_id.rs create mode 100644 src/types/type_id/map.rs delete mode 100644 src/types_scratch.rs diff --git a/.gitignore b/.gitignore index 96ef6c0..ae1bb3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target Cargo.lock +*.json +*.profraw diff --git a/src/an_elem_return.rs b/src/an_elem_return.rs new file mode 100644 index 0000000..c1e7e74 --- /dev/null +++ b/src/an_elem_return.rs @@ -0,0 +1,47 @@ +use crate::an_elem::AnElem; + +use std::sync::{Arc, Mutex}; + +/// AnElem that can be returned, e.g. using IOElems or IOList. +/// +/// In other words, a "typed return slot": use Return::new() to initialize an empty slot +#[derive(Clone, Debug)] +pub struct Return { + return_value: Arc>>, +} + +impl Return { + /// New Return slot with nothing in it + pub fn new() -> Self { + Return { + return_value: Arc::new(Mutex::new(None)), + } + } + + // TODO: throw error if try_lock fails + /// Return the given return_value, overwriting any existing value. + /// + /// Panics if Mutex::try_lock fails + pub fn returning(&self, return_value: T) { + let mut lock = (*self.return_value).try_lock(); + if let Ok(ref mut mutex) = lock { + **mutex = Some(return_value) + } else { + panic!("returning: try_lock failed") + } + } + + // TODO: throw error if try_lock fails + /// The stored return_value, or None if nothing has been returned yet. + /// + /// Panics if Mutex::try_lock fails + pub fn returned(&self) -> Option { + let mut lock = (*self.return_value).try_lock(); + if let Ok(ref mut mutex) = lock { + (**mutex).clone() + } else { + panic!("returning: try_lock failed") + } + } +} + diff --git a/src/elems.rs b/src/elems.rs index a0b5aba..b603fe6 100644 --- a/src/elems.rs +++ b/src/elems.rs @@ -3,7 +3,7 @@ use crate::elem::{Elem, ElemSymbol}; use crate::elem_type::{ElemType, ElemTypeError, StackType}; use crate::an_elem::AnElem; use crate::stack::{Stack, StackError}; -use crate::types::ContextError; +use crate::types::context::ContextError; use std::fmt::Debug; use std::marker::PhantomData; @@ -95,8 +95,10 @@ pub enum ElemsPopError { pub trait Elems: Clone + Debug + IntoIterator { /// Head Elem type Hd: AnElem; + /// Multiplicity of the head Elem type N: ArrayLength; + /// Tail Elems, or Nil type Tl: Elems; diff --git a/src/elems_input.rs b/src/elems_input.rs new file mode 100644 index 0000000..0bea87a --- /dev/null +++ b/src/elems_input.rs @@ -0,0 +1,25 @@ +use crate::an_elem::AnElem; +use crate::elems_singleton::Singleton; +use crate::elems_or::Or; +use crate::elems::Elems; + +use std::fmt::Debug; + +use generic_array::ArrayLength; + +/// Input Elems +pub trait IElems: Elems {} + +impl IElems for Singleton +where + T: AnElem, + N: ArrayLength + Debug, +{} + +impl IElems for Or +where + T: AnElem, + N: ArrayLength + Debug, + U: IElems, +{} + diff --git a/src/elems_input_output.rs b/src/elems_input_output.rs new file mode 100644 index 0000000..aae786a --- /dev/null +++ b/src/elems_input_output.rs @@ -0,0 +1,28 @@ +use crate::elem::Elem; +use crate::an_elem_return::Return; +use crate::types::Type; +use crate::elems::{Elems, ElemsPopError}; + +use std::marker::PhantomData; + +use generic_array::GenericArray; + +/// A set of optionally-returned AnElem's with multiplicities +pub trait IOElems: Elems { + /// Unpack either of the Left/Right options: + /// - The Left case is a GenericArray of Self::N copies of Self::Hd, + /// returning Self::Hd + /// - The Right case is Self::Tl + fn or_return(&self, f: F, g: G) -> T + where + F: Fn(&GenericArray, &Return) -> T, + G: Fn(&Self::Tl) -> T; + + // TODO: rename to 'returned' to match Return + /// The returned Elem, if any has been returned + fn returning(&self) -> Option; + + /// The type of the set of optionally-returned AnElem's with multiplicities + fn type_of(t: PhantomData) -> Result; +} + diff --git a/src/elems_input_output_or.rs b/src/elems_input_output_or.rs new file mode 100644 index 0000000..345d877 --- /dev/null +++ b/src/elems_input_output_or.rs @@ -0,0 +1,142 @@ +use crate::elem::Elem; +use crate::elem_type::ElemType; +use crate::an_elem::AnElem; +use crate::stack::Stack; +use crate::types::Type; +use crate::elems_or::Or; +use crate::elems::{Elems, ElemsPopError}; +use crate::elems_input_output::IOElems; +use crate::an_elem_return::Return; + +use std::marker::PhantomData; +use std::fmt::Debug; +use std::sync::Arc; + +use generic_array::{GenericArray, ArrayLength}; + +/// A version of Or where the Left side is equivalent to ReturnSingleton, +/// i.e. Or, U> with appropriate trait constraints to +/// ensure that exactly one typed value is returned. +#[derive(Clone, Debug)] +pub enum ReturnOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + /// ReturnSingleton + Left { + /// N copies of AnElem type T + array: GenericArray, + + /// Returning a single copy of AnElem type T + returning: Return, + }, + + /// The Right or continuation variant. See Or. + Right(U), +} + +impl IntoIterator for ReturnOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Item = Elem; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Left { array, .. } => Or::::Left(array).into_iter(), + Self::Right(xs) => Or::Right(xs).into_iter(), + } + } +} + +impl Elems for ReturnOr +where + T: AnElem, + N: ArrayLength + Debug, + U: Elems, +{ + type Hd = T; + type N = N; + type Tl = U; + + fn or) -> V, G: Fn(&Self::Tl) -> V>(&self, f: F, g: G) -> V { + match self { + Self::Left { array, .. } => f(array), + Self::Right(x) => g(x), + } + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + as Elems>::pop(PhantomData, stack) + .map(|x| { + match x { + Or::Left(array) => Self::Left { + array: array, + returning: Return::new(), + }, + Or::Right(y) => Self::Right(y), + } + }) + } + + fn elem_type(_t: PhantomData) -> Result { + Elems::elem_type(PhantomData::>) + } +} + +impl IOElems for ReturnOr +where + T: AnElem, + N: ArrayLength + Debug, + U: IOElems +{ + fn or_return(&self, f: F, g: G) -> V + where + F: Fn(&GenericArray, &Return) -> V, + G: Fn(&Self::Tl) -> V, + { + match self { + Self::Left { array, returning } => { + f(array, returning) + }, + Self::Right(x) => g(x), + } + } + + fn returning(&self) -> Option { + match self { + Self::Left { returning, .. } => { + returning.returned().map(|x| x.to_elem()) + }, + Self::Right(x) => x.returning(), + } + } + + // TODO: add error info + fn type_of(_t: PhantomData) -> Result { + let mut type_tl = IOElems::type_of(PhantomData::) + .map_err(|e| ElemsPopError::ReturnOrTl(Arc::new(e)))?; + let last_type_id = type_tl.context.max_type_id() + .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; + let next_type_id = type_tl.context.push(ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }); + type_tl.context.unify(last_type_id, next_type_id) + .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; + Ok(type_tl) + } +} + + + + + diff --git a/src/elems_input_output_singleton.rs b/src/elems_input_output_singleton.rs new file mode 100644 index 0000000..79eb36c --- /dev/null +++ b/src/elems_input_output_singleton.rs @@ -0,0 +1,107 @@ +use crate::elem::Elem; +use crate::elem_type::ElemType; +use crate::an_elem::AnElem; +use crate::stack::Stack; +use crate::types::context::Context; +use crate::types::Type; +use crate::elems_singleton::Singleton; +use crate::elems::{Elems, ElemsPopError}; +use crate::elems_input_output::IOElems; +use crate::an_elem_return::Return; + +use std::marker::PhantomData; +use std::fmt::Debug; + +use generic_array::{GenericArray, ArrayLength}; +use typenum::marker_traits::Unsigned; + +/// A Singleton Return-ing AnElem of the same type, but always with a +/// multiplicity of one. +#[derive(Clone, Debug)] +pub struct ReturnSingleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + /// Wrapped Singleton + pub singleton: Singleton, + + /// Typed return slot + pub returning: Return, +} + +impl IntoIterator for ReturnSingleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Item = Elem; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.singleton.into_iter() + } +} + +impl Elems for ReturnSingleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + type Hd = T; + type N = N; + type Tl = Singleton; + + // fn left(_s: PhantomData, x: GenericArray) -> Self { Elems::left(PhantomData::>, x) } + // fn right(_s: PhantomData, x: Self::Tl) -> Self { Elems::left(PhantomData::>, x) } + fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, g: G) -> U { + self.singleton.or(f, g) + } + + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + Ok(ReturnSingleton { + singleton: Elems::pop(PhantomData::>, stack)?, + returning: Return::new(), + }) + } + + fn elem_type(_t: PhantomData) -> Result { + Elems::elem_type(PhantomData::>) + } +} + +impl IOElems for ReturnSingleton +where + T: AnElem, + N: ArrayLength + Debug, +{ + fn or_return(&self, f: F, _g: G) -> U + where + F: Fn(&GenericArray, &Return) -> U, + G: Fn(&Self::Tl) -> U, + { + f(&self.singleton.array, &self.returning) + } + + fn returning(&self) -> Option { + self.returning.returned().map(|x| x.to_elem()) + } + + fn type_of(_t: PhantomData) -> Result { + let num_inputs = ::to_usize(); + let mut context = Context::new(); + let type_id = context.push(ElemType { + type_set: AnElem::elem_symbol(PhantomData::), + info: vec![], + }); + Ok(Type { + context: context, + i_type: (1..num_inputs).into_iter().map(|_| type_id).collect(), + o_type: vec![type_id], + }) + } +} + diff --git a/src/elems_list.rs b/src/elems_list.rs new file mode 100644 index 0000000..0d1ccba --- /dev/null +++ b/src/elems_list.rs @@ -0,0 +1,73 @@ +use crate::elem::Elem; +use crate::elem_type::StackType; +use crate::stack::Stack; +use crate::elems::{Elems, ElemsPopError}; + +use std::marker::PhantomData; +use std::fmt::Debug; +use std::sync::Arc; + +/// A non-empty ordered list of Elems +pub trait IsList: Clone + Debug + IntoIterator { + /// The Hd Elems, or unit if empty + type Hd: Elems; + + /// The rest of the list, or Nil + type Tl: IsList; + + /// Return Self if empty + fn empty_list() -> Option where Self: Sized; + + /// Cons Self::Hd with Self::Tl + fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self; + + /// Is it empty? + fn is_empty(&self) -> bool { + ::empty_list().is_some() + } + + /// Self::Hd can always be returned + fn hd(self) -> Self::Hd; + + /// Self::Tl can always be returned + fn tl(self) -> Self::Tl; + + // fn cons(self, x: T) -> Cons + // where + // Self: Sized, + // { + // Cons { + // hd: x, + // tl: self, + // } + // } + + /// The StackType of this list of Elems + fn stack_type(t: PhantomData) -> Result; + + /// Pop this type from an untyped Stack + fn pop(_x: PhantomData, stack: &mut Stack) -> Result + where + Self: Sized, + { + match ::empty_list() { + Some(x) => Ok(x), + None => { + let original_stack = stack.clone(); + let x = ::pop(PhantomData, stack).or_else(|e| Err(ElemsPopError::IsListHd { + stack_type: IsList::stack_type(PhantomData::)?, + elem_set: Elems::elem_type(PhantomData::)?, + stack_type_of: original_stack.clone().type_of(), + error: Arc::new(e), + }))?; + let xs = ::pop(PhantomData, stack).or_else(|e| Err(ElemsPopError::IsListTl { + stack_type: IsList::stack_type(PhantomData::)?, + stack_type_of: original_stack.clone().type_of(), + error: Arc::new(e), + }))?; + Ok(::cons_list(x, xs)) + } + } + } +} + diff --git a/src/elems_list_cons.rs b/src/elems_list_cons.rs new file mode 100644 index 0000000..d0777f3 --- /dev/null +++ b/src/elems_list_cons.rs @@ -0,0 +1,94 @@ +use crate::elem::Elem; +use crate::elem_type::StackType; +use crate::elems::{Elems, ElemsPopError}; +use crate::elems_list::IsList; + +use std::marker::PhantomData; +use std::fmt::{self, Debug, Formatter}; + +use typenum::marker_traits::Unsigned; + +/// A non-empty list of Elems, where the first element is explicitly provided +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Cons { + /// The head of the list, which must be Elems + pub hd: T, + + /// The tail of the list, which IsList + pub tl: U, +} + +/// IntoIterator applied to Cons::hd and Cons::tl +pub struct IterCons { + hd: ::IntoIter, + tl: ::IntoIter, +} + +impl Debug for IterCons +where + T: Elems, + U: IsList, + ::IntoIter: Debug, + ::IntoIter: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "Cons {{\n hd: {:?},\n tl: {:?}\n}}", self.hd, self.tl) + } +} + +impl IntoIterator for Cons { + type Item = Elem; + type IntoIter = IterCons; + + fn into_iter(self) -> Self::IntoIter { + IterCons { + hd: self.hd.into_iter(), + tl: self.tl.into_iter(), + } + } +} + +impl Iterator for IterCons { + type Item = Elem; + + fn next(&mut self) -> Option { + self.hd.next().or_else(|| self.tl.next()) + } +} + +impl IsList for Cons { + type Hd = T; + type Tl = U; + + fn empty_list() -> Option where Self: Sized { + None + } + + fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self { + Cons { + hd: x, + tl: xs, + } + } + + // fn is_empty(&self) -> bool { + // false + // } + + fn hd(self) -> Self::Hd { + self.hd + } + + fn tl(self) -> Self::Tl { + self.tl + } + + fn stack_type(_t: PhantomData) -> Result { + let elem_type_hd = Elems::elem_type(PhantomData::)?; + let elem_type_hd_count = <::N as Unsigned>::to_usize(); + let mut stack_type_tl = IsList::stack_type(PhantomData::)?; + stack_type_tl.push_n(elem_type_hd, elem_type_hd_count); + Ok(stack_type_tl) + } +} + diff --git a/src/elems_list_input.rs b/src/elems_list_input.rs new file mode 100644 index 0000000..28ed825 --- /dev/null +++ b/src/elems_list_input.rs @@ -0,0 +1,19 @@ +use crate::elems_input::IElems; +use crate::elems_list::IsList; +use crate::elems_list_nil::Nil; +use crate::elems_list_cons::Cons; + +/// IsList that defines inputs, but no outputs. See IOList for more info +pub trait IList: IsList { +} + +impl IList for Nil { +} + +impl IList for Cons +where + T: IElems, + U: IList, +{ +} + diff --git a/src/elems_list_input_output.rs b/src/elems_list_input_output.rs new file mode 100644 index 0000000..03f9069 --- /dev/null +++ b/src/elems_list_input_output.rs @@ -0,0 +1,47 @@ +use crate::elem::Elem; +use crate::types::Type; +use crate::elems::{Elems, ElemsPopError}; +use crate::elems_input::IElems; +use crate::elems_input_output::IOElems; +use crate::elems_list::IsList; +use crate::elems_list_cons::Cons; + +use std::marker::PhantomData; + +use typenum::marker_traits::Unsigned; + +/// Input-output type of an instruction +pub trait IOList: IsList { + /// Returned IOElems + type Return: IOElems; + + // TODO: rename to returned a la Return. + /// Returned value, if set + fn returning(&self) -> Option; + + /// IOList's define a complete input/output Type, with exacly one return value + fn type_of(t: PhantomData) -> Result; +} + +impl IOList for Cons +where + T: IElems, + U: IOList, +{ + type Return = U::Return; + + fn returning(&self) -> Option { + self.tl.returning() + } + + // TODO: test + fn type_of(_t: PhantomData) -> Result { + let num_elem_type_hd = <::N as Unsigned>::to_usize(); + let elem_type_hd = Elems::elem_type(PhantomData::)?; + let mut type_tl = IOList::type_of(PhantomData::)?; + + type_tl.prepend_inputs(num_elem_type_hd, elem_type_hd); + Ok(type_tl) + } +} + diff --git a/src/elems_list_input_output_cons.rs b/src/elems_list_input_output_cons.rs new file mode 100644 index 0000000..8c0a224 --- /dev/null +++ b/src/elems_list_input_output_cons.rs @@ -0,0 +1,94 @@ +use crate::elem::Elem; +use crate::elem_type::StackType; +use crate::types::Type; +use crate::elems::ElemsPopError; +use crate::elems_input_output::IOElems; +use crate::elems_list::IsList; +use crate::elems_list_cons::{Cons, IterCons}; +use crate::elems_list_input::IList; +use crate::elems_list_input_output::IOList; + +use std::marker::PhantomData; +use std::fmt::Debug; + +/// Cons whose hd type is restricted to IOElems and whose tl type is restricted to IList +/// +/// This ensures that there can only be one ConsOut per IOList. +/// By restricting instances of IOList, it must contain exactly one ConsOut. +#[derive(Clone, Debug)] +pub struct ConsOut +where + T: IOElems, + U: IList, +{ + cons: Cons, +} + +impl IntoIterator for ConsOut { + type Item = Elem; + type IntoIter = IterCons; + + fn into_iter(self) -> Self::IntoIter { + self.cons.into_iter() + } +} + +impl IsList for ConsOut +where + T: IOElems, + U: IList +{ + type Hd = T; + type Tl = U; + + fn empty_list() -> Option where Self: Sized { + None + } + + fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self { + ConsOut { + cons: Cons { + hd: x, + tl: xs, + }, + } + } + + fn is_empty(&self) -> bool { + self.cons.is_empty() + } + + fn hd(self) -> Self::Hd { + self.cons.hd() + } + + fn tl(self) -> Self::Tl { + self.cons.tl() + } + + fn stack_type(_t: PhantomData) -> Result { + IsList::stack_type(PhantomData::>) + } +} + +impl IOList for ConsOut +where + T: IOElems, + U: IList, +{ + type Return = T; + + fn returning(&self) -> Option { + self.cons.hd.returning() + } + + // TODO: add info to errors + fn type_of(_t: PhantomData) -> Result { + // let num_elem_type_hd = <::N as Unsigned>::to_usize(); + let mut type_hd = IOElems::type_of(PhantomData::)?; + let elem_type_tl = IsList::stack_type(PhantomData::)?; + type_hd.append_inputs(elem_type_tl); + Ok(type_hd) + } +} + diff --git a/src/elems_list_nil.rs b/src/elems_list_nil.rs new file mode 100644 index 0000000..30b7013 --- /dev/null +++ b/src/elems_list_nil.rs @@ -0,0 +1,58 @@ +use crate::elem::Elem; +use crate::elem_type::StackType; +use crate::elems_list::IsList; +use crate::elems_singleton::Singleton; +use crate::elems::ElemsPopError; + +use std::marker::PhantomData; +use std::fmt::Debug; + +use generic_array::sequence::GenericSequence; +use generic_array::typenum::U0; +use generic_array::GenericArray; + +/// An empty IsList, i.e. an empty list of Elems +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Nil {} + +impl Iterator for Nil { + type Item = Elem; + + fn next(&mut self) -> Option { + None + } +} + +impl IsList for Nil { + type Hd = Singleton<(), U0>; + type Tl = Nil; + + fn empty_list() -> Option where Self: Sized { + Some(Self {}) + } + + fn cons_list(_x: Self::Hd, _xs: Self::Tl) -> Self { + Self {} + } + + // fn is_empty(&self) -> bool { + // true + // } + + fn hd(self) -> Self::Hd { + Singleton { + array: GenericArray::generate(|_| ()), + } + } + + fn tl(self) -> Self::Tl { + Self {} + } + + fn stack_type(_t: PhantomData) -> Result { + Ok(StackType { + types: vec![], + }) + } +} + diff --git a/src/elems_or.rs b/src/elems_or.rs index 6606ec0..38c2d56 100644 --- a/src/elems_or.rs +++ b/src/elems_or.rs @@ -11,7 +11,6 @@ use std::sync::Arc; use generic_array::{GenericArray, ArrayLength}; - /// Either AnElem with type T an multiplicity N or Elems U, i.e. /// Or is equivalent to Result, U> with constraints #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/lib.rs b/src/lib.rs index be063c6..fa1ba47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,11 +19,20 @@ mod elem_type; pub use elem_type::{ElemType, StackType}; mod an_elem; pub use an_elem::{AnElem, AnElemError}; +mod an_elem_return; +pub use an_elem_return::Return; mod location; pub use location::{ArgumentIndex, LineNo}; mod stack; pub use stack::{Stack, StackError}; + mod types; +pub use types::empty::Empty; +pub use types::context::{Context, ContextError}; +pub use types::type_id::TypeId; +pub use types::type_id::map::{TypeIdMap, TypeIdMapError}; +pub use types::{Type, TypeError}; + mod elems; pub use elems::{Elems, ElemsPopError}; mod elems_singleton; @@ -32,8 +41,26 @@ mod elems_or; pub use elems_or::Or; mod elems_all; pub use elems_all::AllElems; -mod types_scratch; -pub use types_scratch::{IList, IOList}; +mod elems_input; +pub use elems_input::IElems; +mod elems_input_output; +pub use elems_input_output::IOElems; +mod elems_input_output_singleton; +pub use elems_input_output_singleton::ReturnSingleton; +mod elems_input_output_or; +pub use elems_input_output_or::ReturnOr; +mod elems_list; +pub use elems_list::IsList; +mod elems_list_nil; +pub use elems_list_nil::Nil; +mod elems_list_cons; +pub use elems_list_cons::{Cons, IterCons}; +mod elems_list_input; +pub use elems_list_input::IList; +mod elems_list_input_output; +pub use elems_list_input_output::IOList; +mod elems_list_input_output_cons; +pub use elems_list_input_output_cons::ConsOut; mod json_template; pub use json_template::{TMap, TValue, TValueRunError, Template}; mod query; diff --git a/src/main.rs b/src/main.rs index 755ecc9..ba6cec8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,28 +13,30 @@ use std::marker::PhantomData; use clap::{Parser}; use serde_json::{Map, Number, Value}; -#[cfg(test)] -mod tests { - use super::*; - use cryptoscript::{parse}; - - #[test] - fn test_parse_exec() { - let instructions = parse( - r#" - push b"I am the walrus."; - hash_sha256; - push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; - check_equal; - assert_true; - "#, - ) - .expect("failed to parse the input"); - Executor::default() - .consume(instructions) - .expect("error processing instructions"); - } -} +// TODO: migrate test to current version +// +// #[cfg(test)] +// mod tests { +// use super::*; +// use cryptoscript::{parse}; + +// #[test] +// fn test_parse_exec() { +// let instructions = parse( +// r#" +// push b"I am the walrus."; +// hash_sha256; +// push 0x475b03e74f7ee448273dbde5ab892746c7b23a2b4d050ccb7d9270b6fb152b72; +// check_equal; +// assert_true; +// "#, +// ) +// .expect("failed to parse the input"); +// Executor::default() +// .consume(instructions) +// .expect("error processing instructions"); +// } +// } #[tokio::main] async fn main() { diff --git a/src/restack.rs b/src/restack.rs index d99bcf9..45770b7 100644 --- a/src/restack.rs +++ b/src/restack.rs @@ -179,7 +179,7 @@ mod tests { let mut example_stack = vec![false, true]; let restack = Restack::id(); assert!(restack.is_valid_depth(), "Restack::id() has invalid depth"); - assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) + assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack).map(|()| example_stack)) } #[test] @@ -188,7 +188,7 @@ mod tests { assert_eq!(Restack { restack_depth: 5, restack_vec: vec![4, 0, 1, 2, 3] }, Restack::dig(4)); let mut example_stack_in = vec![false, false, false, false, true]; let example_stack_out = vec![true, false, false, false, false]; - assert_eq!(Ok(example_stack_out.clone()), Restack::dig(4).run(&mut example_stack_in)) + assert_eq!(Ok(example_stack_out.clone()), Restack::dig(4).run(&mut example_stack_in).map(|()| example_stack_in)) } #[test] @@ -197,20 +197,20 @@ mod tests { assert_eq!(Restack { restack_depth: 5, restack_vec: vec![1, 2, 3, 4, 0] }, Restack::dug(4)); let mut example_stack_in = vec![true, false, false, false, false]; let example_stack_out = vec![false, false, false, false, true]; - assert_eq!(Ok(example_stack_out.clone()), Restack::dug(4).run(&mut example_stack_in)) + assert_eq!(Ok(example_stack_out.clone()), Restack::dug(4).run(&mut example_stack_in).map(|()| example_stack_in)) } #[test] fn test_restack_drop_n() { - let example_stack_in = vec![false, true, false]; for example_stack_out in [vec![false, true, false], vec![true, false], vec![false], vec![]] { + let mut example_stack_in = vec![false, true, false]; let restack = Restack::drop_n(3 - example_stack_out.len()); assert!(restack.is_valid_depth(), "Restack::drop_n(_) has invalid depth"); - assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in.clone())); + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in).map(|()| example_stack_in)); } } @@ -220,7 +220,7 @@ mod tests { let example_stack_out = vec![true]; let restack = Restack::drop(); assert!(restack.is_valid_depth(), "Restack::drop() has invalid depth"); - assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in).map(|()| example_stack_in)) } #[test] @@ -229,7 +229,7 @@ mod tests { let example_stack_out = vec![true, false]; let restack = Restack::swap(); assert!(restack.is_valid_depth(), "Restack::swap() has invalid depth"); - assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in)) + assert_eq!(Ok(example_stack_out), restack.run(&mut example_stack_in).map(|()| example_stack_in)) } #[test] @@ -237,6 +237,6 @@ mod tests { let mut example_stack = vec![false, true]; let restack = Restack::swap().append(Restack::swap()); assert!(restack.is_valid_depth(), "Restack::swap().append(Restack::swap()) has invalid depth"); - assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack)) + assert_eq!(Ok(example_stack.clone()), restack.run(&mut example_stack).map(|()| example_stack)) } } diff --git a/src/typed_instruction.rs b/src/typed_instruction.rs index 997787c..bbe76b5 100644 --- a/src/typed_instruction.rs +++ b/src/typed_instruction.rs @@ -3,7 +3,8 @@ use crate::stack::Stack; use crate::restack::RestackError; use crate::types::{Type, TypeError}; use crate::elems::ElemsPopError; -use crate::types_scratch::{IOList, IsList}; +use crate::elems_list::IsList; +use crate::elems_list_input_output::IOList; use crate::untyped_instruction::Instruction; use std::marker::PhantomData; diff --git a/src/typed_instructions.rs b/src/typed_instructions.rs index d42b4fb..13e0f8a 100644 --- a/src/typed_instructions.rs +++ b/src/typed_instructions.rs @@ -1,10 +1,15 @@ use crate::elem::{Elem, ElemSymbol}; use crate::an_elem::AnElem; -use crate::types::{Empty, Nil}; +use crate::types::empty::Empty; use crate::elems_singleton::Singleton; use crate::elems_or::Or; use crate::elems_all::AllElems; -use crate::types_scratch::{Cons, ReturnSingleton, ConsOut, ReturnOr, IsList}; +use crate::elems_input_output_singleton::ReturnSingleton; +use crate::elems_input_output_or::ReturnOr; +use crate::elems_list::IsList; +use crate::elems_list_nil::Nil; +use crate::elems_list_cons::Cons; +use crate::elems_list_input_output_cons::ConsOut; use crate::untyped_instruction::Instruction; use crate::typed_instruction::{IsInstructionT, StackInstructionError}; diff --git a/src/types.rs b/src/types.rs index 43b9de4..4cd47ab 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,157 +1,21 @@ +pub(crate) mod empty; + +pub(crate) mod type_id; +use type_id::TypeId; +use type_id::map::TypeIdMapError; + +pub(crate) mod context; +use context::{Context, ContextError}; + use crate::restack::{Restack, RestackError}; use crate::location::LineNo; -use crate::elem::Elem; -use crate::elem_type::{ElemType, ElemTypeError, StackType}; +use crate::elem_type::{ElemType, StackType}; -use std::cmp; -use std::collections::BTreeMap; use std::fmt::{Display, Formatter}; use std::fmt; -use std::marker::PhantomData; -use std::sync::Arc; use thiserror::Error; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] -pub enum Empty {} - -impl Empty { - pub fn absurd(&self, _p: PhantomData) -> T { - match *self {} - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Nil {} - -impl Iterator for Nil { - type Item = Elem; - - fn next(&mut self) -> Option { - None - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -////// -//////////// -////// -//////////// -////////////////// -//////////// -////// -//////////// -////// -//////////// -////////////////// -//////////// -////////////////// -//////////////////////// -////////////////// -//////////// -////////////////// -//////////// -////// -//////////// -////// -//////////// -////////////////// -//////////// -////// -//////////// -////// -//////////// -////////////////// -//////////// -////////////////// -//////////////////////// -////////////////// -//////////// -////////////////// -//////////// -////////////////// -//////////////////////// -////////////////// -//////////////////////// -////////////////////////////// -//////////////////////// -////////////////// -//////////////////////// -////////////////// -//////////// -////////////////// -//////////// -////////////////// -//////////////////////// -////////////////// -//////////// -////////////////// -//////////// -////// -//////////// -////// -//////////// -////////////////// -//////////// -////// -//////////// -////// -//////////// -////////////////// -//////////// -////////////////// -//////////////////////// -////////////////// -//////////// -////////////////// -//////////// -////// -//////////// -////// -//////////// -////////////////// -//////////// -////// -//////////// -////// - - // typing: // - unification // + inference @@ -160,273 +24,22 @@ impl Iterator for Nil { // + property tests for typing methods themselves // + test that a function having a particular type -> it runs w/o type errors on such inputs -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeId { - type_id: usize, -} - - -impl Display for TypeId { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "type#{}", self.type_id) - } -} - -impl TypeId { - // TODO: test by checking: - // xs.map(TypeId).fold(x, offset) = TypeId(xs.fold(x, +)) - pub fn offset(&self, offset: TypeId) -> Self { - TypeId { - type_id: self.type_id + offset.type_id, - } - } - - pub fn update_type_id(&self, from: Self, to: Self) -> Self { - if *self == from { - to - } else { - *self - } - } -} - -// TODO: relocate -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Context { - context: BTreeMap, - next_type_id: TypeId, -} - - -// Formatting: -// ``` -// Context { -// context: [ -// (t0, {A, B, C}), -// (t1, {B, C}), -// .. -// (tN, {D, E, F})], -// next_type_id: N+1, -// } -// ``` -// -// Results in: -// ``` -// ∀ (t0 ∊ {A, B, C}), -// ∀ (t1 ∊ {B, C}), -// .. -// ∀ (tN ∊ {D, E, F}), -// ``` -impl Display for Context { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, - "{}", - self.context.iter() - .fold(String::new(), |memo, (i, xs)| { - memo + - "\n" + - &format!("∀ (t{i} ∊ {xs}),", i = i.type_id, xs = xs).to_string() - })) - } -} - -#[cfg(test)] -mod context_display_tests { - use super::*; - - #[test] - fn test_empty() { - let big_type_id = TypeId { - type_id: 2^32 - }; - let context = Context { - context: BTreeMap::new(), - next_type_id: big_type_id, - }; - assert_eq!("", format!("{}", context)); - } - - #[test] - fn test_singleton() { - for elem_symbol in EnumSet::all().iter() { - let elem_type = ElemType { - type_set: EnumSet::only(elem_symbol), - info: vec![], - }; - let mut context_map = BTreeMap::new(); - context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); - let context = Context { - context: context_map, - next_type_id: TypeId { - type_id: 1, - }, - }; - assert_eq!(format!("\n∀ (t0 ∊ {}),", elem_type), format!("{}", context)); - } - } -} - -impl Context { - pub fn new() -> Self { - Context { - context: BTreeMap::new(), - next_type_id: TypeId { - type_id: 0, - }, - } - } - - pub fn is_valid(&self) -> bool { - !self.context.keys().any(|x| *x >= self.next_type_id) - } - - pub fn size(&self) -> usize { - self.context.len() - } - - pub fn push(&mut self, elem_type: ElemType) -> TypeId { - let push_id = self.next_type_id; - self.context.insert(push_id, elem_type); - self.next_type_id = TypeId { - type_id: push_id.type_id + 1, - }; - push_id - } - - // NormalizeOnInvalidBasis is possible iff a `TypeId` in (basis) is repeated - // or missing from (self) - pub fn normalize_on(&self, basis: Vec) -> Result<(Self, TypeIdMap), ContextError> { - let mut source = self.clone(); - let mut result = Self::new(); - let mut type_map = TypeIdMap::new(); - for &type_id in &basis { - match source.context.remove(&type_id) { - None => Err(ContextError::NormalizeOnInvalidBasis { - type_id: type_id, - context: self.clone(), - basis: basis.clone().into_iter().collect(), - }), - Some(elem_type) => { - let new_type_id = result.next_type_id; - result.push(elem_type); - type_map.push(type_id, new_type_id)?; - Ok(()) - }, - }? - } - Ok((result, type_map)) - } - - pub fn offset(&self, offset: TypeId) -> Self { - Context { - context: self.context.iter().map(|(k, x)| (k.offset(offset), x.clone())).collect(), - next_type_id: self.next_type_id.offset(offset), - } - } - - pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), ContextError> { - if self.context.contains_key(&from) { - Ok(()) - } else { - Err(ContextError::UpdateTypeIdFromMissing { - from: from, - to: to, - context: self.clone(), - }) - }?; - if self.context.contains_key(&to) { - Err(ContextError::UpdateTypeIdToPresent { - from: from, - to: to, - context: self.clone(), - }) - } else { - Ok(()) - }?; - self.context = self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(); - self.next_type_id = cmp::max(self.next_type_id, to); - Ok(()) - } - - pub fn disjoint_union(&mut self, other: Self) -> Result<(), ContextError> { - for (&type_id, elem_type) in other.context.iter() { - match self.context.insert(type_id, elem_type.clone()) { - None => { - Ok(()) - }, - Some(conflicting_elem_type) => Err(ContextError::DisjointUnion { - type_id: type_id, - elem_type: elem_type.clone(), - conflicting_elem_type: conflicting_elem_type, - lhs: self.clone(), - rhs: other.clone(), - }), - }? - } - self.next_type_id = cmp::max(self.next_type_id, other.next_type_id); - Ok(()) - } - - pub fn get(&mut self, index: &TypeId, error: &dyn Fn() -> ContextError) -> Result { - Ok(self.context.get(index).ok_or_else(|| ContextError::GetUnknownTypeId { - context: self.clone(), - index: *index, - error: Arc::new(error()), - })?.clone()) - } - - // unify the types of two TypeId's into the rhs - // removing the lhs - pub fn unify(&mut self, xi: TypeId, yi: TypeId) -> Result<(), ContextError> { - let x_type = self.context.remove(&xi).ok_or_else(|| ContextError::Unify { - xs: self.clone(), - xi: xi.clone(), - yi: yi.clone(), - is_lhs: true, - })?; - let y_type = self.context.remove(&yi).ok_or_else(|| ContextError::Unify { - xs: self.clone(), - xi: xi.clone(), - yi: yi.clone(), - is_lhs: false, - })?; - let xy_type = x_type.unify(y_type).or_else(|e| Err(ContextError::UnifyElemType { - xs: self.clone(), - xi: xi.clone(), - yi: yi.clone(), - error: e, - }))?; - self.context.insert(yi, xy_type); - Ok(()) - } - - pub fn unify_elem_type(&mut self, xi: TypeId, elem_type: ElemType) -> Result<(), ContextError> { - let yi = self.push(elem_type); - self.unify(xi, yi) - } - - // maximum possible, not maximum present - pub fn max_type_id(&self) -> Result { - let type_id = self.next_type_id.type_id; - if type_id == 0 { - Err(ContextError::MaxTypeId(self.clone())) - } else { - Ok(TypeId { - type_id: type_id - 1, - }) - } - } -} - - +// TODO: make fields private +/// Type of a series of instructions #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Type { + /// TypeId Context, assigning ElemType's to each TypeId pub context: Context, + + /// Input type stack (all TypeId's must be in the Context) pub i_type: Vec, + + /// Output type stack (all TypeId's must be in the Context) pub o_type: Vec, } impl Type { + /// Identity Type pub fn id() -> Self { Type { context: Context::new(), @@ -435,11 +48,12 @@ impl Type { } } + /// The next TypeId, guaranteed to not be present in the Context pub fn next_type_id(&self) -> TypeId { self.context.next_type_id } - // check whether all the TypeId's are valid + /// check whether all the TypeId's are valid pub fn is_valid(&self) -> bool { let next_type_id = self.next_type_id(); self.context.is_valid() && @@ -447,8 +61,8 @@ impl Type { self.o_type.iter().any(|x| *x >= next_type_id)) } - // equivalent to running update_type_id w/ offset from largest to smallest - // existing TypeId + /// Equivalent to running update_type_id w/ offset from largest to smallest + /// existing TypeId pub fn offset(&self, offset: TypeId) -> Self { Type { context: self.context.offset(offset), @@ -457,6 +71,7 @@ impl Type { } } + /// Update a TypeId, failing if "from" isn't present or "to" already is pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeError> { self.context.update_type_id(from, to).map_err(|e| TypeError::UpdateTypeId(e))?; self.i_type = self.i_type.iter().map(|x| x.update_type_id(from, to)).collect(); @@ -464,6 +79,7 @@ impl Type { Ok(()) } + /// Normalize self.context on self.i_type as a basis pub fn normalize(&self) -> Result { let mut basis = self.i_type.clone(); basis.append(&mut self.o_type.clone()); @@ -476,7 +92,10 @@ impl Type { }) } - /// Returns output stack + /// Specialize self to the given StackType, or fail if it's not a valid + /// specialization. + /// + /// Returns the output stack pub fn specialize_to_input_stack(&mut self, stack_type: StackType) -> Result { if self.i_type.len() <= stack_type.len() { let mut stack_type_iter = stack_type.clone().into_iter(); @@ -514,20 +133,24 @@ impl Type { } } - // f : self - // g : other - // self.compose(other) : (f ++ g).type_of() - // - // input -> - // other.i_type - // other.o_type - // self.i_type - // self.o_type - // -> output - // - // 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context - // 2. collect the remainder and add them to the context - // 3. add the remainder to (self.i_type, other.o_type), with replaced variables + /// Unify two Type's by producing the type of their composition. + /// + /// ``` + /// f : self + /// g : other + /// self.compose(other) : (f ++ g).type_of() + /// + /// input -> + /// other.i_type + /// other.o_type + /// self.i_type + /// self.o_type + /// -> output + /// ``` + /// + /// 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context + /// 2. collect the remainder and add them to the context + /// 3. add the remainder to (self.i_type, other.o_type), with replaced variables pub fn compose(&self, other: Self) -> Result { println!(""); println!("composing:\n{0}\n\nAND\n{1}\n", self, other); @@ -564,6 +187,7 @@ impl Type { }) } + /// Prepend inputs to self.i_type pub fn prepend_inputs(&mut self, num_copies: usize, elem_type: ElemType) -> () { if 0 < num_copies { let type_id = self.context.push(elem_type); @@ -574,6 +198,7 @@ impl Type { } } + /// Append inputs to self.i_type pub fn append_inputs(&mut self, elem_types: T) -> () where T: IntoIterator, @@ -620,18 +245,18 @@ impl Display for Type { "{context}\n[{i_type}] ->\n[{o_type}]", context = self_normalized.context, i_type = self_normalized.i_type.iter().fold(String::new(), |memo, x| { - let x_str = format!("t{}", x.type_id); + let x_str = x.debug(); if memo == "" { x_str } else { - memo + ", " + &x_str.to_string() + memo + ", " + &x_str }}), o_type = self_normalized.o_type.iter().fold(String::new(), |memo, x| { - let x_str = format!("t{}", x.type_id); + let x_str = x.debug(); if memo == "" { x_str } else { - memo + ", " + &x_str.to_string() + memo + ", " + &x_str }})) } } @@ -639,6 +264,7 @@ impl Display for Type { #[cfg(test)] mod type_display_tests { use super::*; + use enumset::EnumSet; #[test] fn test_empty() { @@ -682,178 +308,62 @@ mod type_display_tests { } } - -#[derive(Clone, Debug, PartialEq, Error)] -pub enum ContextError { - #[error("Context::get applied to a TypeId: \n{index:?}\n, not in the Context: \n{context:?}\n, error: \n{error:?}\n")] - GetUnknownTypeId { - context: Context, - index: TypeId, - error: Arc, - }, - - #[error("Context::disjoint_union applied to lhs: \n{lhs:?}\n, and rhs: \n{rhs:?}\n, / - with type_id: \n{type_id:?}\n, and elem_type: \n{elem_type:?}\n, conflicted / - with lhs entry conflicting_elem_type: {conflicting_elem_type:?\n}\n")] - DisjointUnion { - type_id: TypeId, - elem_type: ElemType, - conflicting_elem_type: ElemType, - lhs: Context, - rhs: Context, - }, - - #[error("Context::normalize_on applied to invalid basis: type_id: \n{type_id:?}\n, context: \n{context:?}\n, basis: \n{basis:?}\n")] - NormalizeOnInvalidBasis { - type_id: TypeId, - context: Context, - basis: Vec, - }, - - #[error("Context::update_type_id called on missing 'from: TypeId':\n from: \n{from:?}\n to: {to:?}\n context: {context:?}")] - UpdateTypeIdFromMissing { - from: TypeId, - to: TypeId, - context: Context, - }, - - #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: \n{from:?}\n\n to: \n{to:?}\n context: \n{context:?}\n")] - UpdateTypeIdToPresent { - from: TypeId, - to: TypeId, - context: Context, - }, - - #[error("Context::unify failed:\n xs: \n{xs:?}\n xi: \n{xi:?}\n yi: \n{yi:?}\n is_lhs: \n{is_lhs:?}\n")] - Unify { - xs: Context, - xi: TypeId, - yi: TypeId, - is_lhs: bool, - }, - - #[error("Context::unify failed to unify ElemType's:\n\nxs:\n{xs}\n\nxi:\n{xi}\n\nyi:\n{yi}\n\nelem_error:\n{error}\n")] - UnifyElemType { - xs: Context, - xi: TypeId, - yi: TypeId, - error: ElemTypeError, - }, - - #[error("Type::specialize_to_input_stack failed to resolve ElemType's:\ntype_of:\n{type_of}\n\nstack_type:\n{stack_type}")] - SpecializeToInputStack { - type_of: Type, - stack_type: StackType, - }, - - #[error("Context::normalize_on building TypeIdMap failed: \n{0:?}\n")] - TypeIdMapError(TypeIdMapError), - - #[error("Context::max_type_id: next_type_id == 0: \n{0:?}\n")] - MaxTypeId(Context), -} - -impl From for ContextError { - fn from(error: TypeIdMapError) -> Self { - Self::TypeIdMapError(error) - } -} - - +/// Type trait errors #[derive(Clone, Debug, PartialEq, Error)] pub enum TypeError { + /// "Specialization error:\ntype_id:\n{type_id}\n\nelem_type:\n{elem_type}\n\ncontext:\n{context}\n\nerror:\n{error}" #[error("Specialization error:\ntype_id:\n{type_id}\n\nelem_type:\n{elem_type}\n\ncontext:\n{context}\n\nerror:\n{error}")] Specialization { + /// ElemType with this TypeId is invalid specialization of Context type_id: TypeId, + + /// ElemType for type_id is invalid specialization of Context elem_type: ElemType, + + /// Context not compatible with TypeId, ElemType pair context: Context, + + /// ContextError error: ContextError, }, + /// "NormalizeContextError\n{0}" #[error("NormalizeContextError\n{0}")] NormalizeContextError(ContextError), + /// "ComposeContextError\n{0}" #[error("ComposeContextError\n{0}")] ComposeContextError(ContextError), + /// "TypeError::update_type_id failed when updating the Context:\n{0}" #[error("TypeError::update_type_id failed when updating the Context:\n{0}")] UpdateTypeId(ContextError), + /// "TypeError::compose disjoint_union\n{0}" #[error("TypeError::compose disjoint_union\n{0}")] ComposeDisjointUnion(ContextError), + /// "Type::normalize applying TypeIdMap failed:\n{0}" #[error("Type::normalize applying TypeIdMap failed:\n{0}")] TypeIdMapError(TypeIdMapError), + /// "Type::specialize_to_input_stack ContextError:\n{0}" #[error("Type::specialize_to_input_stack ContextError:\n{0}")] SpecializeToInputStackContextError(ContextError), // TODO: use StackType and Display instead of Vec + /// "Type::specialize_to_input_stack: stack_type shorter than expected:\n{type_of}\n{stack_type}" #[error("Type::specialize_to_input_stack: stack_type shorter than expected:\n{type_of}\n{stack_type}")] SpecializeToInputStack { + /// Type too long for stack_type type_of: Type, + + /// Shorter than expected StackType stack_type: StackType, }, } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeIdMap { - map: BTreeMap, -} - - -impl TypeIdMap { - pub fn new() -> Self { - TypeIdMap { - map: BTreeMap::new(), - } - } - - pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { - if self.map.contains_key(&from) { - Err(TypeIdMapError::PushExists { - from: from, - to: to, - map: self.clone(), - }) - } else { - self.map.insert(from, to); - Ok(()) - } - } - - pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { - self.map.get(index) - .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { - index: index.clone(), - location: location, - type_map: self.clone(), - }) - } - - pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { - type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() - } -} - -#[derive(Clone, Debug, PartialEq, Error)] -pub enum TypeIdMapError { - #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] - GetUnknownTypeId { - index: TypeId, - location: usize, - type_map: TypeIdMap, - }, - - #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] - PushExists { - from: TypeId, - to: TypeId, - map: TypeIdMap, - }, -} - impl Restack { /// Calculate the Type of a Restack instruction /// diff --git a/src/types/context.rs b/src/types/context.rs new file mode 100644 index 0000000..72a6c65 --- /dev/null +++ b/src/types/context.rs @@ -0,0 +1,396 @@ +use crate::elem_type::{ElemType, ElemTypeError, StackType}; +use crate::types::type_id::TypeId; +use crate::types::type_id::map::{TypeIdMap, TypeIdMapError}; +use crate::types::Type; + +use std::cmp; +use std::collections::BTreeMap; +use std::fmt::{Display, Formatter}; +use std::fmt; +use std::sync::Arc; + +use thiserror::Error; + +/// A context defining associations between TypeId's and ElemType's +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Context { + context: BTreeMap, + + /// TODO: make read-only + pub next_type_id: TypeId, +} + +// Formatting: +// ``` +// Context { +// context: [ +// (t0, {A, B, C}), +// (t1, {B, C}), +// .. +// (tN, {D, E, F})], +// next_type_id: N+1, +// } +// ``` +// +// Results in: +// ``` +// ∀ (t0 ∊ {A, B, C}), +// ∀ (t1 ∊ {B, C}), +// .. +// ∀ (tN ∊ {D, E, F}), +// ``` +impl Display for Context { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, + "{}", + self.context.iter() + .fold(String::new(), |memo, (i, xs)| { + memo + + "\n" + + &format!("∀ ({t_i} ∊ {xs}),", t_i = i.debug(), xs = xs).to_string() + })) + } +} + +#[cfg(test)] +mod context_display_tests { + use super::*; + use enumset::EnumSet; + + #[test] + fn test_empty() { + let big_type_id = TypeId { + type_id: 2^32 + }; + let context = Context { + context: BTreeMap::new(), + next_type_id: big_type_id, + }; + assert_eq!("", format!("{}", context)); + } + + #[test] + fn test_singleton() { + for elem_symbol in EnumSet::all().iter() { + let elem_type = ElemType { + type_set: EnumSet::only(elem_symbol), + info: vec![], + }; + let mut context_map = BTreeMap::new(); + context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); + let context = Context { + context: context_map, + next_type_id: TypeId { + type_id: 1, + }, + }; + assert_eq!(format!("\n∀ (t0 ∊ {}),", elem_type), format!("{}", context)); + } + } +} + +impl Context { + /// New empty context with next_type_id = 0. + pub fn new() -> Self { + Context { + context: BTreeMap::new(), + next_type_id: TypeId::new(0), + } + } + + /// Is self.context valid with respect to self.next_type_id? + pub fn is_valid(&self) -> bool { + !self.context.keys().any(|x| *x >= self.next_type_id) + } + + /// The size of self.context + pub fn size(&self) -> usize { + self.context.len() + } + + /// Push a new ElemType onto the Context, returning its TypeId + pub fn push(&mut self, elem_type: ElemType) -> TypeId { + let push_id = self.next_type_id; + self.context.insert(push_id, elem_type); + self.next_type_id = push_id.offset(TypeId::new(1)); + push_id + } + + /// Normalize the naming of TypeId's along the given basis vector, returning a TypeIdMap with + /// the new associations. + /// + /// Note: NormalizeOnInvalidBasis is possible iff a `TypeId` in (basis) is repeated + /// or missing from (self) + pub fn normalize_on(&self, basis: Vec) -> Result<(Self, TypeIdMap), ContextError> { + let mut source = self.clone(); + let mut result = Self::new(); + let mut type_map = TypeIdMap::new(); + for &type_id in &basis { + match source.context.remove(&type_id) { + None => Err(ContextError::NormalizeOnInvalidBasis { + type_id: type_id, + context: self.clone(), + basis: basis.clone().into_iter().collect(), + }), + Some(elem_type) => { + let new_type_id = result.next_type_id; + result.push(elem_type); + type_map.push(type_id, new_type_id)?; + Ok(()) + }, + }? + } + Ok((result, type_map)) + } + + /// Offset all TypeId's + pub fn offset(&self, offset: TypeId) -> Self { + Context { + context: self.context.iter().map(|(k, x)| (k.offset(offset), x.clone())).collect(), + next_type_id: self.next_type_id.offset(offset), + } + } + + /// Update a TypeId, fails if: + /// - The "from" destination TypeId does not exist in self.context + /// - The "to" destination TypeId already exists in self.context + pub fn update_type_id(&mut self, from: TypeId, to: TypeId) -> Result<(), ContextError> { + if self.context.contains_key(&from) { + Ok(()) + } else { + Err(ContextError::UpdateTypeIdFromMissing { + from: from, + to: to, + context: self.clone(), + }) + }?; + if self.context.contains_key(&to) { + Err(ContextError::UpdateTypeIdToPresent { + from: from, + to: to, + context: self.clone(), + }) + } else { + Ok(()) + }?; + self.context = self.context.iter().map(|(k, x)| (k.update_type_id(from, to), x.clone())).collect(); + self.next_type_id = cmp::max(self.next_type_id, to); + Ok(()) + } + + /// Disjoint union of two Context's: fails if not disjoint + pub fn disjoint_union(&mut self, other: Self) -> Result<(), ContextError> { + for (&type_id, elem_type) in other.context.iter() { + match self.context.insert(type_id, elem_type.clone()) { + None => { + Ok(()) + }, + Some(conflicting_elem_type) => Err(ContextError::DisjointUnion { + type_id: type_id, + elem_type: elem_type.clone(), + conflicting_elem_type: conflicting_elem_type, + lhs: self.clone(), + rhs: other.clone(), + }), + }? + } + self.next_type_id = cmp::max(self.next_type_id, other.next_type_id); + Ok(()) + } + + /// Get the ElemType associated with the given TypeId + pub fn get(&mut self, index: &TypeId, error: &dyn Fn() -> ContextError) -> Result { + Ok(self.context.get(index).ok_or_else(|| ContextError::GetUnknownTypeId { + context: self.clone(), + index: *index, + error: Arc::new(error()), + })?.clone()) + } + + /// Unify the types of two TypeId's into the RHS, + /// removing the LHS + pub fn unify(&mut self, xi: TypeId, yi: TypeId) -> Result<(), ContextError> { + let x_type = self.context.remove(&xi).ok_or_else(|| ContextError::Unify { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + is_lhs: true, + })?; + let y_type = self.context.remove(&yi).ok_or_else(|| ContextError::Unify { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + is_lhs: false, + })?; + let xy_type = x_type.unify(y_type).or_else(|e| Err(ContextError::UnifyElemType { + xs: self.clone(), + xi: xi.clone(), + yi: yi.clone(), + error: e, + }))?; + self.context.insert(yi, xy_type); + Ok(()) + } + + /// Unify the given ElemType into a particular TypeId in self.context + pub fn unify_elem_type(&mut self, xi: TypeId, elem_type: ElemType) -> Result<(), ContextError> { + let yi = self.push(elem_type); + self.unify(xi, yi) + } + + /// Maximum possible TypeId, not maximum present + pub fn max_type_id(&self) -> Result { + self.next_type_id.previous().ok_or_else(|| ContextError::MaxTypeId(self.clone())) + // let type_id = self.next_type_id.type_id; + // if type_id == 0 { + // Err(ContextError::MaxTypeId(self.clone())) + // } else { + // Ok(TypeId { + // type_id: type_id - 1, + // }) + // } + } +} + +/// Context trait errors +#[derive(Clone, Debug, PartialEq, Error)] +pub enum ContextError { + /// "Context::get applied to a TypeId: \n{index:?}\n, not in the Context: \n + /// {context:?}\n, error: \n{error:?}\n" + #[error("Context::get applied to a TypeId: \n{index:?}\n, not in the Context: \n{context:?}\n, error: \n{error:?}\n")] + GetUnknownTypeId { + /// Given Context + context: Context, + + /// TypeId not found + index: TypeId, + + /// Associated error + error: Arc, + }, + + /// "Context::disjoint_union applied to lhs: \n{lhs:?}\n, and rhs: \n{rhs:?}\n, + /// with type_id: \n{type_id:?}\n, and elem_type: \n{elem_type:?}\n, + /// conflicted with lhs entry conflicting_elem_type: {conflicting_elem_type:?\n}\n" + #[error("Context::disjoint_union applied to lhs: \n{lhs:?}\n, and rhs: \n{rhs:?}\n, / + with type_id: \n{type_id:?}\n, and elem_type: \n{elem_type:?}\n, conflicted / + with lhs entry conflicting_elem_type: {conflicting_elem_type:?\n}\n")] + DisjointUnion { + /// Conflicting TypeId + type_id: TypeId, + + /// LHS conflicting ElemType + elem_type: ElemType, + + /// RHS conflicting ElemType + conflicting_elem_type: ElemType, + + /// RHS Context + lhs: Context, + + /// RHS Context + rhs: Context, + }, + + /// "Context::normalize_on applied to invalid basis: type_id: \n + /// {type_id:?}\n, context: \n{context:?}\n, basis: \n{basis:?}\n" + #[error("Context::normalize_on applied to invalid basis: type_id: \n{type_id:?}\n, context: \n{context:?}\n, basis: \n{basis:?}\n")] + NormalizeOnInvalidBasis { + /// TypeId invalid for given context, basis + type_id: TypeId, + + /// Given Context + context: Context, + + /// Basis of TypeId's to normalize onto: attempts to sort by this basis + basis: Vec, + }, + + /// "Context::update_type_id called on missing 'from: TypeId':\n from: \n + /// {from:?}\n to: {to:?}\n context: {context:?}" + #[error("Context::update_type_id called on missing 'from: TypeId':\n from: \n{from:?}\n to: {to:?}\n context: {context:?}")] + UpdateTypeIdFromMissing { + /// Updating TypeId from + from: TypeId, + + /// Updating TypeId to + to: TypeId, + + /// Given Context + context: Context, + }, + + /// "Context::update_type_id called on already-present 'to: TypeId':\n from: \n + /// {from:?}\n\n to: \n{to:?}\n context: \n{context:?}\n" + #[error("Context::update_type_id called on already-present 'to: TypeId':\n from: \n{from:?}\n\n to: \n{to:?}\n context: \n{context:?}\n")] + UpdateTypeIdToPresent { + /// Updating TypeId from + from: TypeId, + + /// Updating TypeId to + to: TypeId, + + /// Given Context + context: Context, + }, + + /// "Context::unify failed:\n xs: \n{xs:?}\n xi: \n{xi:?}\n yi: \n{yi:?}\n + /// is_lhs: \n{is_lhs:?}\n" + #[error("Context::unify failed:\n xs: \n{xs:?}\n xi: \n{xi:?}\n yi: \n{yi:?}\n is_lhs: \n{is_lhs:?}\n")] + Unify { + /// Given Context + xs: Context, + + /// RHS + xi: TypeId, + + /// LHS + yi: TypeId, + + /// Is it on the LHS? + is_lhs: bool, + }, + + /// "Context::unify failed to unify ElemType's:\n\nxs:\n{xs}\n\nxi:\n{xi}\n\n + /// yi:\n{yi}\n\nelem_error:\n{error}\n" + #[error("Context::unify failed to unify ElemType's:\n\nxs:\n{xs}\n\nxi:\n{xi}\n\nyi:\n{yi}\n\nelem_error:\n{error}\n")] + UnifyElemType { + /// Given Context + xs: Context, + + /// RHS TypeId + xi: TypeId, + + /// LHS TypeId + yi: TypeId, + + /// ElemTypeError + error: ElemTypeError, + }, + + /// "Type::specialize_to_input_stack failed to resolve ElemType's:\ntype_of:\n + /// {type_of}\n\nstack_type:\n{stack_type}" + #[error("Type::specialize_to_input_stack failed to resolve ElemType's:\ntype_of:\n{type_of}\n\nstack_type:\n{stack_type}")] + SpecializeToInputStack { + /// The type being specialized + type_of: Type, + + /// Stack type attempted to specialize to + stack_type: StackType, + }, + + /// "Context::normalize_on building TypeIdMap failed: \n{0:?}\n" + #[error("Context::normalize_on building TypeIdMap failed: \n{0:?}\n")] + TypeIdMapError(TypeIdMapError), + + /// "Context::max_type_id: next_type_id == 0: \n{0:?}\n" + #[error("Context::max_type_id: next_type_id == 0: \n{0:?}\n")] + MaxTypeId(Context), +} + +impl From for ContextError { + fn from(error: TypeIdMapError) -> Self { + Self::TypeIdMapError(error) + } +} + diff --git a/src/types/empty.rs b/src/types/empty.rs new file mode 100644 index 0000000..e289aad --- /dev/null +++ b/src/types/empty.rs @@ -0,0 +1,15 @@ +use std::marker::PhantomData; + +use thiserror::Error; + +/// An empty enum or impossible-to-inhabit type +#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)] +pub enum Empty {} + +impl Empty { + /// Given Empty, produce anything + pub fn absurd(&self, _p: PhantomData) -> T { + match *self {} + } +} + diff --git a/src/types/type_id.rs b/src/types/type_id.rs new file mode 100644 index 0000000..a7a725f --- /dev/null +++ b/src/types/type_id.rs @@ -0,0 +1,59 @@ + +pub(crate) mod map; + +use std::fmt::{Display, Formatter}; +use std::fmt; + +/// A Type ID, represented as a usize +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct TypeId { + type_id: usize, +} + +impl Display for TypeId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "type#{}", self.type_id) + } +} + +impl TypeId { + /// New TypeId with the given ID + pub fn new(type_id: usize) -> Self { + Self { + type_id: type_id, + } + } + + /// format!("t{}", self.type_id) + pub fn debug(&self) -> String { + format!("t{}", self.type_id) + } + + /// Subtract one or return None if 0 + pub fn previous(&self) -> Option { + self.type_id.checked_sub(1).map(|type_id| Self { + type_id: type_id, + }) + } + + // TODO: test by checking: + // xs.map(TypeId).fold(x, offset) = TypeId(xs.fold(x, +)) + /// Offset (add) one TypeId to another + pub fn offset(&self, offset: TypeId) -> Self { + TypeId { + type_id: self.type_id + offset.type_id, + } + } + + /// Replaces "from" TypeId with "to" TypeId. + /// + /// For compatibility with update_type_id in Context, etc. + pub fn update_type_id(&self, from: Self, to: Self) -> Self { + if *self == from { + to + } else { + *self + } + } +} + diff --git a/src/types/type_id/map.rs b/src/types/type_id/map.rs new file mode 100644 index 0000000..ad01139 --- /dev/null +++ b/src/types/type_id/map.rs @@ -0,0 +1,84 @@ +use crate::types::type_id::TypeId; + +use std::collections::BTreeMap; + +use thiserror::Error; + +/// A mapping between assignments of TypeId's +/// +/// Used to preserve consistency of associations from TypeId to ElemType when +/// updating multiple TypeId's +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct TypeIdMap { + map: BTreeMap, +} + +impl TypeIdMap { + /// New empty TypeIdMap + pub fn new() -> Self { + TypeIdMap { + map: BTreeMap::new(), + } + } + + /// Add a mapping to the TypeIdMap, failing if the "from" TypeId" already + /// exists in the map + pub fn push(&mut self, from: TypeId, to: TypeId) -> Result<(), TypeIdMapError> { + if self.map.contains_key(&from) { + Err(TypeIdMapError::PushExists { + from: from, + to: to, + map: self.clone(), + }) + } else { + self.map.insert(from, to); + Ok(()) + } + } + + /// Resolve the map on a single TypeId + pub fn get(&self, index: &TypeId, location: usize) -> Result<&TypeId, TypeIdMapError> { + self.map.get(index) + .ok_or_else(|| TypeIdMapError::GetUnknownTypeId { + index: index.clone(), + location: location, + type_map: self.clone(), + }) + } + + /// Resolve the map on a Vec of TypeId's + pub fn run(&self, type_vars: Vec) -> Result, TypeIdMapError> { + type_vars.iter().enumerate().map(|(i, x)| Ok(self.get(x, i)?.clone())).collect() + } +} + +/// TypeIdMap trait errors +#[derive(Clone, Debug, PartialEq, Error)] +pub enum TypeIdMapError { + /// "TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}" + #[error("TypeIdMap::get attempted to get a TypeId: {index:?}, not in the map: {type_map:?}; at location in TypeIdMap::run {location:?}")] + GetUnknownTypeId { + /// Missing TypeId + index: TypeId, + + /// TypeIdMap::run location + location: usize, + + /// index missing from this TypeIdMap + type_map: TypeIdMap, + }, + + /// "TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}" + #[error("TypeIdMap::push already exists: mapping from: {from:?}, to: {to:?}, in TypeIdMap {map:?}")] + PushExists { + /// _.push(from, _) + from: TypeId, + + /// _.push(_, to) + to: TypeId, + + /// TypeId "from" already present in this TypeIdMap + map: TypeIdMap, + }, +} + diff --git a/src/types_scratch.rs b/src/types_scratch.rs deleted file mode 100644 index 9e21570..0000000 --- a/src/types_scratch.rs +++ /dev/null @@ -1,585 +0,0 @@ -use crate::elem::Elem; -use crate::elem_type::{ElemType, StackType}; -use crate::an_elem::AnElem; -use crate::stack::Stack; -use crate::types::{Context, Type, Nil}; -use crate::elems_singleton::Singleton; -use crate::elems_or::Or; -use crate::elems::{Elems, ElemsPopError}; - -use std::marker::PhantomData; -use std::fmt::{self, Debug, Formatter}; -use std::sync::{Arc, Mutex}; - -use generic_array::sequence::GenericSequence; -use generic_array::typenum::U0; -use generic_array::{GenericArray, ArrayLength}; -use typenum::marker_traits::Unsigned; - -// TODO: split out -// - IOElems -// + IElems -// + Return -// - IOElems_singleton -// - IOElems_or -// - IsList -// - IOList -// + IOList_cons_out - - -pub trait IElems: Elems {} - - -#[derive(Clone, Debug)] -pub struct Return { - return_value: Arc>>, -} - -impl Return { - // TODO: throw error if try_lock fails - pub fn returning(&self, return_value: T) { - let mut lock = (*self.return_value).try_lock(); - if let Ok(ref mut mutex) = lock { - **mutex = Some(return_value) - } else { - panic!("returning: TODO") - } - } - - // TODO: throw error if try_lock fails - pub fn returned(&self) -> Option { - let mut lock = (*self.return_value).try_lock(); - if let Ok(ref mut mutex) = lock { - (**mutex).clone() - } else { - panic!("returned: TODO") - } - } -} - -pub trait IOElems: Elems { - fn or_return(&self, f: F, g: G) -> T - where - F: Fn(&GenericArray, &Return) -> T, - G: Fn(&Self::Tl) -> T; - - // TODO: rename to 'returned' to match Return - fn returning(&self) -> Option; - - fn type_of(t: PhantomData) -> Result; -} - - - -impl IElems for Singleton -where - T: AnElem, - N: ArrayLength + Debug, -{} - -impl IElems for Or -where - T: AnElem, - N: ArrayLength + Debug, - U: IElems, -{} - - -#[derive(Clone, Debug)] -pub struct ReturnSingleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - pub singleton: Singleton, - pub returning: Return, -} - -impl IntoIterator for ReturnSingleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - type Item = Elem; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.singleton.into_iter() - } -} - -impl Elems for ReturnSingleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - type Hd = T; - type N = N; - type Tl = Singleton; - - // fn left(_s: PhantomData, x: GenericArray) -> Self { Elems::left(PhantomData::>, x) } - // fn right(_s: PhantomData, x: Self::Tl) -> Self { Elems::left(PhantomData::>, x) } - fn or) -> U, G: Fn(&Self::Tl) -> U>(&self, f: F, g: G) -> U { - self.singleton.or(f, g) - } - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result - where - Self: Sized, - { - Ok(ReturnSingleton { - singleton: Elems::pop(PhantomData::>, stack)?, - returning: Return { - return_value: Arc::new(Mutex::new(None)), - }, - }) - } - - fn elem_type(_t: PhantomData) -> Result { - Elems::elem_type(PhantomData::>) - } -} - -impl IOElems for ReturnSingleton -where - T: AnElem, - N: ArrayLength + Debug, -{ - fn or_return(&self, f: F, _g: G) -> U - where - F: Fn(&GenericArray, &Return) -> U, - G: Fn(&Self::Tl) -> U, - { - f(&self.singleton.array, &self.returning) - } - - fn returning(&self) -> Option { - self.returning.returned().map(|x| x.to_elem()) - } - - fn type_of(_t: PhantomData) -> Result { - let num_inputs = ::to_usize(); - let mut context = Context::new(); - let type_id = context.push(ElemType { - type_set: AnElem::elem_symbol(PhantomData::), - info: vec![], - }); - Ok(Type { - context: context, - i_type: (1..num_inputs).into_iter().map(|_| type_id).collect(), - o_type: vec![type_id], - }) - } -} - - -#[derive(Clone, Debug)] -pub enum ReturnOr -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - Left { - array: GenericArray, - returning: Return, - }, - Right(U), -} - -impl IntoIterator for ReturnOr -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - type Item = Elem; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - match self { - Self::Left { array, .. } => Or::::Left(array).into_iter(), - Self::Right(xs) => Or::Right(xs).into_iter(), - } - } -} - -impl Elems for ReturnOr -where - T: AnElem, - N: ArrayLength + Debug, - U: Elems, -{ - type Hd = T; - type N = N; - type Tl = U; - - fn or) -> V, G: Fn(&Self::Tl) -> V>(&self, f: F, g: G) -> V { - match self { - Self::Left { array, .. } => f(array), - Self::Right(x) => g(x), - } - } - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result - where - Self: Sized, - { - as Elems>::pop(PhantomData, stack) - .map(|x| { - match x { - Or::Left(array) => Self::Left { - array: array, - returning: Return { - return_value: Arc::new(Mutex::new(None)), - }, - }, - Or::Right(y) => Self::Right(y), - } - }) - } - - fn elem_type(_t: PhantomData) -> Result { - Elems::elem_type(PhantomData::>) - } -} - -impl IOElems for ReturnOr -where - T: AnElem, - N: ArrayLength + Debug, - U: IOElems -{ - fn or_return(&self, f: F, g: G) -> V - where - F: Fn(&GenericArray, &Return) -> V, - G: Fn(&Self::Tl) -> V, - { - match self { - Self::Left { array, returning } => { - f(array, returning) - }, - Self::Right(x) => g(x), - } - } - - fn returning(&self) -> Option { - match self { - Self::Left { returning, .. } => { - returning.returned().map(|x| x.to_elem()) - }, - Self::Right(x) => x.returning(), - } - } - - // TODO: add error info - fn type_of(_t: PhantomData) -> Result { - let mut type_tl = IOElems::type_of(PhantomData::) - .map_err(|e| ElemsPopError::ReturnOrTl(Arc::new(e)))?; - let last_type_id = type_tl.context.max_type_id() - .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; - let next_type_id = type_tl.context.push(ElemType { - type_set: AnElem::elem_symbol(PhantomData::), - info: vec![], - }); - type_tl.context.unify(last_type_id, next_type_id) - .map_err(|e| ElemsPopError::ReturnOrContextError(e))?; - Ok(type_tl) - } -} - - - - - - - -pub trait IsList: Clone + Debug + IntoIterator { - type Hd: Elems; - type Tl: IsList; - - fn empty_list() -> Option where Self: Sized; - fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self; - - fn is_empty(&self) -> bool; - fn hd(self) -> Self::Hd; - fn tl(self) -> Self::Tl; - fn cons(self, x: T) -> Cons - where - Self: Sized, - { - Cons { - hd: x, - tl: self, - } - } - - fn stack_type(t: PhantomData) -> Result; - - fn pop(_x: PhantomData, stack: &mut Stack) -> Result - where - Self: Sized, - { - match ::empty_list() { - Some(x) => Ok(x), - None => { - let original_stack = stack.clone(); - let x = ::pop(PhantomData, stack).or_else(|e| Err(ElemsPopError::IsListHd { - stack_type: IsList::stack_type(PhantomData::)?, - elem_set: Elems::elem_type(PhantomData::)?, - stack_type_of: original_stack.clone().type_of(), - error: Arc::new(e), - }))?; - let xs = ::pop(PhantomData, stack).or_else(|e| Err(ElemsPopError::IsListTl { - stack_type: IsList::stack_type(PhantomData::)?, - stack_type_of: original_stack.clone().type_of(), - error: Arc::new(e), - }))?; - Ok(::cons_list(x, xs)) - } - } - } -} - -impl IsList for Nil { - type Hd = Singleton<(), U0>; - type Tl = Nil; - - fn empty_list() -> Option where Self: Sized { - Some(Self {}) - } - - fn cons_list(_x: Self::Hd, _xs: Self::Tl) -> Self { - Self {} - } - - fn is_empty(&self) -> bool { - true - } - - fn hd(self) -> Self::Hd { - Singleton { - array: GenericArray::generate(|_| ()), - } - } - - fn tl(self) -> Self::Tl { - Self {} - } - - fn stack_type(_t: PhantomData) -> Result { - Ok(StackType { - types: vec![], - }) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Cons { - hd: T, - tl: U, -} - -pub struct IterCons { - hd: ::IntoIter, - tl: ::IntoIter, -} - -impl Debug for IterCons -where - T: Elems, - U: IsList, - ::IntoIter: Debug, - ::IntoIter: Debug, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "Cons {{\n hd: {:?},\n tl: {:?}\n}}", self.hd, self.tl) - } -} - -impl IntoIterator for Cons { - type Item = Elem; - type IntoIter = IterCons; - - fn into_iter(self) -> Self::IntoIter { - IterCons { - hd: self.hd.into_iter(), - tl: self.tl.into_iter(), - } - } -} - -impl Iterator for IterCons { - type Item = Elem; - - fn next(&mut self) -> Option { - self.hd.next().or_else(|| self.tl.next()) - } -} - -impl IsList for Cons { - type Hd = T; - type Tl = U; - - fn empty_list() -> Option where Self: Sized { - None - } - - fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self { - Cons { - hd: x, - tl: xs, - } - } - - fn is_empty(&self) -> bool { - false - } - - fn hd(self) -> Self::Hd { - self.hd - } - - fn tl(self) -> Self::Tl { - self.tl - } - - fn stack_type(_t: PhantomData) -> Result { - let elem_type_hd = Elems::elem_type(PhantomData::)?; - let elem_type_hd_count = <::N as Unsigned>::to_usize(); - let mut stack_type_tl = IsList::stack_type(PhantomData::)?; - stack_type_tl.push_n(elem_type_hd, elem_type_hd_count); - Ok(stack_type_tl) - } -} - - - - - -/// IsList and defines inputs, but no outputs. See IOList for more info -pub trait IList: IsList { -} - -impl IList for Nil { -} - -impl IList for Cons -where - T: IElems, - U: IList, -{ -} - -/// Input-output type of an instruction -pub trait IOList: IsList { - /// Returned IOElems - type Return: IOElems; - - /// Returned value, if set - fn returning(&self) -> Option; - - /// IOList's define a complete input/output Type, with exacly one return value - fn type_of(t: PhantomData) -> Result; -} - -impl IOList for Cons -where - T: IElems, - U: IOList, -{ - type Return = U::Return; - - fn returning(&self) -> Option { - self.tl.returning() - } - - // TODO: test - fn type_of(_t: PhantomData) -> Result { - let num_elem_type_hd = <::N as Unsigned>::to_usize(); - let elem_type_hd = Elems::elem_type(PhantomData::)?; - let mut type_tl = IOList::type_of(PhantomData::)?; - - type_tl.prepend_inputs(num_elem_type_hd, elem_type_hd); - Ok(type_tl) - } -} - - -#[derive(Clone, Debug)] -pub struct ConsOut -where - T: IOElems, - U: IList, -{ - cons: Cons, -} - -impl IntoIterator for ConsOut { - type Item = Elem; - type IntoIter = IterCons; - - fn into_iter(self) -> Self::IntoIter { - self.cons.into_iter() - } -} - -impl IsList for ConsOut -where - T: IOElems, - U: IList -{ - type Hd = T; - type Tl = U; - - fn empty_list() -> Option where Self: Sized { - None - } - - fn cons_list(x: Self::Hd, xs: Self::Tl) -> Self { - ConsOut { - cons: Cons { - hd: x, - tl: xs, - }, - } - } - - fn is_empty(&self) -> bool { - self.cons.is_empty() - } - - fn hd(self) -> Self::Hd { - self.cons.hd() - } - - fn tl(self) -> Self::Tl { - self.cons.tl() - } - - fn stack_type(_t: PhantomData) -> Result { - IsList::stack_type(PhantomData::>) - } -} - -impl IOList for ConsOut -where - T: IOElems, - U: IList, -{ - type Return = T; - - fn returning(&self) -> Option { - self.cons.hd.returning() - } - - // TODO: add info to errors - fn type_of(_t: PhantomData) -> Result { - // let num_elem_type_hd = <::N as Unsigned>::to_usize(); - let mut type_hd = IOElems::type_of(PhantomData::)?; - let elem_type_tl = IsList::stack_type(PhantomData::)?; - type_hd.append_inputs(elem_type_tl); - Ok(type_hd) - } -} - From 74be22fc574e28090dc32197c36729a9bab044f6 Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 24 May 2022 18:11:16 -0400 Subject: [PATCH 77/77] add demo to readme with JSON examples of code, input, queries --- .gitignore | 4 +- README.md | 62 ++++++++++++++++++++++ examples/demo_code.json | 93 ++++++++++++++++++++++++++++++++ examples/input.json | 40 ++++++++++++++ examples/local_demo_code.json | 99 +++++++++++++++++++++++++++++++++++ examples/local_query.json | 82 +++++++++++++++++++++++++++++ examples/query.json | 32 +++++++++++ src/types.rs | 29 +++------- src/types/context.rs | 19 ++----- 9 files changed, 423 insertions(+), 37 deletions(-) create mode 100644 examples/demo_code.json create mode 100644 examples/input.json create mode 100644 examples/local_demo_code.json create mode 100644 examples/local_query.json create mode 100644 examples/query.json diff --git a/.gitignore b/.gitignore index ae1bb3d..bf197f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target Cargo.lock -*.json *.profraw +/*.json +/examples/cache.json +/examples/local_cache.json diff --git a/README.md b/README.md index 69a4c36..94e9f82 100644 --- a/README.md +++ b/README.md @@ -79,3 +79,65 @@ the presenter "clears" a puzzle to get authorized. cryptoscript is extensible with modules to support all blockchain networks and off-chain data. This policy-as-code primitive can create infinite matching representations. +## Demo + +There are two demos: +- Local: this demo is self-contained to not require any API keys +- Etherscan: this demo requires a free Etherscan API key, which you can get + [here](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics) + +### Local Demo + +The local demo requires running a tiny test server, which can be started with the following command: + +```bash +cargo run --bin rest-api +``` + +Note: this API accepts PUT's of new GET "API's" for testing: each requires a +fixed `application/json` request body and returns a fixed `application/json` +response. + +To run the demo itself, run: + +```bash +cargo r --bin cryptoscript -- \ + --code examples/local_demo_code.json \ + --cache-location examples/local_cache.json \ + --input examples/input.json \ + --queries examples/local_query.json \ + --variables '{ + "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", + "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + "apikey": "DUMMY_ETHERSCAN_API_KEY" }' +``` + +You'll see `successful!` if it completes without any errors. + +### Etherscan Demo + +*NOTE: this demo currently ignores any errors from Etherscan.* + +This demo requires a free Etherscan API key, which you can get +[here](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics) + +Once you have an API key, replace `YOUR_ETHERSCAN_API_KEY` below with your API +key from Etherscan to run the demo: + +```bash +cargo r --bin cryptoscript -- \ + --code examples/demo_code.json \ + --cache-location examples/cache.json \ + --input examples/input.json \ + --queries examples/query.json \ + --variables '{ + "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", + "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + "apikey": "YOUR_ETHERSCAN_API_KEY" }' +``` + +### Troubleshooting Demo's + +If you have any issues, make sure to clear any `cache.json` files to ensure +you're receiving fresh query responses. + diff --git a/examples/demo_code.json b/examples/demo_code.json new file mode 100644 index 0000000..00f3bf3 --- /dev/null +++ b/examples/demo_code.json @@ -0,0 +1,93 @@ +{ + "instructions": [ + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + }, + { + "UnpackJson": "Object" + }, + { + "Push": { + "String": "queries" + } + }, + "Lookup", + { + "UnpackJson": "Array" + }, + { + "Push": { + "Number": 0 + } + }, + "Index", + { + "UnpackJson": "Object" + }, + { + "Restack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "action" + } + }, + "Lookup", + { + "UnpackJson": "String" + }, + { + "Push": { + "String": "tokenbalance" + } + }, + "StringEq", + "AssertTrue", + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + }, + { + "Restack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "contractaddress" + } + }, + "Lookup", + { + "UnpackJson": "String" + }, + { + "Push": { + "String": "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + } + }, + "StringEq", + "AssertTrue", + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + } + ] +} diff --git a/examples/input.json b/examples/input.json new file mode 100644 index 0000000..2406786 --- /dev/null +++ b/examples/input.json @@ -0,0 +1,40 @@ +{ + "queries": [ + { + "uri": "https://api.etherscan.io/api", + "module": "account", + "action": "tokenbalance", + "contractaddress": "0x57d90b64a1a57749b0f932f1a3395792e12e7055", + "address": "0xe04f27eb70e025b78871a2ad7eabe85e61212761", + "tag": "latest", + "blockno": "8000000", + "apikey": "YourApiKeyToken", + "response": + { + "status": "1", + "message": "OK", + "result": "135499" + } + } + ], + "prompts": [ + { + "action": "siwe", + "version": "1.1.0", + "data": { + "message": "service.org wants you to sign in with your Ethereum account:\n0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2\n\nI accept the ServiceOrg Terms of Service: https://service.org/tos\n\nURI: https://service.org/login\nVersion: 1\nChain ID: 1\nNonce: 32891757\nIssued At: 2021-09-30T16:25:24.000Z\nResources:\n- ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu\n- https://example.com/my-web2-claim.json", + "fields": { + "domain": "service.org", + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "statement": "I accept the ServiceOrg Terms of Service: https://service.org/tos", + "uri": "https://service.org/login", + "version": "1", + "chainId": 1, + "nonce": "32891757", + "issuedAt": "2021-09-30T16:25:24.000Z", + "resources": ["ipfs://Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu", "https://example.com/my-web2-claim.json"] + } + } + } + ] +} diff --git a/examples/local_demo_code.json b/examples/local_demo_code.json new file mode 100644 index 0000000..af18c86 --- /dev/null +++ b/examples/local_demo_code.json @@ -0,0 +1,99 @@ +{ + "instructions": [ + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + }, + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + }, + { + "UnpackJson": "Object" + }, + { + "Push": { + "String": "queries" + } + }, + "Lookup", + { + "UnpackJson": "Array" + }, + { + "Push": { + "Number": 0 + } + }, + "Index", + { + "UnpackJson": "Object" + }, + { + "Restack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "action" + } + }, + "Lookup", + { + "UnpackJson": "String" + }, + { + "Push": { + "String": "tokenbalance" + } + }, + "StringEq", + "AssertTrue", + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + }, + { + "Restack": { + "restack_depth": 1, + "restack_vec": [ + 0, + 0 + ] + } + }, + { + "Push": { + "String": "contractaddress" + } + }, + "Lookup", + { + "UnpackJson": "String" + }, + { + "Push": { + "String": "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + } + }, + "StringEq", + "AssertTrue", + { + "Restack": { + "restack_depth": 1, + "restack_vec": [] + } + } + ] +} diff --git a/examples/local_query.json b/examples/local_query.json new file mode 100644 index 0000000..d6b1416 --- /dev/null +++ b/examples/local_query.json @@ -0,0 +1,82 @@ +{ + "queries": [ + { + "name": "setup_erc20", + "url": "http://127.0.0.1:8080/apis/erc20", + "template": { + "Object": { + "request": { + "Object": { + "module": { + "String": "account" + }, + "action": { + "String": "tokenbalance" + }, + "contractaddress": { + "String": "0x57d90b64a1a57749b0f932f1a3395792e12e7055" + }, + "address": { + "String": "0xe04f27eb70e025b78871a2ad7eabe85e61212761" + }, + "tag": { + "String": "latest" + }, + "apikey": { + "String": "DUMMY_ETHERSCAN_API_KEY" + } + } + }, + "response": { + "Object": { + "status": { + "String": "1" + }, + "message": { + "String": "OK" + }, + "result": { + "String": "135499" + } + } + }, + "rate_limit_seconds": { + "Number": 1 + }, + "last_api_call": "Null" + } + }, + "cached": true, + "query_type": "Put" + }, + + { + "name": "erc20", + "url": "http://127.0.0.1:8080/apis/erc20", + "template": { + "Object": { + "module": { + "String": "account" + }, + "action": { + "String": "tokenbalance" + }, + "contractaddress": { + "Var": "contractaddress" + }, + "address": { + "Var": "address" + }, + "tag": { + "String": "latest" + }, + "apikey": { + "Var": "apikey" + } + } + }, + "cached": true, + "query_type": "Get" + } + ] +} diff --git a/examples/query.json b/examples/query.json new file mode 100644 index 0000000..f2ce857 --- /dev/null +++ b/examples/query.json @@ -0,0 +1,32 @@ +{ + "queries": [ + { + "name": "erc20", + "url": "https://api.etherscan.io/api", + "template": { + "Object": { + "module": { + "String": "account" + }, + "action": { + "String": "tokenbalance" + }, + "contractaddress": { + "Var": "contractaddress" + }, + "address": { + "Var": "address" + }, + "tag": { + "String": "latest" + }, + "apikey": { + "Var": "apikey" + } + } + }, + "cached": true, + "query_type": "Get" + } + ] +} diff --git a/src/types.rs b/src/types.rs index 4cd47ab..dcea3c7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -135,7 +135,6 @@ impl Type { /// Unify two Type's by producing the type of their composition. /// - /// ``` /// f : self /// g : other /// self.compose(other) : (f ++ g).type_of() @@ -146,7 +145,6 @@ impl Type { /// self.i_type /// self.o_type /// -> output - /// ``` /// /// 1. iterate through (zip(self.o_type, other.i_type)) and unify the pairs into a new context /// 2. collect the remainder and add them to the context @@ -228,14 +226,14 @@ impl Type { // ``` // // Results in: -// ``` +// // ∀ (t0 ∊ {A, B, C}), // ∀ (t1 ∊ {B, C}), // .. // ∀ (tN ∊ {D, E, F}), // [t0, t1, .., tN] -> // [ti, tj, .., tk] -// ``` +// impl Display for Type { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { // TODO: fix normalize @@ -268,13 +266,8 @@ mod type_display_tests { #[test] fn test_empty() { - let big_type_id = TypeId { - type_id: 2^32 - }; - let context = Context { - context: BTreeMap::new(), - next_type_id: big_type_id, - }; + let big_type_id = TypeId::new(2^32); + let context = Context::new().offset(big_type_id); let example_type = Type { context: context, i_type: vec![], @@ -290,18 +283,12 @@ mod type_display_tests { type_set: EnumSet::only(elem_symbol), info: vec![], }; - let mut context_map = BTreeMap::new(); - context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); - let context = Context { - context: context_map, - next_type_id: TypeId { - type_id: 1, - }, - }; + let mut context = Context::new(); + let type_id = context.push(elem_type.clone()); let example_type = Type { context: context, - i_type: vec![TypeId { type_id: 0 }, TypeId { type_id: 0 }], - o_type: vec![TypeId { type_id: 0 }], + i_type: vec![type_id, type_id], + o_type: vec![type_id], }; assert_eq!(format!("\n∀ (t0 ∊ {}),\n[t0, t0] ->\n[t0]", elem_type), format!("{}", example_type)); } diff --git a/src/types/context.rs b/src/types/context.rs index 72a6c65..e7848fb 100644 --- a/src/types/context.rs +++ b/src/types/context.rs @@ -59,13 +59,8 @@ mod context_display_tests { #[test] fn test_empty() { - let big_type_id = TypeId { - type_id: 2^32 - }; - let context = Context { - context: BTreeMap::new(), - next_type_id: big_type_id, - }; + let big_type_id = TypeId::new(2^32); + let context = Context::new().offset(big_type_id); assert_eq!("", format!("{}", context)); } @@ -76,14 +71,8 @@ mod context_display_tests { type_set: EnumSet::only(elem_symbol), info: vec![], }; - let mut context_map = BTreeMap::new(); - context_map.insert(TypeId { type_id: 0 }, elem_type.clone()); - let context = Context { - context: context_map, - next_type_id: TypeId { - type_id: 1, - }, - }; + let mut context = Context::new(); + context.push(elem_type.clone()); assert_eq!(format!("\n∀ (t0 ∊ {}),", elem_type), format!("{}", context)); } }