├── .gitignore ├── src ├── op_systems │ ├── mod.rs │ ├── simple_math.rs │ └── pokemon.rs ├── core │ ├── mod.rs │ ├── item.rs │ ├── condition_stack.rs │ ├── stack.rs │ ├── value.rs │ └── machine.rs ├── codecs │ ├── enc.rs │ ├── dec.rs │ ├── mod.rs │ └── simple │ │ ├── enc.rs │ │ ├── dec.rs │ │ └── mod.rs └── lib.rs ├── logo.png ├── .github └── workflows │ └── rust.yml ├── COPYRIGHT ├── .travis.yml ├── Cargo.toml ├── LICENSE-MIT ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /target 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /src/op_systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pokemon; 2 | pub mod simple_math; 3 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aesedepece/scriptful/HEAD/logo.png -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Run formatter 13 | run: cargo clippy --all --verbose --all-features -- -D warnings 14 | - name: Run Clippy 15 | run: cargo clippy --verbose 16 | - name: Run tests 17 | run: cargo test --all --verbose 18 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the Scriptful project are retained by their contributors. 2 | No copyright assignment is required to contribute to the Scriptful project. 3 | 4 | For full authorship information, see the version control history. 5 | 6 | Except as otherwise noted (below and/or in individual files), Rand is 7 | licensed under the Apache License, Version 2.0 or 8 | or the MIT license 9 | or , at your option. 10 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use crate::core::value::Value; 4 | 5 | pub mod condition_stack; 6 | pub mod item; 7 | pub mod machine; 8 | pub mod stack; 9 | pub mod value; 10 | 11 | /// A simple alias for referring an ordered sequence of [`Item`][Item]s of definite length. 12 | /// 13 | /// [Item]: item 14 | pub type Script = Vec>; 15 | 16 | /// Convenient type alias for `&Script`. 17 | pub type ScriptRef<'a, Op, Val = Value> = &'a [item::Item]; 18 | 19 | /// Generic error trait for all errors defined in this crate. 20 | pub trait Error: core::fmt::Debug + PartialEq { 21 | fn from_str(input: &str) -> Self; 22 | } 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | sudo: false 3 | language: rust 4 | 5 | rust: 6 | - stable 7 | 8 | # https://levans.fr/rust_travis_cache.html 9 | # Need to cache the whole `.cargo` directory to keep .crates.toml for `cargo update` to work... 10 | # ...but don't cache the cargo registry 11 | cache: 12 | directories: 13 | - /home/travis/.cargo 14 | before_cache: 15 | - rm -rf /home/travis/.cargo/registry 16 | 17 | env: 18 | global: 19 | - RUST_BACKTRACE="1" 20 | 21 | before_script: 22 | - rustup update 23 | - rustup component add rustfmt-preview 24 | - rustup component add clippy-preview 25 | 26 | script: 27 | - cargo fmt --all -- --check 28 | - cargo clippy --all --all-features -- -D warnings 29 | - cargo test --all --verbose 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scriptful" 3 | version = "0.4.0" 4 | authors = ["The Scriptful Project Developers"] 5 | license = "MIT OR Apache-2.0" 6 | readme = "README.md" 7 | documentation = "https://docs.rs/scriptful" 8 | homepage = "https://github.com/aesedepece/scriptful" 9 | repository = "https://github.com/aesedepece/scriptful" 10 | description = """ 11 | A minimalist, `no_std` stack machine library for interpreting domain specific interpreted languages. 12 | """ 13 | keywords = ["stack", "machine", "script", "dsl"] 14 | categories = ["parsing", "parser-implementations", "no-std"] 15 | edition = "2021" 16 | 17 | [badges] 18 | travis-ci = { repository = "aesedepece/scriptful" } 19 | 20 | [dependencies] 21 | serde = { version = "1", features = ["derive"], optional = true } 22 | 23 | [features] 24 | default = ["codecs"] 25 | codecs = [] 26 | use_serde = ["serde"] 27 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2020 Developers of the Scriptful project 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/codecs/enc.rs: -------------------------------------------------------------------------------- 1 | //! Traits for encoding of scripts, operators and values. 2 | //! 3 | //! These are modeled after `serde`'s `Serializer` and `Serialize` traits. 4 | //! 5 | //! - `Encoder` shall be implemented by different encoding and decoding mechanisms (aka **codecs**). 6 | //! - `Encode` shall be implemented for every data structure that can be encoded using any codec. 7 | 8 | use alloc::vec::Vec; 9 | use core::marker::Sized; 10 | 11 | use crate::prelude::*; 12 | 13 | pub trait Encode { 14 | fn encode(&self, encoder: E) -> E::Ok 15 | where 16 | E: Encoder; 17 | } 18 | 19 | pub trait EncodeSequence { 20 | type Ok; 21 | 22 | fn encode_element(&mut self, value: &T) 23 | where 24 | T: Encode; 25 | 26 | fn end(self) -> Self::Ok; 27 | } 28 | 29 | pub trait Encoder: Sized { 30 | type Ok; 31 | type EncodeSequence: EncodeSequence; 32 | 33 | fn to_vec(input: &Script) -> Vec 34 | where 35 | Op: core::fmt::Debug + Encode, 36 | Val: core::fmt::Debug + Encode; 37 | 38 | fn write_u8(self, input: u8) -> Self::Ok; 39 | fn write_bytes(self, input: &[u8]) -> Self::Ok; 40 | 41 | fn encode_item(self, input: &Item) -> Self::Ok 42 | where 43 | Op: core::fmt::Debug + Encode, 44 | Val: core::fmt::Debug + Encode; 45 | fn encode_seq(self) -> Self::EncodeSequence; 46 | } 47 | -------------------------------------------------------------------------------- /src/codecs/dec.rs: -------------------------------------------------------------------------------- 1 | //! Traits for decoding of scripts, operators and values. 2 | //! 3 | //! These are modeled after `serde`'s `Deserializer` and `Deserialize` traits. 4 | //! 5 | //! - `Decoder` shall be implemented by different encoding and decoding mechanisms (aka **codecs**). 6 | //! - `Decode` shall be implemented for every data structure that can be decoded using any codec. 7 | 8 | use alloc::string::String; 9 | use alloc::vec::Vec; 10 | 11 | use crate::prelude::*; 12 | 13 | pub trait Decoder: Sized { 14 | type Error: Error; 15 | 16 | fn decode_i128(&mut self) -> Result; 17 | 18 | fn decode_f64(&mut self) -> Result; 19 | 20 | fn decode_string(&mut self) -> Result; 21 | 22 | fn decode_item(&mut self) -> Result, Self::Error> 23 | where 24 | Op: core::fmt::Debug + Decode, 25 | Val: core::fmt::Debug + Decode; 26 | 27 | fn decode_script(&mut self) -> Result, Self::Error> 28 | where 29 | Op: core::fmt::Debug + Decode, 30 | Val: core::fmt::Debug + Decode; 31 | 32 | fn from_vec(input: Vec) -> Result, Self::Error> 33 | where 34 | Op: core::fmt::Debug + Decode, 35 | Val: core::fmt::Debug + Decode; 36 | 37 | fn peek_byte(&self) -> Result<&u8, Self::Error>; 38 | 39 | fn read_byte(&mut self) -> Result; 40 | 41 | fn read_bytes(&mut self, length: usize) -> Result<&[u8], Self::Error>; 42 | } 43 | 44 | pub trait Decode: Sized { 45 | fn decode(decoder: &mut D) -> Result 46 | where 47 | D: Decoder; 48 | } 49 | -------------------------------------------------------------------------------- /src/codecs/mod.rs: -------------------------------------------------------------------------------- 1 | //! Codecs are different ways to encode and decode scripts, normally into and from compact binary 2 | //! formats. 3 | //! 4 | //! The traits and implementations found in this module are really similar to those used by `serde`. 5 | //! However, these are intentionally not compatible, to avoid all the `std` overhead that `serde` 6 | //! brings with itself. 7 | 8 | use alloc::string::String; 9 | 10 | use crate::prelude::*; 11 | 12 | pub mod dec; 13 | pub mod enc; 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use alloc::vec::Vec; 18 | 19 | use crate::codecs::codecs::simple; 20 | use crate::codecs::dec::Decoder; 21 | use crate::codecs::enc::Encoder; 22 | use crate::core::item::Item::*; 23 | use crate::core::value::Value::*; 24 | use crate::op_systems::simple_math::MathOperator::{self, *}; 25 | use crate::prelude::*; 26 | 27 | fn example_bytes() -> Vec { 28 | Vec::from([ 29 | 3, 1, 6, 255, 224, 245, 5, 128, 2, 31, 133, 235, 81, 184, 30, 9, 64, 130, 20, 13, 72, 30 | 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 19, 31 | ]) 32 | } 33 | 34 | fn example_script() -> Script { 35 | Vec::from([ 36 | Value(Integer(1)), 37 | Value(Integer(99999999)), 38 | Operator(Add), 39 | Value(Float(3.14)), 40 | Operator(Mul), 41 | Value(String("Hello, World!".into())), 42 | Value(String("".into())), 43 | ]) 44 | } 45 | 46 | #[test] 47 | fn test_encoding() { 48 | let decoded: Script = example_script(); 49 | 50 | let encoded = <&mut simple::SimpleScriptCodec>::to_vec(&decoded); 51 | let expected = example_bytes(); 52 | 53 | assert_eq!(encoded, expected); 54 | } 55 | 56 | #[test] 57 | fn test_decoding() { 58 | let encoded = example_bytes(); 59 | 60 | let decoded = <&mut simple::SimpleScriptCodec>::from_vec(encoded).unwrap(); 61 | let expected = example_script(); 62 | 63 | assert_eq!(decoded, expected); 64 | } 65 | } 66 | 67 | #[derive(Debug, PartialEq)] 68 | pub struct EncodingError(String); 69 | 70 | #[derive(Debug, PartialEq)] 71 | pub struct DecodingError(String); 72 | 73 | impl Error for EncodingError { 74 | fn from_str(input: &str) -> Self { 75 | EncodingError(input.into()) 76 | } 77 | } 78 | 79 | impl Error for DecodingError { 80 | fn from_str(input: &str) -> Self { 81 | DecodingError(input.into()) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/op_systems/simple_math.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | /// Frequently used mathematical operators. 4 | #[derive(Debug, PartialEq, Eq)] 5 | #[cfg_attr(feature = "use_serde", derive(serde::Deserialize, serde::Serialize))] 6 | pub enum MathOperator { 7 | /// Addition of two numbers (`a + b`). 8 | Add, 9 | /// Equivalence of two numbers (`a == b`). 10 | Equal, 11 | /// Multiplication of two numbers (`a * b`) 12 | Mul, 13 | /// Negation of one number (`-a`). 14 | Not, 15 | /// Subtraction of two numbers (`a - b`). 16 | Sub, 17 | } 18 | 19 | /// A simple operator system that decides how each of the variants of [`MathOperator`][MathOperator] 20 | /// trigger push and pulls on the [`Stack`][Stack] inside a [`Machine`][Machine]. 21 | /// 22 | /// [MathOperator]: enum.MathOperator.html 23 | /// [Stack]: ../../core/stack/struct.Stack.html 24 | /// [Machine]: ../../core/machine/struct.Machine.html 25 | pub fn simple_math_op_sys(stack: &mut Stack, operator: &MathOperator) { 26 | use crate::core::value::Value::*; 27 | 28 | match operator { 29 | MathOperator::Add => { 30 | let a = stack.pop().unwrap(); 31 | let b = stack.pop().unwrap(); 32 | stack.push(a + b); 33 | } 34 | MathOperator::Equal => { 35 | let a = stack.pop().unwrap(); 36 | let b = stack.pop().unwrap(); 37 | stack.push(Boolean(a == b)); 38 | } 39 | MathOperator::Mul => { 40 | let a = stack.pop().unwrap(); 41 | let b = stack.pop().unwrap(); 42 | stack.push(a * b); 43 | } 44 | MathOperator::Not => { 45 | let x = stack.pop().unwrap(); 46 | stack.push(!x); 47 | } 48 | MathOperator::Sub => { 49 | let a = stack.pop().unwrap(); 50 | let b = stack.pop().unwrap(); 51 | stack.push(a - b); 52 | } 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use crate::core::value::Value::*; 59 | use crate::op_systems::simple_math::{simple_math_op_sys, MathOperator}; 60 | use crate::prelude::Item::*; 61 | use crate::prelude::*; 62 | use alloc::vec::Vec; 63 | 64 | #[test] 65 | fn test_one_plus_one_equals_two() { 66 | let machine = &mut Machine::new(&simple_math_op_sys); 67 | 68 | let result = machine 69 | .run_script(&Vec::from([ 70 | Value(Integer(1)), 71 | Value(Integer(1)), 72 | Operator(MathOperator::Add), 73 | Value(Integer(2)), 74 | Operator(MathOperator::Equal), 75 | ])) 76 | .unwrap(); 77 | 78 | assert_eq!(result, &Boolean(true)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/core/item.rs: -------------------------------------------------------------------------------- 1 | use crate::core::value::Value; 2 | 3 | /// An `Item` is each one of the entities that conform a [`Script`][Script]. 4 | /// 5 | /// In other words, a [`Script`][Script] is no more than an array of `Item`s in the likes of 6 | /// `[Item]`. 7 | /// 8 | /// `Ìtem` does not implement any methods other than implementations of some traits from the 9 | /// [`core`][core] crate. 10 | /// 11 | /// [Script]: ../type.Script.html 12 | /// [core]: https://doc.rust-lang.org/nightly/core/ 13 | #[derive(Clone, Debug, PartialEq)] 14 | #[cfg_attr(feature = "use_serde", derive(serde::Deserialize, serde::Serialize))] 15 | pub enum Item 16 | where 17 | Op: core::fmt::Debug, 18 | Val: core::fmt::Debug, 19 | { 20 | /// An operator code, either a variant of an user-defined [`enum`][enum] containing different 21 | /// operator identifiers, or any of the ones found in the [`op_systems`][op_systems] module. 22 | /// 23 | /// [enum]: https://doc.rust-lang.org/std/keyword.enum.html 24 | /// [op_systems]: ../../op_systems/ 25 | Operator(Op), 26 | /// A value, either a variant of an user-defined [`enum`][enum] representing a type system, or 27 | /// an instance of any of the variants of [`Value`][Value], i.e. [`Boolean`][Boolean], 28 | /// [`Float`][Float], [`Integer`][Integer] or [`String`][String]. 29 | /// 30 | /// [enum]: https://doc.rust-lang.org/std/keyword.enum.html 31 | /// [Value]: ../value/enum.Value.html 32 | /// [Boolean]: ../value/enum.Value.html#variant.Boolean 33 | /// [Float]: ../value/enum.Value.html#variant.Float 34 | /// [Integer]: ../value/enum.Value.html#variant.Integer 35 | /// [String]: ../value/enum.Value.html#variant.String 36 | Value(Val), 37 | } 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | use crate::core::item::Item::{self, *}; 42 | use crate::core::value::Value::*; 43 | 44 | #[test] 45 | fn test_eq() { 46 | let op_item1: Item = Operator(u8::default()); 47 | let op_item2: Item = Operator(u8::default()); 48 | 49 | assert_eq!(op_item1, op_item2); 50 | 51 | let val_item1: Item = Value(Integer(i128::default())); 52 | let val_item2: Item = Value(Integer(i128::default())); 53 | 54 | assert_eq!(val_item1, val_item2); 55 | } 56 | 57 | #[test] 58 | fn test_ne() { 59 | let op_item1: Item = Operator(u8::default()); 60 | let op_item2: Item = Operator(u8::default() + 1); 61 | 62 | assert_ne!(op_item1, op_item2); 63 | 64 | let val_item1: Item = Value(Integer(i128::default())); 65 | let val_item2: Item = Value(Integer(i128::default() + 1)); 66 | 67 | assert_ne!(val_item1, val_item2); 68 | 69 | assert_ne!(op_item1, val_item1); 70 | assert_ne!(op_item1, val_item2); 71 | assert_ne!(op_item2, val_item1); 72 | assert_ne!(op_item2, val_item2); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/core/condition_stack.rs: -------------------------------------------------------------------------------- 1 | // ConditionStack implementation from bitcoin-core 2 | // https://github.com/bitcoin/bitcoin/blob/505ba3966562b10d6dd4162f3216a120c73a4edb/src/script/interpreter.cpp#L272 3 | // https://bitslog.com/2017/04/17/new-quadratic-delays-in-bitcoin-scripts/ 4 | /** A data type to abstract out the condition stack during script execution. 5 | * 6 | * Conceptually it acts like a vector of booleans, one for each level of nested 7 | * IF/THEN/ELSE, indicating whether we're in the active or inactive branch of 8 | * each. 9 | * 10 | * The elements on the stack cannot be observed individually; we only need to 11 | * expose whether the stack is empty and whether or not any false values are 12 | * present at all. To implement OP_ELSE, a toggle_top modifier is added, which 13 | * flips the last value without returning it. 14 | * 15 | * This uses an optimized implementation that does not materialize the 16 | * actual stack. Instead, it just stores the size of the would-be stack, 17 | * and the position of the first false value in it. 18 | */ 19 | #[derive(Debug)] 20 | pub struct ConditionStack { 21 | stack_size: u32, 22 | first_false_pos: u32, 23 | } 24 | 25 | impl Default for ConditionStack { 26 | fn default() -> Self { 27 | Self { 28 | stack_size: 0, 29 | first_false_pos: Self::NO_FALSE, 30 | } 31 | } 32 | } 33 | 34 | impl ConditionStack { 35 | const NO_FALSE: u32 = u32::MAX; 36 | 37 | pub fn is_empty(&self) -> bool { 38 | self.stack_size == 0 39 | } 40 | 41 | pub fn all_true(&self) -> bool { 42 | self.first_false_pos == Self::NO_FALSE 43 | } 44 | 45 | pub fn push_back(&mut self, b: bool) { 46 | if (self.first_false_pos == Self::NO_FALSE) && !b { 47 | // The stack consists of all true values, and a false is added. 48 | // The first false value will appear at the current size. 49 | self.first_false_pos = self.stack_size; 50 | } 51 | 52 | self.stack_size += 1; 53 | } 54 | 55 | pub fn pop_back(&mut self) -> Option<()> { 56 | if self.stack_size == 0 { 57 | return None; 58 | } 59 | 60 | self.stack_size -= 1; 61 | if self.first_false_pos == self.stack_size { 62 | // When popping off the first false value, everything becomes true. 63 | self.first_false_pos = Self::NO_FALSE; 64 | } 65 | 66 | Some(()) 67 | } 68 | 69 | pub fn toggle_top(&mut self) -> Option<()> { 70 | if self.stack_size == 0 { 71 | return None; 72 | } 73 | 74 | if self.first_false_pos == Self::NO_FALSE { 75 | // The current stack is all true values; the first false will be the top. 76 | self.first_false_pos = self.stack_size - 1; 77 | } else if self.first_false_pos == self.stack_size - 1 { 78 | // The top is the first false value; toggling it will make everything true. 79 | self.first_false_pos = Self::NO_FALSE; 80 | } else { 81 | // There is a false value, but not on top. No action is needed as toggling 82 | // anything but the first false value is unobservable. 83 | } 84 | 85 | Some(()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/codecs/simple/enc.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use crate::{ 4 | core::value::Value, 5 | encoding::{ 6 | codecs::simple::{significant_bytes_count, SimpleScriptCodec}, 7 | enc::{Encode, EncodeSequence, Encoder}, 8 | }, 9 | op_systems::simple_math::MathOperator, 10 | prelude::*, 11 | }; 12 | 13 | impl<'a> Encoder for &'a mut SimpleScriptCodec { 14 | type Ok = (); 15 | type EncodeSequence = Self; 16 | 17 | fn to_vec(input: &Script) -> Vec 18 | where 19 | Op: core::fmt::Debug + Encode, 20 | Val: core::fmt::Debug + Encode, 21 | { 22 | let mut codec = SimpleScriptCodec::default(); 23 | input.encode(&mut codec); 24 | 25 | codec.data() 26 | } 27 | 28 | fn write_u8(self, input: u8) -> Self::Ok { 29 | self.data_push(input); 30 | } 31 | 32 | fn write_bytes(self, input: &[u8]) -> Self::Ok { 33 | for byte in input { 34 | self.write_u8(*byte); 35 | } 36 | } 37 | 38 | fn encode_item(self, input: &Item) -> Self::Ok 39 | where 40 | Op: core::fmt::Debug + Encode, 41 | Val: core::fmt::Debug + Encode, 42 | { 43 | input.encode(self); 44 | } 45 | 46 | fn encode_seq(self) -> Self::EncodeSequence { 47 | self 48 | } 49 | } 50 | 51 | impl<'a> EncodeSequence for &'a mut SimpleScriptCodec { 52 | type Ok = (); 53 | 54 | fn encode_element(&mut self, value: &T) 55 | where 56 | T: Encode, 57 | { 58 | value.encode(&mut **self) 59 | } 60 | 61 | fn end(self) -> Self::Ok { 62 | () 63 | } 64 | } 65 | 66 | impl Encode for Script 67 | where 68 | Op: core::fmt::Debug + Encode, 69 | Val: core::fmt::Debug + Encode, 70 | { 71 | fn encode(&self, encoder: E) -> ::Ok 72 | where 73 | E: Encoder, 74 | { 75 | let mut seq = encoder.encode_seq(); 76 | for item in self { 77 | seq.encode_element(item); 78 | } 79 | seq.end() 80 | } 81 | } 82 | 83 | impl Encode for Item 84 | where 85 | Op: core::fmt::Debug + Encode, 86 | Val: core::fmt::Debug + Encode, 87 | { 88 | fn encode(&self, encoder: E) -> ::Ok 89 | where 90 | E: Encoder, 91 | { 92 | match self { 93 | Item::Operator(op) => op.encode(encoder), 94 | Item::Value(val) => val.encode(encoder), 95 | } 96 | } 97 | } 98 | 99 | impl Encode for crate::op_systems::simple_math::MathOperator { 100 | fn encode(&self, encoder: E) -> ::Ok 101 | where 102 | E: Encoder, 103 | { 104 | let discriminant = match self { 105 | MathOperator::Add => 0x00, 106 | MathOperator::Equal => 0x01, 107 | MathOperator::Mul => 0x02, 108 | MathOperator::Not => 0x03, 109 | MathOperator::Sub => 0x04, 110 | }; 111 | 112 | encoder.write_u8(discriminant + 0x80) 113 | } 114 | } 115 | 116 | impl Encode for Value { 117 | fn encode(&self, encoder: E) -> ::Ok 118 | where 119 | E: Encoder, 120 | { 121 | let bytes: Vec = match self { 122 | Value::Boolean(val) => match val { 123 | false => Vec::from([0x00]), 124 | true => Vec::from([0x01]), 125 | }, 126 | Value::Float(val) => { 127 | let num_bytes = val.to_le_bytes(); 128 | let first_byte = 0x02; 129 | [&[first_byte], &num_bytes[..]].concat() 130 | } 131 | Value::Integer(val) => { 132 | let num_bytes = val.to_le_bytes(); 133 | let significant_bytes_count = significant_bytes_count(*val); 134 | let first_byte = 0x03 + significant_bytes_count as u8; 135 | 136 | [&[first_byte], &num_bytes[..significant_bytes_count + 1]].concat() 137 | } 138 | Value::String(val) => { 139 | if val.len() == 0 { 140 | Vec::from([0x13]) 141 | } else { 142 | let str_bytes = val.as_bytes(); 143 | let str_bytes_len_as_bytes = str_bytes.len().to_le_bytes(); 144 | let str_bytes_len_sbc = 1 + significant_bytes_count(str_bytes.len() as i128); 145 | let first_byte = 0x13 + str_bytes_len_sbc as u8; 146 | 147 | [ 148 | &[first_byte], 149 | &str_bytes_len_as_bytes[..str_bytes_len_sbc], 150 | &str_bytes[..], 151 | ] 152 | .concat() 153 | } 154 | } 155 | }; 156 | 157 | encoder.write_bytes(&bytes) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Scriptful](logo.png) 2 | 3 | [![Build Status](https://travis-ci.com/aesedepece/scriptful.svg?branch=master)](https://travis-ci.com/aesedepece/scriptful) 4 | [![Crate](https://img.shields.io/crates/v/scriptful.svg)](https://crates.io/crates/scriptful) 5 | [![Docs](https://docs.rs/scriptful/badge.svg)](https://docs.rs/scriptful) 6 | ![License](https://img.shields.io/crates/l/scriptful.svg) 7 | 8 | ___Scriptful_ is a minimalist `no_std`, zero dependency stack machine for interpreting scripts written with domain 9 | specific interpreted languages.__ 10 | 11 | This library is heavily inspired by the [Forth] programming language and [Script][BitcoinScript] (the scripting language in Bitcoin). 12 | 13 | ## General design 14 | 15 | The whole library is built around these concepts: 16 | 17 | - __[Stack]__: an ordered sequence of values that can be operated in a [LIFO]-alike way. 18 | - __[Item]__: either a `Value` (a piece of data to be pushed into the stack) or an `Operator` (the descriptor for an action that operates on the topmost items in the stack). 19 | - __Type system__: an [`enum`][enum] whose variants are all the possible data types allowed in a [`Stack`][Stack]. 20 | - __[Operator system]__: a function that decides how each operator will mutate a given stack. 21 | - __[Script]__: an ordered sequence of items (values and operators) that can be passed to an operator system for operating on a given stack. 22 | - __[Machine]__: a convenient wrapper around a stack that enables multiple modes of operation. 23 | - __[Codec]__: a set of methods for encoding and decoding scripts and items, normally into and from binary formats. 24 | 25 | Using this library is as easy as: 26 | 27 | 1. Defining your own set of operators, or using any of the ones that come bundled in the [`op_systems`][Operator system] module. 28 | 2. Defining your own type system, or using the [`Value`][Value] type system that comes bundled in the [`core::value`][Value] module. 29 | 3. Defining your own [operator system][Operator system] function, or using any of the ones that come bundled in the [`op_systems`][Operator system] module. 30 | 4. Instantiating a [machine][Machine] with a reference to your operator system. 31 | 5. Composing a [script][Script] and running it in the machine. 32 | 33 | ## Quick example 34 | 35 | ```rust 36 | use scriptful::prelude::*; 37 | use scriptful::core::value::Value::*; 38 | 39 | // You can define your own operators. 40 | #[derive(Debug, PartialEq, Eq)] 41 | enum MyOperator { 42 | Add, 43 | Equal, 44 | Sub, 45 | } 46 | 47 | // An operator system decides what to do with the stack when each operator is applied on it. 48 | fn my_operator_system(stack: &mut Stack, operator: &MyOperator) { 49 | match operator { 50 | MyOperator::Add => { 51 | let a = stack.pop(); 52 | let b = stack.pop(); 53 | stack.push(a + b); 54 | } 55 | MyOperator::Equal => { 56 | let a = stack.pop(); 57 | let b = stack.pop(); 58 | stack.push(Boolean(a == b)); 59 | } 60 | MyOperator::Sub => { 61 | let a = stack.pop(); 62 | let b = stack.pop(); 63 | stack.push(a - b); 64 | } 65 | } 66 | } 67 | 68 | // Instantiate the machine with a reference to your operator system. 69 | let mut machine = Machine::new(&my_operator_system); 70 | 71 | // Run a script that simply adds 1 and 2. 72 | let result = machine.run_script(&[ 73 | Item::Value(Integer(1)), 74 | Item::Value(Integer(2)), 75 | Item::Operator(MyOperator::Add), 76 | ]); 77 | 78 | // The result should unsurprisingly be 3. 79 | assert_eq!(result, Some(&Integer(3))); 80 | ``` 81 | 82 | ## Known limitations 83 | 84 | - _Beware of unwraps!_ This is a proof-of-concept and it is modelled to panic upon errors. 85 | Making the library safe for production usage is in the near horizon though. 86 | 87 | ## License 88 | 89 | Scriptful is distributed under the terms of both the MIT license and the Apache License (Version 2.0). 90 | 91 | See [LICENSE-APACHE] and [LICENSE-MIT], and [COPYRIGHT] for details. 92 | 93 | [Forth]: https://en.wikipedia.org/wiki/Forth_(programming_language) 94 | [BitcoinScript]: https://en.bitcoin.it/wiki/Script 95 | [LIFO]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 96 | [Stack]: https://docs.rs/scriptful/latest/scriptful/core/stack/struct.Stack.html 97 | [Item]: https://docs.rs/scriptful/latest/scriptful/core/item/enum.Item.html 98 | [Operator system]: https://docs.rs/scriptful/latest/scriptful/op_systems/ 99 | [Script]: https://docs.rs/scriptful/latest/scriptful/core/type.Script.html 100 | [Machine]: https://docs.rs/scriptful/latest/scriptful/core/machine/struct.Machine.html 101 | [smallvec]: https://crates.io/crates/smallvec 102 | [Value]: core/value/enum.Value.html 103 | [enum]: https://doc.rust-lang.org/std/keyword.enum.html 104 | [LICENSE-APACHE]: LICENSE-APACHE 105 | [LICENSE-MIT]: LICENSE-MIT 106 | [COPYRIGHT]: COPYRIGHT 107 | -------------------------------------------------------------------------------- /src/core/stack.rs: -------------------------------------------------------------------------------- 1 | //! An ordered sequence of values that can be operated in a [LIFO]-alike way. 2 | //! 3 | //! This module provides the [`Stack`][Stack] struct which in turn is the core of the [`Machine`][Machine] abstraction. 4 | //! 5 | //! For more details on [`Stack`][Stack], how it works and which methods does it provide, please go to the [`struct Stack` documentation][Stack]. 6 | //! 7 | //! [LIFO]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 8 | //! [Stack]: core/stack/struct.Stack.html 9 | //! [Script]: core/type.Script.html 10 | //! [Machine]: core/machine/ 11 | 12 | use alloc::vec::Vec; 13 | 14 | use crate::core::value::Value; 15 | 16 | /// An ordered sequence of values that can be operated in a [LIFO]-alike way. 17 | /// 18 | /// Every `Stack` actually comprises two sequences of values: the `main` sub-stack and the `alt` sub-stack. 19 | /// 20 | /// As its name indicates, the `main` sub-stack is the one you operate by default. 21 | /// That is, the `alt` sub-stack cannot be operated directly — you can only move values between both sub-stacks with the [`pop_into_alt`][pop_into_alt] and [`push_from_alt`][push_from_alt] methods. 22 | /// The `alt` sub-stack is therefore limited for usage as a sort of _clipboard_ for values. 23 | /// 24 | /// [LIFO]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 25 | /// [pop_into_alt]: #method.pop_into_alt 26 | /// [push_from_alt]: #method.push_from_alt 27 | #[derive(Debug)] 28 | pub struct Stack 29 | where 30 | Val: core::fmt::Debug, 31 | { 32 | main: Vec, 33 | alt: Vec, 34 | } 35 | 36 | impl Stack 37 | where 38 | Val: core::fmt::Debug, 39 | { 40 | /// Returns the number of values in the `main` sub-stack, also referred to as its 'length'. 41 | /// 42 | /// # Examples 43 | /// 44 | /// ```rust 45 | /// use scriptful::prelude::*; 46 | /// use scriptful::core::value::Value::*; 47 | /// 48 | /// let mut stack = Stack::default(); 49 | /// assert_eq!(stack.length(), 0); 50 | /// 51 | /// stack.push(Integer(i128::default())); 52 | /// assert_eq!(stack.length(), 1); 53 | /// 54 | /// stack.pop(); 55 | /// assert_eq!(stack.length(), 0); 56 | /// ``` 57 | pub fn length(&self) -> usize { 58 | self.main.len() 59 | } 60 | 61 | /// Removes the topmost value in the `main` sub-stack and returns it. 62 | /// 63 | /// # Examples 64 | /// 65 | /// ```rust 66 | /// use scriptful::prelude::*; 67 | /// use scriptful::core::value::Value::*; 68 | /// 69 | /// let value = Integer(i128::default()); 70 | /// let mut stack = Stack::default(); 71 | /// stack.push(value.clone()); 72 | /// let popped = stack.pop(); 73 | /// 74 | /// assert_eq!(value, popped); 75 | /// ``` 76 | pub fn pop(&mut self) -> Option { 77 | self.main.pop() 78 | } 79 | 80 | /// Similar to [`pop`][pop], but instead of returning the popped value, it pushes it to the `alt` sub-stack. 81 | /// 82 | /// # Panics 83 | /// Panics if there are no values left in the `main` stack. 84 | /// 85 | /// [pop]: #method.pop 86 | pub fn pop_into_alt(&mut self) { 87 | self.alt.push(self.main.pop().unwrap()) 88 | } 89 | 90 | /// Puts a value on top of the stack. 91 | /// 92 | /// # Examples 93 | /// 94 | /// ```rust 95 | /// use scriptful::prelude::*; 96 | /// use scriptful::core::value::Value::*; 97 | /// 98 | /// let value = Integer(i128::default()); 99 | /// let mut stack = Stack::default(); 100 | /// stack.push(value.clone()); 101 | /// let topmost = stack.topmost(); 102 | /// 103 | /// assert_eq!(topmost, Some(&value)); 104 | /// ``` 105 | pub fn push(&mut self, item: Val) { 106 | self.main.push(item) 107 | } 108 | 109 | /// Similar to [`push`][push], but instead of receiving the value to be pushed as an argument, it pops it from the `alt` sub-stack. 110 | /// 111 | /// [push]: #method.push 112 | pub fn push_from_alt(&mut self) { 113 | self.main.push(self.alt.pop().unwrap()) 114 | } 115 | 116 | /// Returns a reference to the last value in the `main` sub-stack. 117 | /// 118 | /// # Examples 119 | /// 120 | /// ```rust 121 | /// use scriptful::prelude::*; 122 | /// use scriptful::core::value::Value::*; 123 | /// 124 | /// let value = Integer(i128::default()); 125 | /// let mut stack = Stack::default(); 126 | /// stack.push(value.clone()); 127 | /// let topmost = stack.topmost(); 128 | /// 129 | /// assert_eq!(topmost, Some(&value)); 130 | /// ``` 131 | pub fn topmost(&self) -> Option<&Val> { 132 | self.main.last() 133 | } 134 | } 135 | 136 | impl core::default::Default for Stack 137 | where 138 | Val: core::fmt::Debug, 139 | { 140 | fn default() -> Self { 141 | Self { 142 | main: Default::default(), 143 | alt: Default::default(), 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! ___Scriptful_ is a minimalist `no_std`, zero dependency stack machine for interpreting scripts written with domain 2 | //! specific interpreted languages.__ 3 | //! 4 | //! This library is heavily inspired by the [Forth] programming language and [Script][BitcoinScript] 5 | //! (the scripting language in Bitcoin). 6 | //! 7 | //! # General design 8 | //! 9 | //! The whole library is built around these concepts: 10 | //! 11 | //! - __[Stack]__: an ordered sequence of values that can be operated in a [LIFO]-alike way. 12 | //! - __[Item]__: either a `Value` (a piece of data to be pushed into the stack) or an `Operator` (the descriptor for an action that operates on the topmost items in the stack). 13 | //! - __Type system__: an [`enum`][enum] whose variants are all the possible data types allowed in a [`Stack`][Stack]. 14 | //! - __[Operator system]__: a function that decides how each operator will mutate a given stack. 15 | //! - __[Script]__: an ordered sequence of items (values and operators) that can be passed to an operator system for operating on a given stack. 16 | //! - __[Machine]__: a convenient wrapper around a stack that enables multiple modes of operation. 17 | //! - __[Codec]__: a set of methods for encoding and decoding scripts and items, normally into and from binary formats. 18 | //! 19 | //! Using this library is as easy as: 20 | //! 21 | //! 1. Defining your own set of operators, or using any of the ones that come bundled in the [`op_systems`][Operator system] module. 22 | //! 2. Defining your own type system, or using the [`Value`][Value] type system that comes bundled in the [`core::value`][Value] module. 23 | //! 3. Defining your own [operator system][Operator system] function, or using any of the ones that come bundled in the [`op_systems`][Operator system] module. 24 | //! 4. Instantiating a [machine][Machine] with a reference to your operator system. 25 | //! 5. Composing a [script][Script] and running it in the machine. 26 | //! 27 | //! # Quick example 28 | //! 29 | //! ```rust 30 | //! use scriptful::prelude::*; 31 | //! use scriptful::core::value::Value::*; 32 | //! 33 | //! // You can define your own operators. 34 | //! #[derive(Debug, PartialEq, Eq)] 35 | //! enum MyOperator { 36 | //! Add, 37 | //! Equal, 38 | //! Sub, 39 | //! } 40 | //! 41 | //! // An operator system decides what to do with the stack when each operator is applied on it. 42 | //! fn my_operator_system(stack: &mut Stack, operator: &MyOperator) { 43 | //! match operator { 44 | //! MyOperator::Add => { 45 | //! let a = stack.pop(); 46 | //! let b = stack.pop(); 47 | //! stack.push(a + b); 48 | //! } 49 | //! MyOperator::Equal => { 50 | //! let a = stack.pop(); 51 | //! let b = stack.pop(); 52 | //! stack.push(Boolean(a == b)); 53 | //! } 54 | //! MyOperator::Sub => { 55 | //! let a = stack.pop(); 56 | //! let b = stack.pop(); 57 | //! stack.push(a - b); 58 | //! } 59 | //! } 60 | //! } 61 | //! 62 | //! // Instantiate the machine with a reference to your operator system. 63 | //! let mut machine = Machine::new(&my_operator_system); 64 | //! 65 | //! // Run a script that simply adds 1 and 2. 66 | //! let result = machine.run_script(&Vec::from([ 67 | //! Item::Value(Integer(1)), 68 | //! Item::Value(Integer(2)), 69 | //! Item::Operator(MyOperator::Add), 70 | //! ])); 71 | //! 72 | //! // The result should unsurprisingly be 3. 73 | //! assert_eq!(result, Some(&Integer(3))); 74 | //! ``` 75 | //! 76 | //! # Known limitations 77 | //! 78 | //! - _Beware of unwraps!_ This is a proof-of-concept and it is modelled to panic upon errors. 79 | //! Making the library safe for production usage is in the near horizon though. 80 | //! 81 | //! # License 82 | //! 83 | //! Scriptful is distributed under the terms of both the MIT license and the Apache License (Version 2.0). 84 | //! 85 | //! See [LICENSE-APACHE] and [LICENSE-MIT], and [COPYRIGHT] for details. 86 | //! 87 | //! [Forth]: https://en.wikipedia.org/wiki/Forth_(programming_language) 88 | //! [BitcoinScript]: https://en.bitcoin.it/wiki/Script 89 | //! [LIFO]: https://en.wikipedia.org/wiki/Stack_(abstract_data_type) 90 | //! [Stack]: core/stack/struct.Stack.html 91 | //! [Item]: core/item/enum.Item.html 92 | //! [Operator system]: op_systems/ 93 | //! [Script]: core/type.Script.html 94 | //! [Machine]: core/machine/struct.Machine.html 95 | //! [Value]: core/value/enum.Value.html 96 | //! [Codec]: codecs/index.html 97 | //! [enum]: https://doc.rust-lang.org/std/keyword.enum.html 98 | //! [LICENSE-APACHE]: https://github.com/aesedepece/scriptful/blob/master/LICENSE-APACHE 99 | //! [LICENSE-MIT]: https://github.com/aesedepece/scriptful/blob/master/LICENSE-MIT 100 | //! [COPYRIGHT]: https://github.com/aesedepece/scriptful/blob/master/COPYRIGHT 101 | 102 | #![no_std] 103 | #![doc(html_playground_url = "https://play.rust-lang.org/")] 104 | 105 | extern crate alloc; 106 | 107 | #[cfg(feature = "codecs")] 108 | pub mod codecs; 109 | /// The core of this library. 110 | /// 111 | /// Provides all the [`Item`][Item], [`Stack`][Stack], [`Machine`][Machine] and [`Value`][Value] goodness. 112 | /// 113 | /// [Item]: item/ 114 | /// [Stack]: stack/ 115 | /// [Machine]: machine/ 116 | /// [Value]: value/ 117 | pub mod core; 118 | /// Some ready-to-use operator systems that may be useful for _someone_, _somewhere_, _somewhen_. 119 | pub mod op_systems; 120 | 121 | /// Re-exports the most frequently used parts of this library so that they can be used more conveniently. 122 | pub mod prelude { 123 | pub use crate::core::{ 124 | condition_stack::ConditionStack, item::Item, machine::Machine, stack::Stack, Error, Script, 125 | }; 126 | } 127 | -------------------------------------------------------------------------------- /src/op_systems/pokemon.rs: -------------------------------------------------------------------------------- 1 | //! A sample stack machine that showcases how to use custom operators on custom types, and at the 2 | //! same time, doubles as an example of how to implement a state machine. 3 | //! 4 | //! Put any [`Creature`][Creature] into the machine, enter one of the [`Command`s][Command], and 5 | //! watch your creature either evolute or devolute! 6 | //! 7 | //! [Creature]: enum.Creature.html 8 | //! [Command]: enum.Command.html 9 | 10 | use crate::prelude::*; 11 | 12 | /// Simply the first nine Pokémon. 13 | #[derive(Clone, Debug, PartialEq)] 14 | pub enum Creature { 15 | Bulbasaur, 16 | Ivysaur, 17 | Venusaur, 18 | Charmander, 19 | Charmaleon, 20 | Charizard, 21 | Squirtle, 22 | Wartortle, 23 | Blastoise, 24 | } 25 | 26 | /// The different commands that an evolution / devolution machine can understand. 27 | #[derive(Debug, PartialEq, Eq)] 28 | pub enum Command { 29 | /// Evolute a creature into the next stage in its evolution chart. 30 | Evolute, 31 | /// Revert a creature back to its previous stage in its evolution chart. 32 | Devolute, 33 | /// Finish operating the evolution / devolution machine. 34 | Close, 35 | } 36 | 37 | /// The main function that tells which creatures evolute and devolute into which other creatures. 38 | pub fn pokemon_op_sys(stack: &mut Stack, operator: &Command) { 39 | use Creature::*; 40 | let last_creature = stack.pop().unwrap(); 41 | match operator { 42 | Command::Evolute => stack.push(match last_creature { 43 | Bulbasaur => Ivysaur, 44 | Ivysaur => Venusaur, 45 | Charmander => Charmaleon, 46 | Charmaleon => Charizard, 47 | Squirtle => Wartortle, 48 | Wartortle => Blastoise, 49 | any_other => any_other, 50 | }), 51 | Command::Devolute => stack.push(match last_creature { 52 | Ivysaur => Bulbasaur, 53 | Venusaur => Ivysaur, 54 | Charmaleon => Charmander, 55 | Charizard => Charmaleon, 56 | Wartortle => Squirtle, 57 | Blastoise => Wartortle, 58 | any_other => any_other, 59 | }), 60 | Command::Close => {} 61 | } 62 | } 63 | 64 | #[cfg(test)] 65 | mod tests { 66 | use crate::op_systems::pokemon::{pokemon_op_sys, Command::*, Creature::*}; 67 | use crate::prelude::*; 68 | 69 | #[test] 70 | fn test_evolution() { 71 | // Let's create an evolution machine 72 | let mut machine = Machine::new(&pokemon_op_sys); 73 | 74 | // You have a Charmander 75 | let my_creature = Charmander; 76 | 77 | // Put the Charmander into the machine 78 | let result = machine.operate(&Item::Value(my_creature)).unwrap(); 79 | 80 | // There should obviously be a Charmander in the machine 81 | assert_eq!(result, &Charmander); 82 | // And there should be nothing else in the machine 83 | assert_eq!(machine.stack_length(), 1); 84 | 85 | // Let's make it evolute! 86 | let result = machine.operate(&Item::Operator(Evolute)).unwrap(); 87 | 88 | // Charmander should have turned into Charmaleon! 89 | assert_eq!(result, &Charmaleon); 90 | // And again there should be only 1 creature in the machine 91 | assert_eq!(machine.stack_length(), 1); 92 | 93 | // Let's evolute it again 94 | let result = machine.operate(&Item::Operator(Evolute)).unwrap(); 95 | 96 | // Meet our blazing Charizard! 97 | assert_eq!(result, &Charizard); 98 | 99 | // What if we try to evolute Charizard? 100 | let result = machine.operate(&Item::Operator(Evolute)).unwrap(); 101 | 102 | // Good try... but it should still be a Charizard 103 | assert_eq!(result, &Charizard); 104 | 105 | // Ok, we already got Charizard, let's just close the machine and make sure we don't leave 106 | // any creature behind 107 | machine.operate(&Item::Operator(Close)); 108 | assert_eq!(machine.stack_length(), 0); 109 | } 110 | 111 | #[test] 112 | fn test_devolution() { 113 | // Let's create a devolution machine 114 | let mut machine = Machine::new(&pokemon_op_sys); 115 | 116 | // This time you have a Blastoise 117 | let my_creature = Blastoise; 118 | 119 | // Put the Blastoise into the machine 120 | let result = machine.operate(&Item::Value(my_creature)).unwrap(); 121 | 122 | // There should obviously be a Blastoise in the machine 123 | assert_eq!(result, &Blastoise); 124 | // And there should be nothing else in the machine 125 | assert_eq!(machine.stack_length(), 1); 126 | 127 | // Let's make it devolute! 128 | let result = machine.operate(&Item::Operator(Devolute)).unwrap(); 129 | 130 | // Blastoise should have turned into Wartortle! 131 | assert_eq!(result, &Wartortle); 132 | // And again there should be only 1 creature in the machine 133 | assert_eq!(machine.stack_length(), 1); 134 | 135 | // Let's devolute it again 136 | let result = machine.operate(&Item::Operator(Devolute)).unwrap(); 137 | 138 | // Meet our lovely Squirtle! 139 | assert_eq!(result, &Squirtle); 140 | 141 | // What if we try to devolute Squirtle? 142 | let result = machine.operate(&Item::Operator(Devolute)).unwrap(); 143 | 144 | // Good try... but it should still be a Squirtle 145 | assert_eq!(result, &Squirtle); 146 | 147 | // Ok, we already got Squirtle, let's just close the machine and make sure we don't leave 148 | // any creature behind 149 | machine.operate(&Item::Operator(Close)); 150 | assert_eq!(machine.stack_length(), 0); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/codecs/simple/dec.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use alloc::vec::Vec; 3 | 4 | use crate::{ 5 | core::value::Value, 6 | encoding::{ 7 | codecs::simple::SimpleScriptCodec, 8 | dec::{Decode, Decoder}, 9 | DecodingError, 10 | }, 11 | op_systems::simple_math::MathOperator, 12 | prelude::*, 13 | }; 14 | 15 | impl<'a> Decoder for &'a mut SimpleScriptCodec { 16 | type Error = DecodingError; 17 | 18 | fn decode_i128(&mut self) -> Result { 19 | let length = self.read_byte()? as usize - 0x02; 20 | let significant_bytes = self.read_bytes(length)?; 21 | let mut sixteen_bytes = [0u8; 16]; 22 | sixteen_bytes[..length].copy_from_slice(&significant_bytes); 23 | let integer = i128::from_le_bytes(sixteen_bytes); 24 | 25 | Ok(integer) 26 | } 27 | 28 | fn decode_f64(&mut self) -> Result { 29 | self.read_byte()?; 30 | let bytes = self.read_bytes(8)?; 31 | let mut eight_bytes = [0u8; 8]; 32 | eight_bytes.copy_from_slice(&bytes); 33 | let float = f64::from_le_bytes(eight_bytes); 34 | 35 | Ok(float) 36 | } 37 | 38 | fn decode_string(&mut self) -> Result { 39 | let length_length = self.read_byte()? as usize - 0x13; 40 | let length_bytes = self.read_bytes(length_length)?; 41 | let mut eight_length_bytes = [0u8; 8]; 42 | eight_length_bytes[..length_length].copy_from_slice(&length_bytes); 43 | let length = usize::from_le_bytes(eight_length_bytes); 44 | let bytes = self.read_bytes(length)?; 45 | let string = String::from_utf8(bytes.into()) 46 | .map_err(|_| DecodingError::from_str("Not a valid UTF-8 string")); 47 | 48 | string 49 | } 50 | 51 | fn decode_item(&mut self) -> Result, Self::Error> 52 | where 53 | Op: core::fmt::Debug + Decode, 54 | Val: core::fmt::Debug + Decode, 55 | { 56 | let byte = self.peek_byte()?; 57 | if *byte < 0x80 { 58 | Val::decode(&mut *self).map(Item::Value) 59 | } else { 60 | Op::decode(&mut *self).map(Item::Operator) 61 | } 62 | } 63 | 64 | fn decode_script(&mut self) -> Result, Self::Error> 65 | where 66 | Op: core::fmt::Debug + Decode, 67 | Val: core::fmt::Debug + Decode, 68 | { 69 | let mut script = Script::::new(); 70 | 71 | while self.bytes_left() > 0 { 72 | let item = self.decode_item().unwrap(); 73 | script.push(item); 74 | } 75 | 76 | Ok(script) 77 | } 78 | 79 | fn from_vec(input: Vec) -> Result, Self::Error> 80 | where 81 | Op: core::fmt::Debug + Decode, 82 | Val: core::fmt::Debug + Decode, 83 | { 84 | let mut codec = SimpleScriptCodec::from_data(input); 85 | let script = <&mut SimpleScriptCodec as Decoder>::decode_script(&mut &mut codec); 86 | 87 | script 88 | } 89 | 90 | fn peek_byte(&self) -> Result<&u8, Self::Error> { 91 | self.data 92 | .get(self.cursor) 93 | .ok_or_else(|| DecodingError::from_str("Decoder cursor hit end of vector")) 94 | } 95 | 96 | fn read_byte(&mut self) -> Result { 97 | if self.cursor < self.data.len() { 98 | let byte = self.data[self.cursor]; 99 | self.cursor += 1; 100 | 101 | Ok(byte) 102 | } else { 103 | Err(DecodingError::from_str( 104 | "Decoder cursor hit end of vector when reading a single byte", 105 | )) 106 | } 107 | } 108 | 109 | fn read_bytes(&mut self, length: usize) -> Result<&[u8], Self::Error> { 110 | if self.cursor + length <= self.data.len() { 111 | let bytes = &self.data[self.cursor..self.cursor + length]; 112 | self.cursor += length; 113 | 114 | Ok(bytes) 115 | } else { 116 | Err(DecodingError::from_str(&alloc::format!( 117 | "Decoder cursor hit end of vector when reading {} bytes, while the decoder only had {} in its data vector", 118 | length, self.bytes_left() 119 | ))) 120 | } 121 | } 122 | } 123 | 124 | impl Decode for MathOperator { 125 | fn decode(decoder: &mut D) -> Result::Error> 126 | where 127 | D: Decoder, 128 | { 129 | let discriminant = decoder.read_byte()? - 0x80; 130 | 131 | match discriminant { 132 | 0x00 => Ok(MathOperator::Add), 133 | 0x01 => Ok(MathOperator::Equal), 134 | 0x02 => Ok(MathOperator::Mul), 135 | 0x03 => Ok(MathOperator::Not), 136 | 0x04 => Ok(MathOperator::Sub), 137 | x => Err(::Error::from_str(&alloc::format!( 138 | "Unsupported MathOperator {}", 139 | x 140 | ))), 141 | } 142 | } 143 | } 144 | 145 | impl Decode for Value { 146 | fn decode(decoder: &mut D) -> Result::Error> 147 | where 148 | D: Decoder, 149 | { 150 | let discriminant = decoder.peek_byte()?; 151 | 152 | match discriminant { 153 | 0x00 => decoder.read_byte().map(|_| Value::Boolean(false)), 154 | 0x01 => decoder.read_byte().map(|_| Value::Boolean(true)), 155 | 0x02 => decoder.decode_f64().map(Value::Float), 156 | 0x03..=0x012 => decoder.decode_i128().map(Value::Integer), 157 | 0x13..=0x79 => decoder.decode_string().map(Value::String), 158 | x => Err(::Error::from_str(&alloc::format!( 159 | "Unsupported value discriminant {}", 160 | x 161 | ))), 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/core/value.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | #[cfg(feature = "use_serde")] 3 | use serde; 4 | 5 | /// An algebraic data type that can be used to represent many types of values that can be present in 6 | /// a [`Stack`][Stack]. 7 | /// 8 | /// Scriptful supports customization of value types through generics, yet this [`enum`][enum] is 9 | /// provided in expectation that some users will prefer not to define their own type systems but 10 | /// rather use a stock solution. 11 | /// 12 | /// `Value` has four variants that should cover most use cases, namely: [`Boolean`][Boolean], 13 | /// [`Float`][Float], [`Integer`][Integer] or [`String`][String]. 14 | /// 15 | /// The point of `Value` is being used inside [`Script`s][Script] (wrapped in an [`Item`][Item]) or 16 | /// to be pushed into a [`Stack`][Stack]. 17 | /// 18 | /// `Value` does not implement any methods other than implementations of some traits from the 19 | /// [`core`][core] crate. 20 | /// 21 | /// [Stack]: ../stack/struct.Stack.html 22 | /// [enum]: https://doc.rust-lang.org/std/keyword.enum.html 23 | /// [Boolean]: #variant.Boolean 24 | /// [Float]: #variant.Float 25 | /// [Integer]: #variant.Integer 26 | /// [String]: #variant.String 27 | /// [Script]: ../type.Script.html 28 | /// [Item]: ../item/enum.Item.html 29 | /// [known limitations]: ../../#known-limitations 30 | /// [core]: https://doc.rust-lang.org/nightly/core/ 31 | #[derive(Clone, Debug, PartialOrd)] 32 | #[cfg_attr(feature = "use_serde", derive(serde::Deserialize, serde::Serialize))] 33 | pub enum Value { 34 | /// A binary value: either `true` or `false`. 35 | Boolean(bool), 36 | /// A signed floating point value. 37 | Float(f64), 38 | /// A signed integer value. 39 | Integer(i128), 40 | /// A string of characters. 41 | String(String), 42 | } 43 | 44 | impl core::ops::Not for Value { 45 | type Output = Self; 46 | 47 | fn not(self) -> Self::Output { 48 | use Value::*; 49 | match self { 50 | Boolean(x) => Boolean(!x), 51 | Float(x) => Float(-x), 52 | Integer(x) => Integer(-x), 53 | _ => panic!("Type of {:?} cannot be negated", self), 54 | } 55 | } 56 | } 57 | 58 | #[allow(clippy::suspicious_arithmetic_impl)] 59 | impl core::ops::Add for Value { 60 | type Output = Self; 61 | 62 | fn add(self, rhs: Self) -> Self::Output { 63 | use Value::*; 64 | match (self, rhs) { 65 | (Boolean(a), Boolean(b)) => Boolean(a || b), 66 | (Float(a), Float(b)) => Float(a + b), 67 | (Float(a), Integer(b)) => Float(a + b as f64), 68 | (Integer(a), Integer(b)) => Integer(a + b), 69 | (Integer(a), Float(b)) => Float(a as f64 + b), 70 | (a, b) => panic!("Types of {:?} and {:?} cannot be added together", a, b), 71 | } 72 | } 73 | } 74 | 75 | #[allow(clippy::suspicious_arithmetic_impl)] 76 | impl core::ops::Mul for Value { 77 | type Output = Self; 78 | 79 | fn mul(self, rhs: Self) -> Self::Output { 80 | use Value::*; 81 | match (self, rhs) { 82 | (Boolean(a), Boolean(b)) => Boolean(a && b), 83 | (Float(a), Float(b)) => Float(a * b), 84 | (Float(a), Integer(b)) => Float(a * b as f64), 85 | (Integer(a), Integer(b)) => Integer(a * b), 86 | (Integer(a), Float(b)) => Float(a as f64 * b), 87 | (a, b) => panic!("Types of {:?} and {:?} cannot be multiplied together", a, b), 88 | } 89 | } 90 | } 91 | 92 | impl core::ops::Sub for Value { 93 | type Output = Self; 94 | 95 | fn sub(self, rhs: Self) -> Self::Output { 96 | self + !rhs 97 | } 98 | } 99 | 100 | /// Approximate comparison, so as to support comparison of floating point values. 101 | /// 102 | /// A floating point values is considered equal to another float or an integer if the difference is 103 | /// less than `10^9`. 104 | impl core::cmp::PartialEq for Value { 105 | fn eq(&self, other: &Self) -> bool { 106 | use Value::*; 107 | match (self, other) { 108 | (Boolean(a), Boolean(b)) => a == b, 109 | (Float(a), Float(b)) => (a - b) * (a - b) < 0.000_000_000_000_000_000_1, 110 | (Float(a), Integer(b)) => { 111 | (a - *b as f64) * (a - *b as f64) < 0.000_000_000_000_000_000_1 112 | } 113 | (Integer(a), Integer(b)) => a == b, 114 | (Integer(a), Float(b)) => { 115 | (*a as f64 - b) * (*a as f64 - *b) < 0.000_000_000_000_000_000_1 116 | } 117 | (String(a), String(b)) => a == b, 118 | _ => false, 119 | } 120 | } 121 | } 122 | 123 | // The `Float` variant is untested because floating point numbers cannot be trivially nor accurately 124 | // compared. 125 | #[cfg(test)] 126 | mod tests { 127 | use crate::core::value::Value::*; 128 | 129 | #[test] 130 | fn test_negation() { 131 | assert_eq!(!Boolean(true), Boolean(false)); 132 | assert_eq!(!Boolean(false), Boolean(true)); 133 | assert_eq!(!Float(0.), Float(0.)); 134 | assert_eq!(!Float(1.1), Float(-1.1)); 135 | assert_eq!(!Integer(0), Integer(0)); 136 | assert_eq!(!Integer(1), Integer(-1)); 137 | } 138 | 139 | #[test] 140 | fn test_addition() { 141 | assert_eq!(Boolean(false) + Boolean(false), Boolean(false)); 142 | assert_eq!(Boolean(false) + Boolean(true), Boolean(true)); 143 | assert_eq!(Boolean(true) + Boolean(false), Boolean(true)); 144 | assert_eq!(Boolean(true) + Boolean(true), Boolean(true)); 145 | assert_eq!(Float(1.1) + Float(2.2), Float(3.3)); 146 | assert_eq!(Float(1.1) + Float(-2.2), Float(-1.1)); 147 | assert_eq!(Float(1.1) + Integer(2), Float(3.1)); 148 | assert_eq!(Float(1.1) + Integer(-2), Float(-0.9)); 149 | assert_eq!(Integer(1) + Integer(2), Integer(3)); 150 | assert_eq!(Integer(1) + Integer(-2), Integer(-1)); 151 | assert_eq!(Integer(1) + Float(2.2), Float(3.2)); 152 | assert_eq!(Integer(1) + Float(-2.1), Float(-1.1)); 153 | } 154 | 155 | #[test] 156 | fn test_subtraction() { 157 | assert_eq!(Boolean(false) - Boolean(false), Boolean(true)); 158 | assert_eq!(Boolean(false) - Boolean(true), Boolean(false)); 159 | assert_eq!(Boolean(true) - Boolean(false), Boolean(true)); 160 | assert_eq!(Boolean(true) - Boolean(true), Boolean(true)); 161 | assert_eq!(Float(1.1) - Float(2.2), Float(-1.1)); 162 | assert_eq!(Float(1.1) - Float(-2.2), Float(3.3)); 163 | assert_eq!(Float(1.1) - Integer(2), Float(-0.9)); 164 | assert_eq!(Float(1.1) - Integer(-2), Float(3.1)); 165 | assert_eq!(Integer(1) - Integer(2), Integer(-1)); 166 | assert_eq!(Integer(1) - Integer(-2), Integer(3)); 167 | assert_eq!(Integer(1) - Float(2.2), Float(-1.2)); 168 | assert_eq!(Integer(1) - Float(-2.2), Float(3.2)); 169 | } 170 | 171 | #[test] 172 | fn test_multiplication() { 173 | assert_eq!(Boolean(false) * Boolean(false), Boolean(false)); 174 | assert_eq!(Boolean(false) * Boolean(true), Boolean(false)); 175 | assert_eq!(Boolean(true) * Boolean(false), Boolean(false)); 176 | assert_eq!(Boolean(true) * Boolean(true), Boolean(true)); 177 | assert_eq!(Float(1.1) * Float(2.2), Float(2.42)); 178 | assert_eq!(Float(1.1) * Float(-2.2), Float(-2.42)); 179 | assert_eq!(Float(1.1) * Integer(2), Float(2.2)); 180 | assert_eq!(Float(1.1) * Integer(-2), Float(-2.2)); 181 | assert_eq!(Integer(1) * Integer(2), Integer(2)); 182 | assert_eq!(Integer(1) * Integer(-2), Integer(-2)); 183 | assert_eq!(Integer(1) * Float(2.2), Float(2.2)); 184 | assert_eq!(Integer(1) * Float(-2.2), Float(-2.2)); 185 | } 186 | 187 | #[test] 188 | fn test_comparison() { 189 | assert_eq!(Boolean(false) == Boolean(false), true); 190 | assert_eq!(Boolean(false) == Boolean(true), false); 191 | assert_eq!(Boolean(true) == Boolean(false), false); 192 | assert_eq!(Boolean(true) == Boolean(true), true); 193 | assert_eq!(Float(1.1) == Float(1.1), true); 194 | assert_eq!(Float(1.1) == Float(2.2), false); 195 | assert_eq!(Float(-1.1) == Float(-1.1), true); 196 | assert_eq!(Float(-1.1) == Float(-2.2), false); 197 | assert_eq!(Float(1.1) == Float(-1.1), false); 198 | assert_eq!(Float(1.) == Integer(1), true); 199 | assert_eq!(Float(-1.) == Integer(-1), true); 200 | assert_eq!(Integer(1) == Integer(1), true); 201 | assert_eq!(Integer(1) == Integer(2), false); 202 | assert_eq!(Integer(-1) == Integer(-1), true); 203 | assert_eq!(Integer(-1) == Integer(-2), false); 204 | assert_eq!(Integer(1) == Integer(-1), false); 205 | assert_eq!(Integer(1) == Float(1.), true); 206 | assert_eq!(Integer(-1) == Float(-1.), true); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/core/machine.rs: -------------------------------------------------------------------------------- 1 | use crate::core::ScriptRef; 2 | use crate::prelude::*; 3 | use core::marker::PhantomData; 4 | 5 | /// A convenient wrapper around [`Stack`][Stack] providing multiple operation methods, i.e. 6 | /// xecuting scripts by evaluating operators and pushing values into the stack. 7 | /// 8 | /// This is the preferred way to interact with [`Stack`s][Stack], as they do not support operators, 9 | /// [`Item`s][Item], and other abstractions. 10 | /// 11 | /// [Stack]: ../stack/struct.Stack.html 12 | /// [Item]: ../item/enum.Item.html 13 | pub struct Machine 14 | where 15 | Val: core::fmt::Debug + core::cmp::PartialEq, 16 | F: FnMut(&mut Stack, &Op, &mut ConditionStack) -> Result<(), E>, 17 | { 18 | op_sys: F, 19 | stack: Stack, 20 | if_stack: ConditionStack, 21 | phantom_op: PhantomData, 22 | } 23 | 24 | impl Machine 25 | where 26 | Op: core::fmt::Debug + core::cmp::Eq, 27 | Val: core::fmt::Debug + core::cmp::PartialEq + core::clone::Clone, 28 | F: FnMut(&mut Stack, &Op, &mut ConditionStack) -> Result<(), E>, 29 | { 30 | /// A simple factory that helps constructing a `Machine` around a existing operator system, be 31 | /// it user defined or any of the ones in the [`op_systems`][op_systems] module. 32 | /// 33 | /// This method initializes the internal stack to be empty. 34 | /// 35 | /// [op_systems]: ../../op_systems/ 36 | /// 37 | /// # Examples 38 | /// 39 | /// ```rust 40 | /// use scriptful::prelude::*; 41 | /// use scriptful::op_systems::simple_math::simple_math_op_sys; 42 | /// 43 | /// // Instantiate the machine with a reference to your operator system, or any of the ones in 44 | /// // the `op_systems` module. 45 | /// let machine = Machine::new(&simple_math_op_sys); 46 | /// 47 | /// // Make sure the stack is initialized to be empty. 48 | /// assert_eq!(machine.stack_length(), 0); 49 | /// ``` 50 | pub fn new(op_sys: F) -> Self { 51 | Self { 52 | op_sys, 53 | stack: Stack::::default(), 54 | if_stack: ConditionStack::default(), 55 | phantom_op: PhantomData, 56 | } 57 | } 58 | 59 | /// The simplest way to make a `Machine` evaluate a single [`Item`][Item], be it a `Value` or 60 | /// `Operator`. 61 | /// 62 | /// Note that the preferred way to evaluate multiple [`Item`s][Item] at once is through the 63 | /// [`run_script`][run_script] method, which instead of single [`Item`s][Item] takes a 64 | /// [`Script`][Script], i.e. an array of [`Item`s][Item]. 65 | /// 66 | /// # Panics 67 | /// 68 | /// Operating on a `Machine` that has an empty [`Stack`][Stack] can cause a panic if the 69 | /// [`Item`][Item] is an operator that tries to pop from it. 70 | /// 71 | /// # Examples 72 | /// 73 | /// ```rust 74 | /// use scriptful::prelude::*; 75 | /// use scriptful::core::value::Value::*; 76 | /// use scriptful::op_systems::simple_math::*; 77 | /// 78 | /// // Instantiate the machine with a reference to your operator system, or any of the ones in 79 | /// // the `op_systems` module. 80 | /// let mut machine = Machine::new(&simple_math_op_sys); 81 | /// 82 | /// // Operating a `Value::Integer(1)` should simply push it into the stack. 83 | /// let result = machine.operate(&Item::Value(Integer(1))); 84 | /// // Make sure the value gets pushed. 85 | /// assert_eq!(result, Some(&Integer(1))); 86 | /// // The length of the stack should be 1. 87 | /// assert_eq!(machine.stack_length(), 1); 88 | /// 89 | /// // Operating a `Value::Integer(2)` should simply push it into the stack. 90 | /// let result = machine.operate(&Item::Value(Integer(2))); 91 | /// // Make sure the value gets pushed. 92 | /// assert_eq!(result, Some(&Integer(2))); 93 | /// // The length of the stack should be 2. 94 | /// assert_eq!(machine.stack_length(), 2); 95 | /// 96 | /// // Operating an `MathOperator::Add` should pop the two topmost values in the stack, add them 97 | /// // together, and push the result back into the stack. 98 | /// let result = machine.operate(&Item::Operator(MathOperator::Add)); 99 | /// // Make sure the result is 3. 100 | /// assert_eq!(result, Some(&Integer(3))); 101 | /// // The final length of the stack should be 1 again. 102 | /// assert_eq!(machine.stack_length(), 1); 103 | /// ``` 104 | /// 105 | /// [Item]: ../item/enum.Item.html 106 | /// [run_script]: #method.run_script 107 | /// [Script]: ../type.Script.html 108 | /// [Stack]: ../stack/struct.Stack.html 109 | pub fn operate(&mut self, item: &Item) -> Result, E> { 110 | match item { 111 | Item::Operator(operator) => { 112 | (self.op_sys)(&mut self.stack, operator, &mut self.if_stack) 113 | } 114 | Item::Value(value) => { 115 | if self.if_stack.all_true() { 116 | self.stack.push((*value).clone()); 117 | } 118 | 119 | Ok(()) 120 | } 121 | } 122 | .map(|()| self.stack.topmost()) 123 | } 124 | 125 | /// Evaluates a [`Script`][Script] in the context of a `Machine`. 126 | /// 127 | /// # Panics 128 | /// 129 | /// Operating on a `Machine` that has an empty [`Stack`][Stack] can cause a panic if any of the 130 | /// [`Item`s][Item] in the [`Script`][Script] is an operator that tries to pop from it. 131 | /// 132 | /// # Examples 133 | /// 134 | /// ```rust 135 | /// use scriptful::prelude::*; 136 | /// use scriptful::core::value::Value::*; 137 | /// use scriptful::op_systems::simple_math::*; 138 | /// 139 | /// // Instantiate the machine with a reference to your operator system, or any of the ones in 140 | /// // the `op_systems` module. 141 | /// let mut machine = Machine::new(&simple_math_op_sys); 142 | /// 143 | /// // Run a script that simply adds 1 and 2. 144 | /// let result = machine.run_script(&Vec::from([ 145 | /// Item::Value(Integer(1)), 146 | /// Item::Value(Integer(2)), 147 | /// Item::Operator(MathOperator::Add), 148 | /// ])); 149 | /// 150 | /// // The result should unsurprisingly be 3. 151 | /// assert_eq!(result, Some(&Integer(3))); 152 | /// // The final length of the stack should be 1. 153 | /// assert_eq!(machine.stack_length(), 1); 154 | /// ``` 155 | /// 156 | /// [Script]: ../type.Script.html 157 | /// [Stack]: ../stack/struct.Stack.html 158 | /// [Item]: ../item/enum.Item.html 159 | pub fn run_script(&mut self, script: ScriptRef) -> Result, E> { 160 | for item in script { 161 | self.operate(item)?; 162 | } 163 | 164 | Ok(self.stack.topmost()) 165 | } 166 | 167 | /// Returns the number of [`Value`s][Value] currently in the [`Stack`][Stack]. 168 | /// 169 | /// # Examples 170 | /// 171 | /// ```rust 172 | /// use scriptful::prelude::*; 173 | /// use scriptful::core::value::Value::*; 174 | /// use scriptful::op_systems::simple_math::*; 175 | /// 176 | /// // Instantiate the machine with a reference to your operator system, or any of the ones in 177 | /// // the `op_systems` module. 178 | /// let mut machine = Machine::new(&simple_math_op_sys); 179 | /// 180 | /// // Run a script that simply pushes 4 values into the stack. 181 | /// machine.run_script(&Vec::from([ 182 | /// Item::Value(Boolean(true)), 183 | /// Item::Value(Float(3.141592)), 184 | /// Item::Value(Integer(1337)), 185 | /// Item::Value(String("foo".into())) 186 | /// ])); 187 | /// 188 | /// // The final length of the stack should be 4. 189 | /// assert_eq!(machine.stack_length(), 4); 190 | /// ``` 191 | /// 192 | /// [Value]: ../value/enum.Value.html 193 | /// [Stack]: ../stack/struct.Stack.html 194 | pub fn stack_length(&self) -> usize { 195 | self.stack.length() 196 | } 197 | } 198 | 199 | /// Debugging of `Machine` only shows the internal [`Stack`][Stack], but not the operator system. 200 | /// 201 | /// The explanation for this is straightforward: how do you print a dynamic reference to a function? 202 | /// 203 | /// [Stack]: ../stack/struct.Stack.html 204 | impl core::fmt::Debug for Machine 205 | where 206 | Op: core::fmt::Debug + core::cmp::Eq, 207 | Val: core::fmt::Debug + core::cmp::PartialEq + core::clone::Clone, 208 | F: FnMut(&mut Stack, &Op, &mut ConditionStack) -> Result<(), E>, 209 | { 210 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { 211 | f.debug_struct("Machine") 212 | .field("stack", &self.stack) 213 | .field("if_stack", &self.if_stack) 214 | .finish() 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | https://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | https://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/codecs/simple/mod.rs: -------------------------------------------------------------------------------- 1 | //! A example of a very simple and compact binary codec for the `SimpleMath` operator system. 2 | //! 3 | //! This is somehow similar to [CBOR], but specific to the data types defined in [`Value`][Value]. 4 | //! 5 | //! As a matter of fact, this is just as concise as CBOR, and the main difference is endianness and 6 | //! simpler logic for encoding and decoding. 7 | //! 8 | //! | Value | CBOR | Simple | 9 | //! |----------------------|----------------------------------------|--------------------------------------| 10 | //! | `false` | `F4` | `00` | 11 | //! | `3.14` | `FB40091EB851EB851F` | `021F85EB51B81E0940` | 12 | //! | `255` | `18FF` | `03FF` | 13 | //! | `999999999999999999` | `1B0DE0B6B3A763FFFF` | `0AFFFF63A7B3B6E00D` | 14 | //! | `i128::MIN` | `C3507FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF` | `1200000000000000000000000000000080` | 15 | //! | `i128::MAX` | `C2507FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF` | `12FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F` | 16 | //! | `""` | `60` | `13` | 17 | //! | `"Hello, World!"` | `6D48656C6C6F2C20576F726C6421` | `140D48656C6C6F2C20576F726C6421` | 18 | //! 19 | //! [CBOR]: https://www.rfc-editor.org/rfc/rfc8949 20 | //! [Value]: ../../../core/value/enum.Value.html 21 | 22 | use alloc::vec::Vec; 23 | 24 | pub mod dec; 25 | pub mod enc; 26 | 27 | #[derive(Default)] 28 | pub struct SimpleScriptCodec { 29 | data: Vec, 30 | cursor: usize, 31 | } 32 | 33 | impl SimpleScriptCodec { 34 | pub fn data(self) -> Vec { 35 | self.data 36 | } 37 | 38 | pub fn data_push(&mut self, input: u8) { 39 | self.data.push(input) 40 | } 41 | 42 | pub fn from_data(data: Vec) -> Self { 43 | Self { data, cursor: 0 } 44 | } 45 | 46 | pub fn bytes_left(&self) -> usize { 47 | self.data.len() - self.cursor 48 | } 49 | } 50 | 51 | /// Tells how many significant bytes a number takes. 52 | /// 53 | /// This operation equates to counting how many zeroed bytes it has in its LSB side. 54 | /// 55 | /// This is useful for compressing numbers in binary serialization formats. 56 | fn significant_bytes_count(input: i128) -> usize { 57 | let mut dividend = input.saturating_abs(); 58 | let mut counter = 0; 59 | 60 | while dividend > 256 { 61 | dividend >>= 8; 62 | counter += 1; 63 | } 64 | 65 | counter 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use alloc::vec::Vec; 71 | 72 | use crate::core::value::Value; 73 | use crate::encoding::codecs::simple::SimpleScriptCodec; 74 | use crate::encoding::dec::Decode; 75 | use crate::encoding::enc::Encode; 76 | 77 | #[test] 78 | fn test_boolean_false_codec() { 79 | let value = Value::Boolean(false); 80 | let mut codec = SimpleScriptCodec::default(); 81 | value.encode(&mut codec); 82 | let encoded = codec.data(); 83 | let expected = Vec::::from([0]); 84 | 85 | assert_eq!(encoded, expected); 86 | 87 | codec = SimpleScriptCodec::from_data(expected); 88 | let decoded = Value::decode(&mut &mut codec).unwrap(); 89 | 90 | assert_eq!(decoded, value); 91 | } 92 | 93 | #[test] 94 | fn test_boolean_true_codec() { 95 | let value = Value::Boolean(true); 96 | let mut codec = SimpleScriptCodec::default(); 97 | value.encode(&mut codec); 98 | let encoded = codec.data(); 99 | let expected = Vec::::from([1]); 100 | 101 | assert_eq!(encoded, expected); 102 | 103 | codec = SimpleScriptCodec::from_data(expected); 104 | let decoded = Value::decode(&mut &mut codec).unwrap(); 105 | 106 | assert_eq!(decoded, value); 107 | } 108 | 109 | #[test] 110 | fn test_float_codec() { 111 | let value = Value::Float(3.14); 112 | let mut codec = SimpleScriptCodec::default(); 113 | value.encode(&mut codec); 114 | let encoded = codec.data(); 115 | let expected = Vec::::from([2, 31, 133, 235, 81, 184, 30, 9, 64]); 116 | 117 | assert_eq!(encoded, expected); 118 | 119 | codec = SimpleScriptCodec::from_data(expected); 120 | let decoded = Value::decode(&mut &mut codec).unwrap(); 121 | 122 | assert_eq!(decoded, value); 123 | } 124 | 125 | #[test] 126 | fn test_integer_small_codec() { 127 | let value = Value::Integer(255); 128 | let mut codec = SimpleScriptCodec::default(); 129 | value.encode(&mut codec); 130 | let encoded = codec.data(); 131 | let expected = Vec::::from([3, 255]); 132 | 133 | assert_eq!(encoded, expected); 134 | 135 | codec = SimpleScriptCodec::from_data(expected); 136 | let decoded = Value::decode(&mut &mut codec).unwrap(); 137 | 138 | assert_eq!(decoded, value); 139 | } 140 | 141 | #[test] 142 | fn test_integer_big_codec() { 143 | let value = Value::Integer(999999999999999999); 144 | let mut codec = SimpleScriptCodec::default(); 145 | value.encode(&mut codec); 146 | let encoded = codec.data(); 147 | let expected = Vec::::from([10, 255, 255, 99, 167, 179, 182, 224, 13]); 148 | 149 | assert_eq!(encoded, expected); 150 | 151 | codec = SimpleScriptCodec::from_data(expected); 152 | let decoded = Value::decode(&mut &mut codec).unwrap(); 153 | 154 | assert_eq!(decoded, value); 155 | } 156 | 157 | #[test] 158 | fn test_integer_max_codec() { 159 | let value = Value::Integer(i128::MAX); 160 | let mut codec = SimpleScriptCodec::default(); 161 | value.encode(&mut codec); 162 | let encoded = codec.data(); 163 | let expected = Vec::::from([ 164 | 18, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 165 | ]); 166 | 167 | assert_eq!(encoded, expected); 168 | 169 | codec = SimpleScriptCodec::from_data(expected); 170 | let decoded = Value::decode(&mut &mut codec).unwrap(); 171 | 172 | assert_eq!(decoded, value); 173 | } 174 | 175 | #[test] 176 | fn test_integer_min_codec() { 177 | let value = Value::Integer(i128::MIN); 178 | let mut codec = SimpleScriptCodec::default(); 179 | value.encode(&mut codec); 180 | let encoded = codec.data(); 181 | let expected = Vec::::from([18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128]); 182 | 183 | assert_eq!(encoded, expected); 184 | 185 | codec = SimpleScriptCodec::from_data(expected); 186 | let decoded = Value::decode(&mut &mut codec).unwrap(); 187 | 188 | assert_eq!(decoded, value); 189 | } 190 | 191 | #[test] 192 | fn test_string_empty_codec() { 193 | let value = Value::String("".into()); 194 | let mut codec = SimpleScriptCodec::default(); 195 | value.encode(&mut codec); 196 | let encoded = codec.data(); 197 | let expected = Vec::::from([19]); 198 | 199 | assert_eq!(encoded, expected); 200 | 201 | codec = SimpleScriptCodec::from_data(expected); 202 | let decoded = Value::decode(&mut &mut codec).unwrap(); 203 | 204 | assert_eq!(decoded, value); 205 | } 206 | 207 | #[test] 208 | fn test_string_regular_codec() { 209 | let value = Value::String("Hello, World!".into()); 210 | let mut codec = SimpleScriptCodec::default(); 211 | value.encode(&mut codec); 212 | let encoded = codec.data(); 213 | let expected = Vec::::from([ 214 | 20, 13, 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33, 215 | ]); 216 | 217 | assert_eq!(encoded, expected); 218 | 219 | codec = SimpleScriptCodec::from_data(expected); 220 | let decoded = Value::decode(&mut &mut codec).unwrap(); 221 | 222 | assert_eq!(decoded, value); 223 | } 224 | 225 | #[test] 226 | fn test_string_massive_codec() { 227 | let value = Value::String( 228 | r#" 229 | En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha mucho tiempo que vivía un 230 | hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor. Una olla de algo 231 | más vaca que carnero, salpicón las más noches, duelos y quebrantos los sábados, lentejas los 232 | viernes, algún palomino de añadidura los domingos, consumían las tres partes de su hacienda. El 233 | resto della concluían sayo de velarte, calzas de velludo para las fiestas con sus pantuflos de lo 234 | mismo, los días de entre semana se honraba con su vellori de lo más fino. Tenía en su casa una ama 235 | que pasaba de los cuarenta, y una sobrina que no llegaba a los veinte, y un mozo de campo y plaza, 236 | que así ensillaba el rocín como tomaba la podadera. Frisaba la edad de nuestro hidalgo con los 237 | cincuenta años, era de complexión recia, seco de carnes, enjuto de rostro; gran madrugador y amigo 238 | de la caza. Quieren decir que tenía el sobrenombre de Quijada o Quesada (que en esto hay alguna 239 | diferencia en los autores que deste caso escriben), aunque por conjeturas verosímiles se deja 240 | entender que se llama Quijana; pero esto importa poco a nuestro cuento; basta que en la narración 241 | dél no se salga un punto de la verdad."# 242 | .into(), 243 | ); 244 | let mut codec = SimpleScriptCodec::default(); 245 | value.encode(&mut codec); 246 | let encoded = codec.data(); 247 | let expected = Vec::::from([ 248 | 21, 198, 4, 10, 69, 110, 32, 117, 110, 32, 108, 117, 103, 97, 114, 32, 100, 101, 32, 249 | 108, 97, 32, 77, 97, 110, 99, 104, 97, 44, 32, 100, 101, 32, 99, 117, 121, 111, 32, 250 | 110, 111, 109, 98, 114, 101, 32, 110, 111, 32, 113, 117, 105, 101, 114, 111, 32, 97, 251 | 99, 111, 114, 100, 97, 114, 109, 101, 44, 32, 110, 111, 32, 104, 97, 32, 109, 117, 99, 252 | 104, 111, 32, 116, 105, 101, 109, 112, 111, 32, 113, 117, 101, 32, 118, 105, 118, 195, 253 | 173, 97, 32, 117, 110, 10, 104, 105, 100, 97, 108, 103, 111, 32, 100, 101, 32, 108, 254 | 111, 115, 32, 100, 101, 32, 108, 97, 110, 122, 97, 32, 101, 110, 32, 97, 115, 116, 105, 255 | 108, 108, 101, 114, 111, 44, 32, 97, 100, 97, 114, 103, 97, 32, 97, 110, 116, 105, 103, 256 | 117, 97, 44, 32, 114, 111, 99, 195, 173, 110, 32, 102, 108, 97, 99, 111, 32, 121, 32, 257 | 103, 97, 108, 103, 111, 32, 99, 111, 114, 114, 101, 100, 111, 114, 46, 32, 85, 110, 97, 258 | 32, 111, 108, 108, 97, 32, 100, 101, 32, 97, 108, 103, 111, 10, 109, 195, 161, 115, 32, 259 | 118, 97, 99, 97, 32, 113, 117, 101, 32, 99, 97, 114, 110, 101, 114, 111, 44, 32, 115, 260 | 97, 108, 112, 105, 99, 195, 179, 110, 32, 108, 97, 115, 32, 109, 195, 161, 115, 32, 261 | 110, 111, 99, 104, 101, 115, 44, 32, 100, 117, 101, 108, 111, 115, 32, 121, 32, 113, 262 | 117, 101, 98, 114, 97, 110, 116, 111, 115, 32, 108, 111, 115, 32, 115, 195, 161, 98, 263 | 97, 100, 111, 115, 44, 32, 108, 101, 110, 116, 101, 106, 97, 115, 32, 108, 111, 115, 264 | 10, 118, 105, 101, 114, 110, 101, 115, 44, 32, 97, 108, 103, 195, 186, 110, 32, 112, 265 | 97, 108, 111, 109, 105, 110, 111, 32, 100, 101, 32, 97, 195, 177, 97, 100, 105, 100, 266 | 117, 114, 97, 32, 108, 111, 115, 32, 100, 111, 109, 105, 110, 103, 111, 115, 44, 32, 267 | 99, 111, 110, 115, 117, 109, 195, 173, 97, 110, 32, 108, 97, 115, 32, 116, 114, 101, 268 | 115, 32, 112, 97, 114, 116, 101, 115, 32, 100, 101, 32, 115, 117, 32, 104, 97, 99, 105, 269 | 101, 110, 100, 97, 46, 32, 69, 108, 10, 114, 101, 115, 116, 111, 32, 100, 101, 108, 270 | 108, 97, 32, 99, 111, 110, 99, 108, 117, 195, 173, 97, 110, 32, 115, 97, 121, 111, 32, 271 | 100, 101, 32, 118, 101, 108, 97, 114, 116, 101, 44, 32, 99, 97, 108, 122, 97, 115, 32, 272 | 100, 101, 32, 118, 101, 108, 108, 117, 100, 111, 32, 112, 97, 114, 97, 32, 108, 97, 273 | 115, 32, 102, 105, 101, 115, 116, 97, 115, 32, 99, 111, 110, 32, 115, 117, 115, 32, 274 | 112, 97, 110, 116, 117, 102, 108, 111, 115, 32, 100, 101, 32, 108, 111, 10, 109, 105, 275 | 115, 109, 111, 44, 32, 108, 111, 115, 32, 100, 195, 173, 97, 115, 32, 100, 101, 32, 276 | 101, 110, 116, 114, 101, 32, 115, 101, 109, 97, 110, 97, 32, 115, 101, 32, 104, 111, 277 | 110, 114, 97, 98, 97, 32, 99, 111, 110, 32, 115, 117, 32, 118, 101, 108, 108, 111, 114, 278 | 105, 32, 100, 101, 32, 108, 111, 32, 109, 195, 161, 115, 32, 102, 105, 110, 111, 46, 279 | 32, 84, 101, 110, 195, 173, 97, 32, 101, 110, 32, 115, 117, 32, 99, 97, 115, 97, 32, 280 | 117, 110, 97, 32, 97, 109, 97, 10, 113, 117, 101, 32, 112, 97, 115, 97, 98, 97, 32, 281 | 100, 101, 32, 108, 111, 115, 32, 99, 117, 97, 114, 101, 110, 116, 97, 44, 32, 121, 32, 282 | 117, 110, 97, 32, 115, 111, 98, 114, 105, 110, 97, 32, 113, 117, 101, 32, 110, 111, 32, 283 | 108, 108, 101, 103, 97, 98, 97, 32, 97, 32, 108, 111, 115, 32, 118, 101, 105, 110, 116, 284 | 101, 44, 32, 121, 32, 117, 110, 32, 109, 111, 122, 111, 32, 100, 101, 32, 99, 97, 109, 285 | 112, 111, 32, 121, 32, 112, 108, 97, 122, 97, 44, 10, 113, 117, 101, 32, 97, 115, 195, 286 | 173, 32, 101, 110, 115, 105, 108, 108, 97, 98, 97, 32, 101, 108, 32, 114, 111, 99, 195, 287 | 173, 110, 32, 99, 111, 109, 111, 32, 116, 111, 109, 97, 98, 97, 32, 108, 97, 32, 112, 288 | 111, 100, 97, 100, 101, 114, 97, 46, 32, 70, 114, 105, 115, 97, 98, 97, 32, 108, 97, 289 | 32, 101, 100, 97, 100, 32, 100, 101, 32, 110, 117, 101, 115, 116, 114, 111, 32, 104, 290 | 105, 100, 97, 108, 103, 111, 32, 99, 111, 110, 32, 108, 111, 115, 10, 99, 105, 110, 99, 291 | 117, 101, 110, 116, 97, 32, 97, 195, 177, 111, 115, 44, 32, 101, 114, 97, 32, 100, 101, 292 | 32, 99, 111, 109, 112, 108, 101, 120, 105, 195, 179, 110, 32, 114, 101, 99, 105, 97, 293 | 44, 32, 115, 101, 99, 111, 32, 100, 101, 32, 99, 97, 114, 110, 101, 115, 44, 32, 101, 294 | 110, 106, 117, 116, 111, 32, 100, 101, 32, 114, 111, 115, 116, 114, 111, 59, 32, 103, 295 | 114, 97, 110, 32, 109, 97, 100, 114, 117, 103, 97, 100, 111, 114, 32, 121, 32, 97, 109, 296 | 105, 103, 111, 10, 100, 101, 32, 108, 97, 32, 99, 97, 122, 97, 46, 32, 81, 117, 105, 297 | 101, 114, 101, 110, 32, 100, 101, 99, 105, 114, 32, 113, 117, 101, 32, 116, 101, 110, 298 | 195, 173, 97, 32, 101, 108, 32, 115, 111, 98, 114, 101, 110, 111, 109, 98, 114, 101, 299 | 32, 100, 101, 32, 81, 117, 105, 106, 97, 100, 97, 32, 111, 32, 81, 117, 101, 115, 97, 300 | 100, 97, 32, 40, 113, 117, 101, 32, 101, 110, 32, 101, 115, 116, 111, 32, 104, 97, 121, 301 | 32, 97, 108, 103, 117, 110, 97, 10, 100, 105, 102, 101, 114, 101, 110, 99, 105, 97, 32, 302 | 101, 110, 32, 108, 111, 115, 32, 97, 117, 116, 111, 114, 101, 115, 32, 113, 117, 101, 303 | 32, 100, 101, 115, 116, 101, 32, 99, 97, 115, 111, 32, 101, 115, 99, 114, 105, 98, 101, 304 | 110, 41, 44, 32, 97, 117, 110, 113, 117, 101, 32, 112, 111, 114, 32, 99, 111, 110, 106, 305 | 101, 116, 117, 114, 97, 115, 32, 118, 101, 114, 111, 115, 195, 173, 109, 105, 108, 101, 306 | 115, 32, 115, 101, 32, 100, 101, 106, 97, 10, 101, 110, 116, 101, 110, 100, 101, 114, 307 | 32, 113, 117, 101, 32, 115, 101, 32, 108, 108, 97, 109, 97, 32, 81, 117, 105, 106, 97, 308 | 110, 97, 59, 32, 112, 101, 114, 111, 32, 101, 115, 116, 111, 32, 105, 109, 112, 111, 309 | 114, 116, 97, 32, 112, 111, 99, 111, 32, 97, 32, 110, 117, 101, 115, 116, 114, 111, 32, 310 | 99, 117, 101, 110, 116, 111, 59, 32, 98, 97, 115, 116, 97, 32, 113, 117, 101, 32, 101, 311 | 110, 32, 108, 97, 32, 110, 97, 114, 114, 97, 99, 105, 195, 179, 110, 10, 100, 195, 169, 312 | 108, 32, 110, 111, 32, 115, 101, 32, 115, 97, 108, 103, 97, 32, 117, 110, 32, 112, 117, 313 | 110, 116, 111, 32, 100, 101, 32, 108, 97, 32, 118, 101, 114, 100, 97, 100, 46, 314 | ]); 315 | 316 | assert_eq!(encoded, expected); 317 | 318 | codec = SimpleScriptCodec::from_data(expected); 319 | let decoded = Value::decode(&mut &mut codec).unwrap(); 320 | 321 | assert_eq!(decoded, value); 322 | } 323 | } 324 | --------------------------------------------------------------------------------