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