Lev
2 years ago
11 changed files with 302 additions and 137 deletions
@ -0,0 +1,9 @@
|
||||
[package] |
||||
name = "agolang" |
||||
version = "0.1.0" |
||||
edition = "2021" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
serde = { version = "1.0", features = ["derive"] } |
@ -0,0 +1,88 @@
|
||||
use serde::{Deserialize, Serialize}; |
||||
use crate::value::Value; |
||||
|
||||
/// Variables for matching.
|
||||
pub mod value; |
||||
/// Templates for types.
|
||||
pub mod templates; |
||||
|
||||
/// The Error type
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub enum AgolangError { |
||||
/// The given value is not of the expected type.
|
||||
TypeError(String), |
||||
/// The given value(s) could not be matched.
|
||||
ValueError(String), |
||||
/// The code is not valid.
|
||||
CodeError(String), |
||||
} |
||||
|
||||
/// Code - a function that works on the variables. For now, the output is fixed.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub struct Code { |
||||
/// Output
|
||||
pub output: Vec<Value>, |
||||
} |
||||
|
||||
/// Function that takes a set of variables, matches them and returns a set of variables.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub struct Function { |
||||
/// Input variables.
|
||||
pub input: Vec<value::Type>, |
||||
/// Output variables.
|
||||
pub output: Vec<value::Type>, |
||||
/// Code to run depending on the template.
|
||||
pub code: Vec<(templates::ComplexMatcher, Code)>, |
||||
} |
||||
|
||||
impl Code { |
||||
/// Check if all the types add up.
|
||||
pub fn check_correctness(&self) -> bool { |
||||
true |
||||
} |
||||
|
||||
/// Run the code and get the output.
|
||||
pub fn eval(&self, _variables: &[Value]) -> Vec<Value> { |
||||
self.output.clone() |
||||
} |
||||
} |
||||
|
||||
impl Function { |
||||
/// Check if all the types add up.
|
||||
pub fn check_correctness(&self) -> bool { |
||||
for c in &self.code { |
||||
if !c.0.check_correctness_for_types(&self.input) { |
||||
return false; |
||||
} |
||||
if !c.1.check_correctness() { |
||||
return false; |
||||
} |
||||
if !c.1.output.iter().zip(self.output.iter()).all(|(o, t)| o.var_type() == *t) { |
||||
return false; |
||||
} |
||||
} |
||||
true |
||||
} |
||||
|
||||
/// Get matcher for the whole input.
|
||||
pub fn get_total_matcher(&self) -> templates::ComplexMatcher { |
||||
templates::ComplexMatcher::Or(self.code.iter().map(|c| c.0.clone()).collect()) |
||||
} |
||||
|
||||
/// Evaluate the function on the given values.
|
||||
pub fn evaluate(&self, values: &[Value]) -> Result<Vec<Value>, AgolangError> { |
||||
if !self.check_correctness() { |
||||
return Err(AgolangError::CodeError("Function is not valid".to_string())); |
||||
} |
||||
for c in &self.code { |
||||
if c.0.match_values(values) { |
||||
return Ok(c.1.eval(values)); |
||||
} |
||||
} |
||||
Err(AgolangError::CodeError("Function could not be matched".to_string())) |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
} |
@ -0,0 +1,132 @@
|
||||
use serde::{Deserialize, Serialize}; |
||||
use crate::value::{Type, Value}; |
||||
|
||||
/// Matchers of values
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub enum Matcher { |
||||
Any, |
||||
Fixed(Value), |
||||
NotEqual(Value), |
||||
Whitelist(Vec<Value>), |
||||
Blacklist(Vec<Value>), |
||||
IntRange(i64, i64), |
||||
Regexp(String), |
||||
} |
||||
|
||||
impl Matcher { |
||||
/// Check if this is a valid matcher for a given type.
|
||||
pub fn check_correctness_for_type(&self, type_: Type) -> bool { |
||||
match self { |
||||
Matcher::Any => true, |
||||
Matcher::Fixed(v) => v.var_type() == type_, |
||||
Matcher::NotEqual(v) => v.var_type() == type_, |
||||
Matcher::Whitelist(v) | Matcher::Blacklist(v) => { |
||||
if v.is_empty() { |
||||
return false; |
||||
} |
||||
// Check that all values are of the same type and that there are no duplicates.
|
||||
if v.iter().any(|v| v.var_type() != type_) { |
||||
return false; |
||||
} |
||||
let mut v_clone = v.clone(); |
||||
v_clone.dedup(); |
||||
v_clone.len() == v.len() |
||||
}, |
||||
Matcher::IntRange(i1, i2) => i1 <= i2 && type_ == Type::Int, |
||||
Matcher::Regexp(_) => type_ == Type::String, |
||||
} |
||||
} |
||||
|
||||
/// Match a value
|
||||
pub fn match_value(&self, value: &Value) -> bool { |
||||
if !self.check_correctness_for_type(value.var_type()) { |
||||
return false; |
||||
} |
||||
match self { |
||||
Matcher::Any => true, |
||||
Matcher::Fixed(v) => v == value, |
||||
Matcher::NotEqual(v) => v != value, |
||||
Matcher::Whitelist(v) => v.contains(value), |
||||
Matcher::Blacklist(v) => !v.contains(value), |
||||
Matcher::IntRange(i1, i2) => match value { |
||||
Value::Int(i) => i >= i1 && i <= i2, |
||||
_ => false, |
||||
}, |
||||
Matcher::Regexp(_regexp) => match value { |
||||
Value::String(_s) => todo!(), |
||||
_ => false, |
||||
}, |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/// **The Complex Matcher**
|
||||
///
|
||||
/// It works on lists of variables and can be composed of other (primitive) matchers.
|
||||
/// The length of the input list will be called the arity of the matcher.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub enum ComplexMatcher { |
||||
/// Checks one value using a primitive matcher. The arity of the matcher is 1.
|
||||
Primitive(Matcher), |
||||
/// Checks if the variables match all matchers. The arity of all matchers is the same.
|
||||
And(Vec<ComplexMatcher>), |
||||
/// Checks if the variables match any matchers. The arity of all matchers is the same.
|
||||
Or(Vec<ComplexMatcher>), |
||||
/// Checks the first variables (as many as possible) using the first matcher, then the second variables (as many as possible) using the second matcher, etc.
|
||||
/// The arity of this matcher is the sum of the arities of all matchers.
|
||||
Compose(Vec<ComplexMatcher>), |
||||
} |
||||
|
||||
impl ComplexMatcher { |
||||
/// Get the arity of the matcher.
|
||||
pub fn arity(&self) -> usize { |
||||
match self { |
||||
ComplexMatcher::Primitive(_m) => 1, |
||||
ComplexMatcher::And(m) => m[0].arity(), |
||||
ComplexMatcher::Or(m) => m[0].arity(), |
||||
ComplexMatcher::Compose(m) => m.iter().map(|m| m.arity()).sum(), |
||||
} |
||||
} |
||||
|
||||
/// Check if this is a valid matcher for the given types.
|
||||
pub fn check_correctness_for_types(&self, types: &[Type]) -> bool { |
||||
match self { |
||||
ComplexMatcher::Primitive(m) => types.len() == 1 && m.check_correctness_for_type(types[0]), |
||||
ComplexMatcher::And(ms) => ms.iter().all(|m| m.check_correctness_for_types(types)), |
||||
ComplexMatcher::Or(ms) => ms.iter().any(|m| m.check_correctness_for_types(types)), |
||||
ComplexMatcher::Compose(ms) => { |
||||
let mut sum = 0; |
||||
for m in ms { |
||||
if !m.check_correctness_for_types(&types[sum..sum + m.arity()]) { |
||||
return false; |
||||
} |
||||
sum += m.arity(); |
||||
} |
||||
sum == types.len() |
||||
}, |
||||
} |
||||
} |
||||
|
||||
/// Check a list of values against this matcher.
|
||||
pub fn match_values(&self, values: &[Value]) -> bool { |
||||
if !self.check_correctness_for_types(&values.iter().map(|v| v.var_type()).collect::<Vec<_>>()) { |
||||
return false; |
||||
} |
||||
match self { |
||||
ComplexMatcher::Primitive(m) => m.match_value(&values[0]), |
||||
ComplexMatcher::And(ms) => ms.iter().all(|m| m.match_values(values)), |
||||
ComplexMatcher::Or(ms) => ms.iter().any(|m| m.match_values(values)), |
||||
ComplexMatcher::Compose(ms) => { |
||||
let mut sum = 0; |
||||
for m in ms { |
||||
if !m.match_values(&values[sum..sum + m.arity()]) { |
||||
return false; |
||||
} |
||||
sum += m.arity(); |
||||
} |
||||
sum == values.len() |
||||
}, |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,69 @@
|
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
/// Values used for variables in templates.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub enum Value { |
||||
Int(i64), |
||||
Bool(bool), |
||||
Address(Address), |
||||
Token(Address), |
||||
Data(Vec<u8>), |
||||
String(String), |
||||
} |
||||
|
||||
/// Types for variable values.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] |
||||
pub enum Type { |
||||
Int, |
||||
Bool, |
||||
Address, |
||||
Token, |
||||
Data, |
||||
String, |
||||
} |
||||
|
||||
impl Value { |
||||
/// Returns the type of the value.
|
||||
pub fn var_type(&self) -> Type { |
||||
match self { |
||||
Value::Int(_) => Type::Int, |
||||
Value::Bool(_) => Type::Bool, |
||||
Value::Address(_) => Type::Address, |
||||
Value::Token(_) => Type::Token, |
||||
Value::Data(_) => Type::Data, |
||||
Value::String(_) => Type::String, |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// For now, only TON addresses are supported.
|
||||
/// See [here](https://ton.org/docs/#/howto/step-by-step) for docs.
|
||||
/// Later, multiple blockchains will be added.
|
||||
/// Also, another networks might be added to TON as separate workchains, thus they will be supported automatically.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub struct Address { |
||||
/// Workchain id.
|
||||
pub workchain: i32, |
||||
/// The address inside the workchain (64-512 bits depending on the workchain).
|
||||
/// `address.0` is the length in bytes, the rest is the address.
|
||||
pub address: (u8, [u8; 32], [u8; 32]), |
||||
} |
||||
|
||||
impl Address { |
||||
/// Creates a new address.
|
||||
pub fn new(workchain: i32, address_vec: Vec<u8>) -> Self { |
||||
let address_len = address_vec.len(); |
||||
let address_bytes = address_vec.as_slice(); |
||||
Address { |
||||
workchain, |
||||
address: (address_len as u8, address_bytes[0..32].try_into().unwrap(), address_bytes[32..64].try_into().unwrap()), |
||||
} |
||||
} |
||||
|
||||
/// Get address in the workchain as bytes.
|
||||
pub fn address_as_bytes(&self) -> Vec<u8> { |
||||
let mut address = self.address.1[..self.address.0.min(32) as usize].to_vec(); |
||||
address.extend_from_slice(&self.address.2[..(self.address.0 as i16 - 32).max(0) as usize]); |
||||
address |
||||
} |
||||
} |
@ -1,61 +0,0 @@
|
||||
use serde::{Deserialize, Serialize}; |
||||
use crate::value::{Type, Value}; |
||||
|
||||
/// Matchers of values
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub enum Matcher { |
||||
Any, |
||||
Fixed(Value), |
||||
NotEqual(Value), |
||||
Whitelist(Vec<Value>), |
||||
Blacklist(Vec<Value>), |
||||
IntRange(i64, i64), |
||||
Regexp(String), |
||||
} |
||||
|
||||
impl Matcher { |
||||
/// Check if this is a valid matcher for a given type.
|
||||
pub fn check_correctness_for_type(&self, type_: Type) -> bool { |
||||
match self { |
||||
Matcher::Any => true, |
||||
Matcher::Fixed(v) => v.var_type() == type_, |
||||
Matcher::NotEqual(v) => v.var_type() == type_, |
||||
Matcher::Whitelist(v) | Matcher::Blacklist(v) => { |
||||
if v.is_empty() { |
||||
return false; |
||||
} |
||||
// Check that all values are of the same type and that there are no duplicates.
|
||||
if v.iter().any(|v| v.var_type() != type_) { |
||||
return false; |
||||
} |
||||
let mut v_clone = v.clone(); |
||||
v_clone.dedup(); |
||||
v_clone.len() == v.len() |
||||
}, |
||||
Matcher::IntRange(i1, i2) => i1 <= i2 && type_ == Type::Int, |
||||
Matcher::Regexp(_) => type_ == Type::String, |
||||
} |
||||
} |
||||
|
||||
/// Match a value
|
||||
pub fn match_value(&self, value: &Value) -> bool { |
||||
if !self.check_correctness_for_type(value.var_type()) { |
||||
return false; |
||||
} |
||||
match self { |
||||
Matcher::Any => true, |
||||
Matcher::Fixed(v) => v == value, |
||||
Matcher::NotEqual(v) => v != value, |
||||
Matcher::Whitelist(v) => v.contains(value), |
||||
Matcher::Blacklist(v) => !v.contains(value), |
||||
Matcher::IntRange(i1, i2) => match value { |
||||
Value::Int(i) => i >= i1 && i <= i2, |
||||
_ => false, |
||||
}, |
||||
Matcher::Regexp(_regexp) => match value { |
||||
Value::String(_s) => todo!(), |
||||
_ => false, |
||||
}, |
||||
} |
||||
} |
||||
} |
@ -1,38 +0,0 @@
|
||||
use crate::message::Address; |
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
/// Values used for variables in templates.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] |
||||
pub enum Value { |
||||
Int(i64), |
||||
Bool(bool), |
||||
Address(Address), |
||||
Token(Address), |
||||
Data(Vec<u8>), |
||||
String(String), |
||||
} |
||||
|
||||
/// Types for variable values.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] |
||||
pub enum Type { |
||||
Int, |
||||
Bool, |
||||
Address, |
||||
Token, |
||||
Data, |
||||
String, |
||||
} |
||||
|
||||
impl Value { |
||||
/// Returns the type of the value.
|
||||
pub fn var_type(&self) -> Type { |
||||
match self { |
||||
Value::Int(_) => Type::Int, |
||||
Value::Bool(_) => Type::Bool, |
||||
Value::Address(_) => Type::Address, |
||||
Value::Token(_) => Type::Token, |
||||
Value::Data(_) => Type::Data, |
||||
Value::String(_) => Type::String, |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue