mirror of
https://github.com/fluencelabs/aquavm
synced 2025-03-15 20:40:50 +00:00
Introduce the match instruction (#60)
This commit is contained in:
parent
f523f27705
commit
7cc248439e
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -11,7 +11,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "air-parser"
|
name = "air-parser"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"codespan",
|
"codespan",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
@ -1668,7 +1668,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stepper-lib"
|
name = "stepper-lib"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"air-parser",
|
"air-parser",
|
||||||
"aqua-test-utils",
|
"aqua-test-utils",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "air-parser"
|
name = "air-parser"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
@ -13,7 +13,7 @@ pub AIR = Instr;
|
|||||||
|
|
||||||
Instr: Box<Instruction<'input>> = {
|
Instr: Box<Instruction<'input>> = {
|
||||||
"(" call <p:PeerPart> <f:FPart> <args:Args> <output:Output?> ")" => {
|
"(" call <p:PeerPart> <f:FPart> <args:Args> <output:Output?> ")" => {
|
||||||
let output = output.unwrap_or(CallOutput::None);
|
let output = output.unwrap_or(CallOutputValue::None);
|
||||||
let args = Rc::new(args);
|
let args = Rc::new(args);
|
||||||
Box::new(Instruction::Call(Call{peer_part: p, function_part: f, args, output}))
|
Box::new(Instruction::Call(Call{peer_part: p, function_part: f, args, output}))
|
||||||
},
|
},
|
||||||
@ -31,10 +31,12 @@ Instr: Box<Instruction<'input>> = {
|
|||||||
|
|
||||||
"(" xor <l:Instr> <r:Instr> ")" => Box::new(Instruction::Xor(Xor(l, r))),
|
"(" xor <l:Instr> <r:Instr> ")" => Box::new(Instruction::Xor(Xor(l, r))),
|
||||||
|
|
||||||
|
"(" match_ <l:Matchable> <r:Matchable> <i:Instr> ")" => Box::new(Instruction::Match(Match(l, r, i))),
|
||||||
|
|
||||||
! => { errors.push(<>); Box::new(Instruction::Error) },
|
! => { errors.push(<>); Box::new(Instruction::Error) },
|
||||||
}
|
}
|
||||||
|
|
||||||
Args: Vec<CallInstructionValue<'input>> = {
|
Args: Vec<CallArgValue<'input>> = {
|
||||||
"[" <args:(<Arg>)*> "]" => args
|
"[" <args:(<Arg>)*> "]" => args
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,11 +50,26 @@ PeerPart: PeerPart<'input> = {
|
|||||||
"(" <pid:PeerId> <sid:ServiceId> ")" => PeerPart::PeerPkWithServiceId(pid, sid),
|
"(" <pid:PeerId> <sid:ServiceId> ")" => PeerPart::PeerPkWithServiceId(pid, sid),
|
||||||
}
|
}
|
||||||
|
|
||||||
Output: CallOutput<'input> = {
|
Output: CallOutputValue<'input> = {
|
||||||
<s:Alphanumeric> => CallOutput::Scalar(s),
|
<s:Alphanumeric> => CallOutputValue::Scalar(s),
|
||||||
<a:Accumulator> => CallOutput::Accumulator(a),
|
<a:Accumulator> => CallOutputValue::Accumulator(a),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Function = CallArgValue;
|
||||||
|
PeerId = CallArgValue;
|
||||||
|
ServiceId = CallArgValue;
|
||||||
|
Arg = CallArgValue;
|
||||||
|
|
||||||
|
CallArgValue: CallArgValue<'input> = {
|
||||||
|
<s:Literal> => CallArgValue::Literal(s),
|
||||||
|
<s:Alphanumeric> => CallArgValue::Variable(s),
|
||||||
|
<v:JsonPath> => {
|
||||||
|
let (variable, path) = into_variable_and_path(v.0, v.1);
|
||||||
|
CallArgValue::JsonPath { variable, path }
|
||||||
|
},
|
||||||
|
InitPeerId => CallArgValue::InitPeerId,
|
||||||
|
}
|
||||||
|
|
||||||
Iterable: IterableValue<'input> = {
|
Iterable: IterableValue<'input> = {
|
||||||
<s:Alphanumeric> => IterableValue::Variable(s),
|
<s:Alphanumeric> => IterableValue::Variable(s),
|
||||||
<v:JsonPath> => {
|
<v:JsonPath> => {
|
||||||
@ -61,19 +78,13 @@ Iterable: IterableValue<'input> = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Function = CallInstructionValue;
|
Matchable: MatchableValue<'input> = {
|
||||||
PeerId = CallInstructionValue;
|
<s:Alphanumeric> => MatchableValue::Variable(s),
|
||||||
ServiceId = CallInstructionValue;
|
<s:Literal> => MatchableValue::Literal(s),
|
||||||
Arg = CallInstructionValue;
|
|
||||||
|
|
||||||
CallInstructionValue: CallInstructionValue<'input> = {
|
|
||||||
<s:Literal> => CallInstructionValue::Literal(s),
|
|
||||||
<s:Alphanumeric> => CallInstructionValue::Variable(s),
|
|
||||||
<v:JsonPath> => {
|
<v:JsonPath> => {
|
||||||
let (variable, path) = into_variable_and_path(v.0, v.1);
|
let (variable, path) = into_variable_and_path(v.0, v.1);
|
||||||
CallInstructionValue::JsonPath { variable, path }
|
MatchableValue::JsonPath { variable, path }
|
||||||
},
|
},
|
||||||
InitPeerId => CallInstructionValue::InitPeerId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
@ -100,5 +111,6 @@ extern {
|
|||||||
fold => Token::Fold,
|
fold => Token::Fold,
|
||||||
xor => Token::Xor,
|
xor => Token::Xor,
|
||||||
next => Token::Next,
|
next => Token::Next,
|
||||||
|
match_ => Token::Match,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -27,6 +27,7 @@ pub enum Instruction<'i> {
|
|||||||
Seq(Seq<'i>),
|
Seq(Seq<'i>),
|
||||||
Par(Par<'i>),
|
Par(Par<'i>),
|
||||||
Xor(Xor<'i>),
|
Xor(Xor<'i>),
|
||||||
|
Match(Match<'i>),
|
||||||
Fold(Fold<'i>),
|
Fold(Fold<'i>),
|
||||||
Next(Next<'i>),
|
Next(Next<'i>),
|
||||||
Error,
|
Error,
|
||||||
@ -34,30 +35,30 @@ pub enum Instruction<'i> {
|
|||||||
|
|
||||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
pub enum PeerPart<'i> {
|
pub enum PeerPart<'i> {
|
||||||
PeerPk(CallInstructionValue<'i>),
|
PeerPk(CallArgValue<'i>),
|
||||||
PeerPkWithServiceId(CallInstructionValue<'i>, CallInstructionValue<'i>),
|
PeerPkWithServiceId(CallArgValue<'i>, CallArgValue<'i>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
pub enum FunctionPart<'i> {
|
pub enum FunctionPart<'i> {
|
||||||
FuncName(CallInstructionValue<'i>),
|
FuncName(CallArgValue<'i>),
|
||||||
ServiceIdWithFuncName(CallInstructionValue<'i>, CallInstructionValue<'i>),
|
ServiceIdWithFuncName(CallArgValue<'i>, CallArgValue<'i>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
pub struct Call<'i> {
|
pub struct Call<'i> {
|
||||||
pub peer_part: PeerPart<'i>,
|
pub peer_part: PeerPart<'i>,
|
||||||
pub function_part: FunctionPart<'i>,
|
pub function_part: FunctionPart<'i>,
|
||||||
pub args: Rc<Vec<CallInstructionValue<'i>>>,
|
pub args: Rc<Vec<CallArgValue<'i>>>,
|
||||||
pub output: CallOutput<'i>,
|
pub output: CallOutputValue<'i>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||||
pub enum CallInstructionValue<'i> {
|
pub enum CallArgValue<'i> {
|
||||||
Variable(&'i str),
|
|
||||||
Literal(&'i str),
|
|
||||||
JsonPath { variable: &'i str, path: &'i str },
|
|
||||||
InitPeerId,
|
InitPeerId,
|
||||||
|
Literal(&'i str),
|
||||||
|
Variable(&'i str),
|
||||||
|
JsonPath { variable: &'i str, path: &'i str },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||||
@ -66,8 +67,15 @@ pub enum IterableValue<'i> {
|
|||||||
JsonPath { variable: &'i str, path: &'i str },
|
JsonPath { variable: &'i str, path: &'i str },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum MatchableValue<'i> {
|
||||||
|
Literal(&'i str),
|
||||||
|
Variable(&'i str),
|
||||||
|
JsonPath { variable: &'i str, path: &'i str },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Hash, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
pub enum CallOutput<'i> {
|
pub enum CallOutputValue<'i> {
|
||||||
Scalar(&'i str),
|
Scalar(&'i str),
|
||||||
Accumulator(&'i str),
|
Accumulator(&'i str),
|
||||||
None,
|
None,
|
||||||
@ -82,6 +90,13 @@ pub struct Par<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
|||||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
pub struct Xor<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
pub struct Xor<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Match<'i>(
|
||||||
|
pub MatchableValue<'i>,
|
||||||
|
pub MatchableValue<'i>,
|
||||||
|
pub Box<Instruction<'i>>,
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
pub struct Fold<'i> {
|
pub struct Fold<'i> {
|
||||||
pub iterable: IterableValue<'i>,
|
pub iterable: IterableValue<'i>,
|
||||||
|
@ -180,6 +180,7 @@ fn string_to_token(input: &str, start_pos: usize) -> Result<Token, LexerError> {
|
|||||||
FOLD_INSTR => Ok(Token::Fold),
|
FOLD_INSTR => Ok(Token::Fold),
|
||||||
XOR_INSTR => Ok(Token::Xor),
|
XOR_INSTR => Ok(Token::Xor),
|
||||||
NEXT_INSTR => Ok(Token::Next),
|
NEXT_INSTR => Ok(Token::Next),
|
||||||
|
MATCH_INSTR => Ok(Token::Match),
|
||||||
|
|
||||||
INIT_PEER_ID => Ok(Token::InitPeerId),
|
INIT_PEER_ID => Ok(Token::InitPeerId),
|
||||||
|
|
||||||
@ -234,6 +235,7 @@ const NULL_INSTR: &str = "null";
|
|||||||
const FOLD_INSTR: &str = "fold";
|
const FOLD_INSTR: &str = "fold";
|
||||||
const XOR_INSTR: &str = "xor";
|
const XOR_INSTR: &str = "xor";
|
||||||
const NEXT_INSTR: &str = "next";
|
const NEXT_INSTR: &str = "next";
|
||||||
|
const MATCH_INSTR: &str = "match";
|
||||||
|
|
||||||
const INIT_PEER_ID: &str = "%init_peer_id%";
|
const INIT_PEER_ID: &str = "%init_peer_id%";
|
||||||
|
|
||||||
|
@ -35,4 +35,5 @@ pub enum Token<'input> {
|
|||||||
Fold,
|
Fold,
|
||||||
Xor,
|
Xor,
|
||||||
Next,
|
Next,
|
||||||
|
Match,
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ fn parse(source_code: &str) -> Instruction {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_seq() {
|
fn parse_seq() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -59,8 +59,8 @@ fn parse_seq() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_seq_seq() {
|
fn parse_seq_seq() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -102,8 +102,8 @@ fn parse_seq_seq() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_json_path() {
|
fn parse_json_path() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -126,8 +126,8 @@ fn parse_json_path() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_json_path_complex() {
|
fn parse_json_path_complex() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -164,8 +164,8 @@ fn parse_json_path_complex() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn json_path_square_braces() {
|
fn json_path_square_braces() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -267,8 +267,8 @@ fn parse_fold_with_xor_par_seq() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_init_peer_id() {
|
fn parse_init_peer_id() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -307,8 +307,8 @@ fn parse_init_peer_id() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn seq_par_call() {
|
fn seq_par_call() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -361,8 +361,8 @@ fn seq_par_call() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn seq_with_empty_and_dash() {
|
fn seq_with_empty_and_dash() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
@ -449,8 +449,8 @@ fn seq_with_empty_and_dash() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn no_output() {
|
fn no_output() {
|
||||||
use ast::Call;
|
use ast::Call;
|
||||||
use ast::CallInstructionValue::*;
|
use ast::CallArgValue::*;
|
||||||
use ast::CallOutput::*;
|
use ast::CallOutputValue::*;
|
||||||
use ast::FunctionPart::*;
|
use ast::FunctionPart::*;
|
||||||
use ast::PeerPart::*;
|
use ast::PeerPart::*;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "stepper-lib"
|
name = "stepper-lib"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ use crate::JValue;
|
|||||||
use crate::ResolvedTriplet;
|
use crate::ResolvedTriplet;
|
||||||
use crate::SecurityTetraplet;
|
use crate::SecurityTetraplet;
|
||||||
|
|
||||||
use air_parser::ast::{CallInstructionValue, CallOutput};
|
use air_parser::ast::{CallArgValue, CallOutputValue};
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -37,8 +37,8 @@ use std::rc::Rc;
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub(super) struct ResolvedCall<'i> {
|
pub(super) struct ResolvedCall<'i> {
|
||||||
triplet: Rc<ResolvedTriplet>,
|
triplet: Rc<ResolvedTriplet>,
|
||||||
function_arg_paths: Rc<Vec<CallInstructionValue<'i>>>,
|
function_arg_paths: Rc<Vec<CallArgValue<'i>>>,
|
||||||
output: CallOutput<'i>,
|
output: CallOutputValue<'i>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
@ -19,15 +19,15 @@ use super::ExecutionError;
|
|||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
|
|
||||||
use air_parser::ast::{CallInstructionValue, FunctionPart, PeerPart};
|
use air_parser::ast::{CallArgValue, FunctionPart, PeerPart};
|
||||||
use polyplets::ResolvedTriplet;
|
use polyplets::ResolvedTriplet;
|
||||||
|
|
||||||
/// Triplet represents a location of the executable code in the network.
|
/// Triplet represents a location of the executable code in the network.
|
||||||
/// It is build from `PeerPart` and `FunctionPart` of a `Call` instruction.
|
/// It is build from `PeerPart` and `FunctionPart` of a `Call` instruction.
|
||||||
pub(super) struct Triplet<'a, 'i> {
|
pub(super) struct Triplet<'a, 'i> {
|
||||||
pub(super) peer_pk: &'a CallInstructionValue<'i>,
|
pub(super) peer_pk: &'a CallArgValue<'i>,
|
||||||
pub(super) service_id: &'a CallInstructionValue<'i>,
|
pub(super) service_id: &'a CallArgValue<'i>,
|
||||||
pub(super) function_name: &'a CallInstructionValue<'i>,
|
pub(super) function_name: &'a CallArgValue<'i>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> Triplet<'a, 'i> {
|
impl<'a, 'i> Triplet<'a, 'i> {
|
||||||
@ -77,18 +77,18 @@ impl<'a, 'i> Triplet<'a, 'i> {
|
|||||||
|
|
||||||
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
||||||
// TODO: return Rc<String> to avoid excess cloning
|
// TODO: return Rc<String> to avoid excess cloning
|
||||||
fn resolve_to_string<'i>(value: &CallInstructionValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
fn resolve_to_string<'i>(value: &CallArgValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
||||||
use crate::execution::utils::resolve_to_jvaluable;
|
use crate::execution::utils::resolve_to_jvaluable;
|
||||||
|
|
||||||
let resolved = match value {
|
let resolved = match value {
|
||||||
CallInstructionValue::InitPeerId => ctx.init_peer_id.clone(),
|
CallArgValue::InitPeerId => ctx.init_peer_id.clone(),
|
||||||
CallInstructionValue::Literal(value) => value.to_string(),
|
CallArgValue::Literal(value) => value.to_string(),
|
||||||
CallInstructionValue::Variable(name) => {
|
CallArgValue::Variable(name) => {
|
||||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||||
let jvalue = resolved.into_jvalue();
|
let jvalue = resolved.into_jvalue();
|
||||||
jvalue_to_string(jvalue)?
|
jvalue_to_string(jvalue)?
|
||||||
}
|
}
|
||||||
CallInstructionValue::JsonPath { variable, path } => {
|
CallArgValue::JsonPath { variable, path } => {
|
||||||
let resolved = resolve_to_jvaluable(variable, ctx)?;
|
let resolved = resolve_to_jvaluable(variable, ctx)?;
|
||||||
let resolved = resolved.apply_json_path(path)?;
|
let resolved = resolved.apply_json_path(path)?;
|
||||||
vec_to_string(resolved, path)?
|
vec_to_string(resolved, path)?
|
||||||
|
@ -22,7 +22,7 @@ use crate::contexts::execution_trace::*;
|
|||||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
|
|
||||||
use air_parser::ast::CallOutput;
|
use air_parser::ast::CallOutputValue;
|
||||||
use polyplets::ResolvedTriplet;
|
use polyplets::ResolvedTriplet;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -31,7 +31,7 @@ use std::rc::Rc;
|
|||||||
pub(super) fn set_local_call_result<'i>(
|
pub(super) fn set_local_call_result<'i>(
|
||||||
result: Rc<JValue>,
|
result: Rc<JValue>,
|
||||||
triplet: Rc<ResolvedTriplet>,
|
triplet: Rc<ResolvedTriplet>,
|
||||||
output: &CallOutput<'i>,
|
output: &CallOutputValue<'i>,
|
||||||
exec_ctx: &mut ExecutionCtx<'i>,
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
) -> ExecutionResult<()> {
|
) -> ExecutionResult<()> {
|
||||||
use crate::contexts::execution::AValue;
|
use crate::contexts::execution::AValue;
|
||||||
@ -42,7 +42,7 @@ pub(super) fn set_local_call_result<'i>(
|
|||||||
let executed_result = ResolvedCallResult { result, triplet };
|
let executed_result = ResolvedCallResult { result, triplet };
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
CallOutput::Scalar(name) => {
|
CallOutputValue::Scalar(name) => {
|
||||||
if let Some(fold_block_name) = exec_ctx.met_folds.back() {
|
if let Some(fold_block_name) = exec_ctx.met_folds.back() {
|
||||||
let fold_state = match exec_ctx.data_cache.get_mut(*fold_block_name) {
|
let fold_state = match exec_ctx.data_cache.get_mut(*fold_block_name) {
|
||||||
Some(AValue::JValueFoldCursor(fold_state)) => fold_state,
|
Some(AValue::JValueFoldCursor(fold_state)) => fold_state,
|
||||||
@ -73,7 +73,7 @@ pub(super) fn set_local_call_result<'i>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
CallOutput::Accumulator(name) => {
|
CallOutputValue::Accumulator(name) => {
|
||||||
match exec_ctx.data_cache.entry(name.to_string()) {
|
match exec_ctx.data_cache.entry(name.to_string()) {
|
||||||
Occupied(mut entry) => match entry.get_mut() {
|
Occupied(mut entry) => match entry.get_mut() {
|
||||||
// if result is an array, insert result to the end of the array
|
// if result is an array, insert result to the end of the array
|
||||||
@ -85,7 +85,7 @@ pub(super) fn set_local_call_result<'i>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
CallOutput::None => {}
|
CallOutputValue::None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -113,7 +113,7 @@ pub(super) fn set_remote_call_result<'i>(
|
|||||||
/// and returns Ok(true) if the call should be executed further.
|
/// and returns Ok(true) if the call should be executed further.
|
||||||
pub(super) fn handle_prev_state<'i>(
|
pub(super) fn handle_prev_state<'i>(
|
||||||
triplet: &Rc<ResolvedTriplet>,
|
triplet: &Rc<ResolvedTriplet>,
|
||||||
output: &CallOutput<'i>,
|
output: &CallOutputValue<'i>,
|
||||||
prev_state: ExecutedState,
|
prev_state: ExecutedState,
|
||||||
exec_ctx: &mut ExecutionCtx<'i>,
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
trace_ctx: &mut ExecutionTraceCtx,
|
trace_ctx: &mut ExecutionTraceCtx,
|
||||||
|
192
stepper-lib/src/execution/air/match_.rs
Normal file
192
stepper-lib/src/execution/air/match_.rs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use super::ExecutionCtx;
|
||||||
|
use super::ExecutionError;
|
||||||
|
use super::ExecutionResult;
|
||||||
|
use super::ExecutionTraceCtx;
|
||||||
|
use crate::log_instruction;
|
||||||
|
|
||||||
|
use air_parser::ast::Match;
|
||||||
|
use air_parser::ast::MatchableValue;
|
||||||
|
|
||||||
|
impl<'i> super::ExecutableInstruction<'i> for Match<'i> {
|
||||||
|
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||||
|
log_instruction!(match_, exec_ctx, trace_ctx);
|
||||||
|
|
||||||
|
let left_value = &self.0;
|
||||||
|
let right_value = &self.1;
|
||||||
|
let is_equal_values = compare_matchable(left_value, right_value, exec_ctx)?;
|
||||||
|
|
||||||
|
if !is_equal_values {
|
||||||
|
return Err(ExecutionError::MatchWithoutXorError);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.2.execute(exec_ctx, trace_ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_matchable<'ctx>(
|
||||||
|
left: &MatchableValue<'_>,
|
||||||
|
right: &MatchableValue<'_>,
|
||||||
|
exec_ctx: &'ctx ExecutionCtx<'_>,
|
||||||
|
) -> ExecutionResult<bool> {
|
||||||
|
use crate::execution::utils::resolve_to_jvaluable;
|
||||||
|
use MatchableValue::*;
|
||||||
|
|
||||||
|
match (left, right) {
|
||||||
|
(Literal(left_name), Literal(right_name)) => Ok(left_name == right_name),
|
||||||
|
(Variable(left_name), Variable(right_name)) => {
|
||||||
|
let left_jvaluable = resolve_to_jvaluable(left_name, exec_ctx)?;
|
||||||
|
let left_value = left_jvaluable.as_jvalue();
|
||||||
|
|
||||||
|
let right_jvaluable = resolve_to_jvaluable(right_name, exec_ctx)?;
|
||||||
|
let right_value = right_jvaluable.as_jvalue();
|
||||||
|
|
||||||
|
Ok(left_value == right_value)
|
||||||
|
}
|
||||||
|
(JsonPath { variable: lv, path: lp }, JsonPath { variable: rv, path: rp }) => {
|
||||||
|
let left_jvaluable = resolve_to_jvaluable(lv, exec_ctx)?;
|
||||||
|
let left_value = left_jvaluable.apply_json_path(lp)?;
|
||||||
|
|
||||||
|
let right_jvaluable = resolve_to_jvaluable(rv, exec_ctx)?;
|
||||||
|
let right_value = right_jvaluable.apply_json_path(rp)?;
|
||||||
|
|
||||||
|
Ok(left_value == right_value)
|
||||||
|
}
|
||||||
|
_ => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::contexts::execution_trace::ExecutionTrace;
|
||||||
|
use crate::JValue;
|
||||||
|
|
||||||
|
use aqua_test_utils::call_vm;
|
||||||
|
use aqua_test_utils::create_aqua_vm;
|
||||||
|
use aqua_test_utils::echo_string_call_service;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_equal() {
|
||||||
|
use crate::contexts::execution_trace::CallResult::*;
|
||||||
|
use crate::contexts::execution_trace::ExecutedState::*;
|
||||||
|
|
||||||
|
let set_variable_peer_id = "set_variable_peer_id";
|
||||||
|
let mut set_variable_vm = create_aqua_vm(echo_string_call_service(), set_variable_peer_id);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut vm = create_aqua_vm(echo_string_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{0}" ("" "") ["value_1"] value_1)
|
||||||
|
(call "{0}" ("" "") ["value_1"] value_2)
|
||||||
|
)
|
||||||
|
(xor
|
||||||
|
(match value_1 value_2
|
||||||
|
(call "{1}" ("service_id_2" "local_fn_name") ["result_1"] result_1)
|
||||||
|
)
|
||||||
|
(call "{1}" ("service_id_2" "local_fn_name") ["result_2"] result_2)
|
||||||
|
)
|
||||||
|
)"#,
|
||||||
|
set_variable_peer_id, local_peer_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||||
|
let res = call_vm!(vm, "asd", script, "", res.data);
|
||||||
|
|
||||||
|
let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json");
|
||||||
|
let expected_executed_call_result = Call(Executed(Rc::new(JValue::String(String::from("result_1")))));
|
||||||
|
|
||||||
|
assert_eq!(actual_trace.len(), 3);
|
||||||
|
assert_eq!(actual_trace[2], expected_executed_call_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_not_equal() {
|
||||||
|
use crate::contexts::execution_trace::CallResult::*;
|
||||||
|
use crate::contexts::execution_trace::ExecutedState::*;
|
||||||
|
|
||||||
|
let set_variable_peer_id = "set_variable_peer_id";
|
||||||
|
let mut set_variable_vm = create_aqua_vm(echo_string_call_service(), set_variable_peer_id);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut vm = create_aqua_vm(echo_string_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{0}" ("" "") ["value_1"] value_1)
|
||||||
|
(call "{0}" ("" "") ["value_2"] value_2)
|
||||||
|
)
|
||||||
|
(xor
|
||||||
|
(match value_1 value_2
|
||||||
|
(call "{1}" ("service_id_2" "local_fn_name") ["result_1"] result_1)
|
||||||
|
)
|
||||||
|
(call "{1}" ("service_id_2" "local_fn_name") ["result_2"] result_2)
|
||||||
|
)
|
||||||
|
)"#,
|
||||||
|
set_variable_peer_id, local_peer_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||||
|
let res = call_vm!(vm, "asd", script, "", res.data);
|
||||||
|
|
||||||
|
let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json");
|
||||||
|
let expected_executed_call_result = Call(Executed(Rc::new(JValue::String(String::from("result_2")))));
|
||||||
|
|
||||||
|
assert_eq!(actual_trace.len(), 3);
|
||||||
|
assert_eq!(actual_trace[2], expected_executed_call_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn match_without_xor() {
|
||||||
|
let set_variable_peer_id = "set_variable_peer_id";
|
||||||
|
let mut set_variable_vm = create_aqua_vm(echo_string_call_service(), set_variable_peer_id);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut vm = create_aqua_vm(echo_string_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{0}" ("" "") ["value_1"] value_1)
|
||||||
|
(call "{0}" ("" "") ["value_2"] value_2)
|
||||||
|
)
|
||||||
|
(match value_1 value_2
|
||||||
|
(call "{1}" ("service_id_2" "local_fn_name") ["result_1"] result_1)
|
||||||
|
)
|
||||||
|
)"#,
|
||||||
|
set_variable_peer_id, local_peer_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||||
|
let res = call_vm!(vm, "asd", script.clone(), "", res.data);
|
||||||
|
|
||||||
|
assert_eq!(res.ret_code, 1015);
|
||||||
|
|
||||||
|
let res = call_vm!(vm, "asd", script, "", res.data);
|
||||||
|
|
||||||
|
assert_eq!(res.ret_code, 1015);
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
mod call;
|
mod call;
|
||||||
mod fold;
|
mod fold;
|
||||||
|
mod match_;
|
||||||
mod null;
|
mod null;
|
||||||
mod par;
|
mod par;
|
||||||
mod seq;
|
mod seq;
|
||||||
@ -44,7 +45,8 @@ impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
|||||||
Instruction::Par(par) => par.execute(exec_ctx, trace_ctx),
|
Instruction::Par(par) => par.execute(exec_ctx, trace_ctx),
|
||||||
Instruction::Seq(seq) => seq.execute(exec_ctx, trace_ctx),
|
Instruction::Seq(seq) => seq.execute(exec_ctx, trace_ctx),
|
||||||
Instruction::Xor(xor) => xor.execute(exec_ctx, trace_ctx),
|
Instruction::Xor(xor) => xor.execute(exec_ctx, trace_ctx),
|
||||||
Instruction::Error => unreachable!("should not execute if parsing failed. QED."),
|
Instruction::Match(match_) => match_.execute(exec_ctx, trace_ctx),
|
||||||
|
Instruction::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,10 @@ pub(crate) enum ExecutionError {
|
|||||||
/// Errors encountered while shadowing non-scalar values.
|
/// Errors encountered while shadowing non-scalar values.
|
||||||
#[error("variable with name '{0}' can't be shadowed, shadowing is supported only for scalar values")]
|
#[error("variable with name '{0}' can't be shadowed, shadowing is supported only for scalar values")]
|
||||||
ShadowingError(String),
|
ShadowingError(String),
|
||||||
|
|
||||||
|
/// This error type is produced by a match to notify xor that compared values aren't equal.
|
||||||
|
#[error("match is used without corresponding xor")]
|
||||||
|
MatchWithoutXorError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecutionError {
|
impl ExecutionError {
|
||||||
@ -102,6 +106,7 @@ impl ExecutionError {
|
|||||||
MultipleFoldStates(_) => 12,
|
MultipleFoldStates(_) => 12,
|
||||||
InvalidExecutedState(..) => 13,
|
InvalidExecutedState(..) => 13,
|
||||||
ShadowingError(_) => 14,
|
ShadowingError(_) => 14,
|
||||||
|
MatchWithoutXorError => 15,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ use crate::execution::ExecutionResult;
|
|||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::SecurityTetraplet;
|
use crate::SecurityTetraplet;
|
||||||
|
|
||||||
use air_parser::ast::CallInstructionValue;
|
use air_parser::ast::CallArgValue;
|
||||||
|
|
||||||
/// Resolve value to called function arguments.
|
/// Resolve value to called function arguments.
|
||||||
pub(crate) fn resolve_to_args<'i>(
|
pub(crate) fn resolve_to_args<'i>(
|
||||||
value: &CallInstructionValue<'i>,
|
value: &CallArgValue<'i>,
|
||||||
ctx: &ExecutionCtx<'i>,
|
ctx: &ExecutionCtx<'i>,
|
||||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||||
fn handle_string_arg<'i>(arg: &str, ctx: &ExecutionCtx<'i>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
fn handle_string_arg<'i>(arg: &str, ctx: &ExecutionCtx<'i>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||||
@ -37,16 +37,16 @@ pub(crate) fn resolve_to_args<'i>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
CallInstructionValue::InitPeerId => handle_string_arg(ctx.init_peer_id.as_str(), ctx),
|
CallArgValue::InitPeerId => handle_string_arg(ctx.init_peer_id.as_str(), ctx),
|
||||||
CallInstructionValue::Literal(value) => handle_string_arg(value, ctx),
|
CallArgValue::Literal(value) => handle_string_arg(value, ctx),
|
||||||
CallInstructionValue::Variable(name) => {
|
CallArgValue::Variable(name) => {
|
||||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||||
let tetraplets = resolved.as_tetraplets();
|
let tetraplets = resolved.as_tetraplets();
|
||||||
let jvalue = resolved.into_jvalue();
|
let jvalue = resolved.into_jvalue();
|
||||||
|
|
||||||
Ok((jvalue, tetraplets))
|
Ok((jvalue, tetraplets))
|
||||||
}
|
}
|
||||||
CallInstructionValue::JsonPath { variable, path } => {
|
CallArgValue::JsonPath { variable, path } => {
|
||||||
let resolved = resolve_to_jvaluable(variable, ctx)?;
|
let resolved = resolve_to_jvaluable(variable, ctx)?;
|
||||||
let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(path)?;
|
let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(path)?;
|
||||||
let jvalue = jvalue.into_iter().cloned().collect::<Vec<_>>();
|
let jvalue = jvalue.into_iter().cloned().collect::<Vec<_>>();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user