mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 15:20:16 +00:00
Introduce restriction operator for streams (#172)
This commit is contained in:
parent
9f47eb9b83
commit
5cd45385b4
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,3 +1,17 @@
|
||||
## Version 0.17.0 (2021-11-24)
|
||||
|
||||
[PR 172](https://github.com/fluencelabs/aquavm/pull/172):
|
||||
A new instruction intended to restrict a scope of variables was introduced to AquaVM.
|
||||
|
||||
[PR 168](https://github.com/fluencelabs/aquavm/pull/168):
|
||||
AIR parser and AST was highly refactored to be more suitable to the scalar/stream restriction scheme used in AIR instructions.
|
||||
|
||||
[PR 164](https://github.com/fluencelabs/aquavm/pull/164):
|
||||
SecurityTetraplet was decoupled with marine-rs-sdk to have the only one definition in AquaVM that then exported by marine-rs-sdk.
|
||||
|
||||
[PR 162](https://github.com/fluencelabs/aquavm/pull/162):
|
||||
The scalar scoping scheme was improved in order to support more than two scope levels.
|
||||
|
||||
## Version 0.16.0 (2021-10-18)
|
||||
|
||||
[PR 154](https://github.com/fluencelabs/aquavm/pull/154)
|
||||
|
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -13,7 +13,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "air"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
dependencies = [
|
||||
"air-execution-info-collector",
|
||||
"air-interpreter-data",
|
||||
@ -49,7 +49,7 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "air-interpreter"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
dependencies = [
|
||||
"air",
|
||||
"air-log-targets",
|
||||
@ -62,7 +62,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "air-interpreter-data"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"semver 1.0.4",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air-interpreter"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
description = "Crate-wrapper for air"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air"
|
||||
version = "0.16.0"
|
||||
version = "0.17.0"
|
||||
description = "Interpreter of AIR scripts intended to coordinate request flow in the Fluence network"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
@ -30,6 +30,7 @@ serde = { version = "1.0.118", features = [ "derive", "rc" ] }
|
||||
serde_json = "1.0.61"
|
||||
|
||||
boolinator = "2.4.0"
|
||||
maplit = "1.0.2"
|
||||
log = "0.4.11"
|
||||
thiserror = "1.0.23"
|
||||
strum = "0.21"
|
||||
@ -46,7 +47,6 @@ criterion = "0.3.3"
|
||||
csv = "1.1.5"
|
||||
once_cell = "1.4.1"
|
||||
env_logger = "0.7.1"
|
||||
maplit = "1.0.2"
|
||||
pretty_assertions = "0.6.1"
|
||||
serde_json = "1.0.61"
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
mod apply_to_arguments;
|
||||
mod utils;
|
||||
|
||||
use super::call::call_result_setter::set_stream_result;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
@ -75,7 +74,10 @@ fn save_result<'ctx>(
|
||||
Scalar(scalar) => exec_ctx.scalars.set_value(scalar.name, result).map(|_| ()),
|
||||
Stream(stream) => {
|
||||
let generation = ap_result_to_generation(merger_ap_result);
|
||||
set_stream_result(result, generation, stream.name.to_string(), exec_ctx).map(|_| ())
|
||||
exec_ctx
|
||||
.streams
|
||||
.add_stream_value(result, generation, stream.name, stream.position)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ fn apply_scalar(
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
// TODO: refactor this code after boxed value
|
||||
match &scalar.lambda {
|
||||
Some(lambda) => apply_scalar_wl_impl(scalar.name, lambda, exec_ctx, trace_ctx),
|
||||
Some(lambda) => apply_scalar_wl_impl(scalar.name, scalar.position, lambda, exec_ctx, trace_ctx),
|
||||
None => apply_scalar_impl(scalar.name, exec_ctx, trace_ctx, should_touch_trace),
|
||||
}
|
||||
}
|
||||
@ -104,11 +104,12 @@ fn apply_scalar_impl(
|
||||
|
||||
fn apply_scalar_wl_impl(
|
||||
scalar_name: &str,
|
||||
position: usize,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
let variable = Variable::scalar(scalar_name);
|
||||
let variable = Variable::scalar(scalar_name, position);
|
||||
let (jvalue, mut tetraplets) = apply_lambda(variable, lambda, exec_ctx)?;
|
||||
|
||||
let tetraplet = tetraplets
|
||||
|
@ -80,7 +80,7 @@ fn variable_to_generations(variable: &ast::Variable<'_>, exec_ctx: &ExecutionCtx
|
||||
Stream(stream) => {
|
||||
// unwrap here is safe because this function will be called only
|
||||
// when this stream's been created
|
||||
let stream = exec_ctx.streams.get(stream.name).unwrap();
|
||||
let stream = exec_ctx.streams.get(stream.name, stream.position).unwrap();
|
||||
let generation = match stream.borrow().generations_count() {
|
||||
0 => 0,
|
||||
n => n - 1,
|
||||
|
@ -17,7 +17,6 @@
|
||||
use super::*;
|
||||
use crate::execution_step::execution_context::*;
|
||||
use crate::execution_step::Generation;
|
||||
use crate::execution_step::Stream;
|
||||
use crate::execution_step::ValueAggregate;
|
||||
|
||||
use air_interpreter_data::CallResult;
|
||||
@ -26,9 +25,6 @@ use air_parser::ast::CallOutputValue;
|
||||
use air_parser::ast::Variable;
|
||||
use air_trace_handler::TraceHandler;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
|
||||
/// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`.
|
||||
/// Returns call result.
|
||||
pub(crate) fn set_local_result<'i>(
|
||||
@ -44,12 +40,21 @@ pub(crate) fn set_local_result<'i>(
|
||||
}
|
||||
CallOutputValue::Variable(Variable::Stream(stream)) => {
|
||||
// TODO: refactor this generation handling
|
||||
let generation = match exec_ctx.streams.get(stream.name) {
|
||||
Some(stream) => Generation::Nth(stream.borrow().generations_count() as u32 - 1),
|
||||
let generation = match exec_ctx.streams.get(stream.name, stream.position) {
|
||||
Some(stream) => {
|
||||
let generation = match stream.borrow().generations_count() {
|
||||
0 => 0,
|
||||
n => n - 1,
|
||||
};
|
||||
Generation::Nth(generation as u32)
|
||||
}
|
||||
None => Generation::Last,
|
||||
};
|
||||
|
||||
let generation = set_stream_result(executed_result, generation, stream.name.to_string(), exec_ctx)?;
|
||||
let generation =
|
||||
exec_ctx
|
||||
.streams
|
||||
.add_stream_value(executed_result, generation, stream.name, stream.position)?;
|
||||
Ok(CallResult::executed_stream(result_value, generation))
|
||||
}
|
||||
CallOutputValue::None => Ok(CallResult::executed_scalar(result_value)),
|
||||
@ -71,7 +76,9 @@ pub(crate) fn set_result_from_value<'i>(
|
||||
(CallOutputValue::Variable(Variable::Stream(stream)), Value::Stream { value, generation }) => {
|
||||
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
||||
let generation = Generation::Nth(generation);
|
||||
let _ = set_stream_result(result, generation, stream.name.to_string(), exec_ctx)?;
|
||||
let _ = exec_ctx
|
||||
.streams
|
||||
.add_stream_value(result, generation, stream.name, stream.position)?;
|
||||
}
|
||||
// it isn't needed to check there that output and value matches because
|
||||
// it's been already checked in trace handler
|
||||
@ -81,28 +88,6 @@ pub(crate) fn set_result_from_value<'i>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: decouple this function to a separate module
|
||||
pub(crate) fn set_stream_result(
|
||||
executed_result: ValueAggregate,
|
||||
generation: Generation,
|
||||
stream_name: String,
|
||||
exec_ctx: &mut ExecutionCtx<'_>,
|
||||
) -> ExecutionResult<u32> {
|
||||
let generation = match exec_ctx.streams.entry(stream_name) {
|
||||
Occupied(mut entry) => {
|
||||
// if result is an array, insert result to the end of the array
|
||||
entry.get_mut().borrow_mut().add_value(executed_result, generation)?
|
||||
}
|
||||
Vacant(entry) => {
|
||||
let stream = Stream::from_value(executed_result);
|
||||
entry.insert(RefCell::new(stream));
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
Ok(generation)
|
||||
}
|
||||
|
||||
/// Writes an executed state of a particle being sent to remote node.
|
||||
pub(crate) fn set_remote_call_result<'i>(
|
||||
peer_pk: String,
|
||||
|
@ -51,10 +51,10 @@ pub(crate) fn construct_scalar_iterable_value<'ctx>(
|
||||
|
||||
/// Constructs iterable value for given stream iterable.
|
||||
pub(crate) fn construct_stream_iterable_value<'ctx>(
|
||||
stream_name: &'ctx str,
|
||||
stream: &ast::Stream<'_>,
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<FoldIterableStream> {
|
||||
match exec_ctx.streams.get(stream_name) {
|
||||
match exec_ctx.streams.get(stream.name, stream.position) {
|
||||
Some(stream) => {
|
||||
let stream = stream.borrow();
|
||||
if stream.is_empty() {
|
||||
|
@ -29,7 +29,7 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(fold, exec_ctx, trace_ctx);
|
||||
|
||||
exec_ctx.scalars.meet_fold_begin();
|
||||
exec_ctx.scalars.meet_fold_start();
|
||||
|
||||
let fold_result = match construct_scalar_iterable_value(&self.iterable, exec_ctx)? {
|
||||
FoldIterableScalar::Empty => Ok(()),
|
||||
|
@ -29,14 +29,14 @@ impl<'i> ExecutableInstruction<'i> for FoldStream<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(fold, exec_ctx, trace_ctx);
|
||||
|
||||
let iterables = match construct_stream_iterable_value(self.iterable.name, exec_ctx)? {
|
||||
let iterables = match construct_stream_iterable_value(&self.iterable, exec_ctx)? {
|
||||
FoldIterableStream::Empty => return Ok(()),
|
||||
FoldIterableStream::Stream(iterables) => iterables,
|
||||
};
|
||||
|
||||
let fold_id = exec_ctx.tracker.fold.seen_stream_count;
|
||||
trace_to_exec_err!(trace_ctx.meet_fold_start(fold_id))?;
|
||||
exec_ctx.scalars.meet_fold_begin();
|
||||
exec_ctx.scalars.meet_fold_start();
|
||||
|
||||
for iterable in iterables {
|
||||
let value = match iterable.peek() {
|
||||
|
@ -22,6 +22,7 @@ mod fold_scalar;
|
||||
mod fold_stream;
|
||||
mod match_;
|
||||
mod mismatch;
|
||||
mod new;
|
||||
mod next;
|
||||
mod null;
|
||||
mod par;
|
||||
@ -126,6 +127,7 @@ impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
||||
Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx),
|
||||
Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||
Instruction::FoldStream(fold) => execute_fold!(self, fold, exec_ctx, trace_ctx),
|
||||
Instruction::New(new) => execute!(self, new, exec_ctx, trace_ctx),
|
||||
Instruction::Next(next) => execute!(self, next, exec_ctx, trace_ctx),
|
||||
Instruction::Null(null) => execute!(self, null, exec_ctx, trace_ctx),
|
||||
Instruction::Par(par) => execute!(self, par, exec_ctx, trace_ctx),
|
||||
@ -147,16 +149,10 @@ macro_rules! log_instruction {
|
||||
log::debug!(target: air_log_targets::INSTRUCTION, "> {}", stringify!($instr_name));
|
||||
|
||||
let mut variables = String::from(" scalars:");
|
||||
|
||||
variables.push_str(&format!("\n {}", $exec_ctx.scalars));
|
||||
|
||||
variables.push_str(" streams:");
|
||||
if $exec_ctx.streams.is_empty() {
|
||||
variables.push_str(" empty");
|
||||
}
|
||||
for (key, value) in $exec_ctx.streams.iter() {
|
||||
variables.push_str(&format!("\n {} => {}", key, value.borrow()));
|
||||
}
|
||||
variables.push_str(&format!("\n {}", $exec_ctx.streams));
|
||||
|
||||
log::trace!(target: air_log_targets::DATA_CACHE, "{}", variables);
|
||||
log::trace!(
|
||||
|
64
air/src/execution_step/air/new.rs
Normal file
64
air/src/execution_step/air/new.rs
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2021 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::ExecutionResult;
|
||||
use super::TraceHandler;
|
||||
use crate::log_instruction;
|
||||
|
||||
use air_parser::ast::New;
|
||||
use air_parser::ast::Variable;
|
||||
|
||||
impl<'i> super::ExecutableInstruction<'i> for New<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(new, exec_ctx, trace_ctx);
|
||||
|
||||
prolog(self, exec_ctx);
|
||||
// it should be a lazy error evaluating after execution of epilog block, since it's
|
||||
// necessary to return a restricted variable to it's previous state in case of
|
||||
// any error. It's highly important to distinguish between global and restricted streams
|
||||
// at the end of execution to make a correct data.
|
||||
let instruction_result = self.instruction.execute(exec_ctx, trace_ctx);
|
||||
epilog(self, exec_ctx);
|
||||
|
||||
instruction_result
|
||||
}
|
||||
}
|
||||
|
||||
fn prolog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) {
|
||||
let position = new.span.left;
|
||||
match &new.variable {
|
||||
Variable::Stream(stream) => {
|
||||
let iteration = exec_ctx.tracker.new_tracker.get_iteration(position);
|
||||
exec_ctx.streams.meet_scope_start(stream.name, new.span, iteration);
|
||||
}
|
||||
// noop
|
||||
Variable::Scalar(_) => {}
|
||||
}
|
||||
|
||||
exec_ctx.tracker.meet_new(position);
|
||||
}
|
||||
|
||||
fn epilog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) {
|
||||
let position = new.span.left;
|
||||
match &new.variable {
|
||||
Variable::Stream(stream) => exec_ctx
|
||||
.streams
|
||||
.meet_scope_end(stream.name.to_string(), position as u32),
|
||||
// noop
|
||||
Variable::Scalar(_) => {}
|
||||
}
|
||||
}
|
@ -19,32 +19,28 @@ use air_parser::ast;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum Variable<'i> {
|
||||
Scalar(&'i str),
|
||||
Stream { name: &'i str, generation: Generation },
|
||||
#[allow(dead_code)]
|
||||
// position will be needed to implement new for operators
|
||||
Scalar { name: &'i str, position: usize },
|
||||
Stream {
|
||||
name: &'i str,
|
||||
generation: Generation,
|
||||
position: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'i> Variable<'i> {
|
||||
pub(crate) fn scalar(name: &'i str) -> Self {
|
||||
Self::Scalar(name)
|
||||
pub(crate) fn scalar(name: &'i str, position: usize) -> Self {
|
||||
Self::Scalar { name, position }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_ast_with_generation(ast_variable: &ast::Variable<'i>, generation: Generation) -> Self {
|
||||
use ast::Variable::*;
|
||||
|
||||
match ast_variable {
|
||||
Scalar(scalar) => Variable::Scalar(scalar.name),
|
||||
Stream(stream) => Variable::Stream {
|
||||
name: stream.name,
|
||||
generation,
|
||||
},
|
||||
pub(crate) fn stream(name: &'i str, generation: Generation, position: usize) -> Self {
|
||||
Self::Stream {
|
||||
name,
|
||||
generation,
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_stream(name: &'i str, generation: Generation) -> Self {
|
||||
Self::Stream { name, generation }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> From<&ast::Variable<'i>> for Variable<'i> {
|
||||
@ -52,11 +48,8 @@ impl<'i> From<&ast::Variable<'i>> for Variable<'i> {
|
||||
use ast::Variable::*;
|
||||
|
||||
match ast_variable {
|
||||
Scalar(scalar) => Self::Scalar(scalar.name),
|
||||
Stream(stream) => Self::Stream {
|
||||
name: stream.name,
|
||||
generation: Generation::Last,
|
||||
},
|
||||
Scalar(scalar) => Self::scalar(scalar.name, scalar.position),
|
||||
Stream(stream) => Self::stream(stream.name, Generation::Last, stream.position),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,11 +59,8 @@ impl<'i> From<&ast::VariableWithLambda<'i>> for Variable<'i> {
|
||||
use ast::VariableWithLambda::*;
|
||||
|
||||
match ast_variable {
|
||||
Scalar(scalar) => Self::Scalar(scalar.name),
|
||||
Stream(stream) => Self::Stream {
|
||||
name: stream.name,
|
||||
generation: Generation::Last,
|
||||
},
|
||||
Scalar(scalar) => Self::scalar(scalar.name, scalar.position),
|
||||
Stream(stream) => Self::stream(stream.name, Generation::Last, stream.position),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ pub(crate) enum ExecutionError {
|
||||
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
|
||||
LocalServiceError(i32, Rc<String>),
|
||||
|
||||
/// Value for such name isn't presence in data.
|
||||
#[error("variable with name '{0}' isn't present in data")]
|
||||
/// Variable with such a name wasn't defined during AIR script execution.
|
||||
#[error("variable with name '{0}' wasn't defined during script execution")]
|
||||
VariableNotFound(String),
|
||||
|
||||
/// Multiple values for such name found.
|
||||
|
@ -17,13 +17,11 @@
|
||||
use super::LastErrorDescriptor;
|
||||
use super::LastErrorWithTetraplet;
|
||||
use super::Scalars;
|
||||
use crate::execution_step::boxed_value::Stream;
|
||||
use super::Streams;
|
||||
|
||||
use air_execution_info_collector::InstructionTracker;
|
||||
use air_interpreter_interface::*;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Contains all necessary state needed to execute AIR script.
|
||||
@ -33,8 +31,7 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
pub(crate) scalars: Scalars<'i>,
|
||||
|
||||
/// Contains all streams.
|
||||
// TODO: use shared string (Rc<String>) to avoid copying.
|
||||
pub(crate) streams: HashMap<String, RefCell<Stream>>,
|
||||
pub(crate) streams: Streams,
|
||||
|
||||
/// Set of peer public keys that should receive resulted data.
|
||||
pub(crate) next_peer_pks: Vec<String>,
|
||||
@ -116,9 +113,7 @@ impl<'i> Display for ExecutionCtx<'i> {
|
||||
writeln!(f, " {}", self.scalars)?;
|
||||
|
||||
writeln!(f, "streams:")?;
|
||||
for (name, stream) in self.streams.iter() {
|
||||
writeln!(f, " {} => {}", name, stream.borrow())?;
|
||||
}
|
||||
writeln!(f, " {}", self.streams)?;
|
||||
|
||||
writeln!(f, "current peer id: {}", self.current_peer_id)?;
|
||||
writeln!(f, "subtree complete: {}", self.subtree_complete)?;
|
||||
|
@ -17,9 +17,11 @@
|
||||
mod context;
|
||||
mod error_descriptor;
|
||||
mod scalar_variables;
|
||||
mod streams_variables;
|
||||
|
||||
pub(crate) use context::*;
|
||||
pub use error_descriptor::LastError;
|
||||
pub(crate) use error_descriptor::LastErrorDescriptor;
|
||||
pub(crate) use error_descriptor::LastErrorWithTetraplet;
|
||||
pub(crate) use scalar_variables::*;
|
||||
pub(crate) use streams_variables::*;
|
||||
|
@ -70,7 +70,6 @@ pub(crate) struct Scalars<'i> {
|
||||
pub fold_block_id: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<'i> Scalars<'i> {
|
||||
/// Returns true if there was a previous value for the provided key on the same
|
||||
/// fold block.
|
||||
@ -122,10 +121,6 @@ impl<'i> Scalars<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove_value(&mut self, name: &str) {
|
||||
self.values.remove(name);
|
||||
}
|
||||
|
||||
pub(crate) fn remove_iterable_value(&mut self, name: &str) {
|
||||
self.iterable_values.remove(name);
|
||||
}
|
||||
@ -143,26 +138,6 @@ impl<'i> Scalars<'i> {
|
||||
.ok_or_else(|| Rc::new(ExecutionError::VariableNotFound(name.to_string())))
|
||||
}
|
||||
|
||||
pub(crate) fn get_value_mut(&'i mut self, name: &str) -> ExecutionResult<&'i mut ValueAggregate> {
|
||||
let fold_block_id = self.fold_block_id;
|
||||
self.values
|
||||
.get_mut(name)
|
||||
.and_then(|scalars| {
|
||||
scalars
|
||||
.iter_mut()
|
||||
.take(fold_block_id)
|
||||
.rev()
|
||||
.find_map(|scalar| scalar.as_mut())
|
||||
})
|
||||
.ok_or_else(|| Rc::new(ExecutionError::VariableNotFound(name.to_string())))
|
||||
}
|
||||
|
||||
pub(crate) fn get_iterable(&self, name: &str) -> ExecutionResult<&FoldState<'i>> {
|
||||
self.iterable_values
|
||||
.get(name)
|
||||
.ok_or_else(|| Rc::new(ExecutionError::FoldStateNotFound(name.to_string())))
|
||||
}
|
||||
|
||||
pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> {
|
||||
self.iterable_values
|
||||
.get_mut(name)
|
||||
@ -181,7 +156,7 @@ impl<'i> Scalars<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn meet_fold_begin(&mut self) {
|
||||
pub(crate) fn meet_fold_start(&mut self) {
|
||||
self.fold_block_id += 1;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2021 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::Scalars;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl<'i> fmt::Display for Scalars<'i> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "fold_block_id: {}", self.fold_blocks_sequence.len())?;
|
||||
|
||||
for (name, _) in self.values.iter() {
|
||||
let descriptor = self.values.get(name);
|
||||
if let Some(descriptor) = descriptor {
|
||||
writeln!(f, "{} => {:?}", name, descriptor.values.last())?;
|
||||
}
|
||||
}
|
||||
|
||||
for (name, _) in self.iterable_values.iter() {
|
||||
// it's impossible to print an iterable value for now
|
||||
writeln!(f, "{} => iterable", name)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
224
air/src/execution_step/execution_context/streams_variables.rs
Normal file
224
air/src/execution_step/execution_context/streams_variables.rs
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2021 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 crate::execution_step::ExecutionResult;
|
||||
use crate::execution_step::Generation;
|
||||
use crate::execution_step::Stream;
|
||||
use crate::execution_step::ValueAggregate;
|
||||
|
||||
use air_interpreter_data::GlobalStreamGens;
|
||||
use air_interpreter_data::RestrictedStreamGens;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Streams {
|
||||
// this one is optimized for speed (not for memory), because it's unexpected
|
||||
// that a script could have a lot of new.
|
||||
// TODO: use shared string (Rc<String>) to avoid copying.
|
||||
streams: HashMap<String, Vec<StreamDescriptor>>,
|
||||
|
||||
/// Contains stream generation that private stream should have at the scope start.
|
||||
data_restr_stream_generations: RestrictedStreamGens,
|
||||
|
||||
/// Contains stream generations that each private stream had at the scope end.
|
||||
/// Then it's placed into data
|
||||
collected_restricted_stream_gens: RestrictedStreamGens,
|
||||
}
|
||||
|
||||
struct StreamDescriptor {
|
||||
pub(self) span: Span,
|
||||
// TODO: get rid of RefCell in a separate PR
|
||||
pub(self) stream: RefCell<Stream>,
|
||||
}
|
||||
|
||||
impl Streams {
|
||||
pub(crate) fn get(&self, name: &str, position: usize) -> Option<&RefCell<Stream>> {
|
||||
self.streams
|
||||
.get(name)
|
||||
.map(|descriptors| find_closest(descriptors.iter(), position))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub(crate) fn add_stream_value(
|
||||
&mut self,
|
||||
value: ValueAggregate,
|
||||
generation: Generation,
|
||||
stream_name: &str,
|
||||
position: usize,
|
||||
) -> ExecutionResult<u32> {
|
||||
match self.get(stream_name, position) {
|
||||
Some(stream) => stream.borrow_mut().add_value(value, generation),
|
||||
None => {
|
||||
// streams could be created in three ways:
|
||||
// - after met new instruction with stream name that isn't present in streams
|
||||
// (it's the only way to create restricted streams)
|
||||
// - by calling add_global_stream with generation that come from data
|
||||
// for global streams
|
||||
// - and by this function, and if there is no such a streams in streams,
|
||||
// it means that a new global one should be created.
|
||||
let stream = Stream::from_value(value);
|
||||
self.add_global_stream(stream_name.to_string(), stream);
|
||||
let generation = 0;
|
||||
Ok(generation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_global_stream(&mut self, name: String, stream: Stream) {
|
||||
let descriptor = StreamDescriptor::global(RefCell::new(stream));
|
||||
self.streams.insert(name, vec![descriptor]);
|
||||
}
|
||||
|
||||
pub(crate) fn meet_scope_start(&mut self, name: impl Into<String>, span: Span, iteration: u32) {
|
||||
let name = name.into();
|
||||
let generations_count = self
|
||||
.stream_generation_from_data(&name, span.left as u32, iteration as usize)
|
||||
.unwrap_or_default();
|
||||
|
||||
let new_stream = RefCell::new(Stream::from_generations_count(generations_count as usize));
|
||||
let new_descriptor = StreamDescriptor::restricted(new_stream, span);
|
||||
match self.streams.entry(name) {
|
||||
Occupied(mut entry) => {
|
||||
entry.get_mut().push(new_descriptor);
|
||||
}
|
||||
Vacant(entry) => {
|
||||
entry.insert(vec![new_descriptor]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn meet_scope_end(&mut self, name: String, position: u32) {
|
||||
// unwraps are safe here because met_scope_end must be called after met_scope_start
|
||||
let stream_descriptors = self.streams.get_mut(&name).unwrap();
|
||||
// delete a stream after exit from a scope
|
||||
let last_descriptor = stream_descriptors.pop().unwrap();
|
||||
if stream_descriptors.is_empty() {
|
||||
// streams should contain only non-empty stream embodiments
|
||||
self.streams.remove(&name);
|
||||
}
|
||||
|
||||
self.collect_stream_generation(
|
||||
name,
|
||||
position,
|
||||
last_descriptor.stream.borrow().generations_count() as u32,
|
||||
);
|
||||
}
|
||||
|
||||
/// This method must be called at the end of execution, because it contains logic to collect
|
||||
/// all global streams depending on their presence in a streams field.
|
||||
pub(crate) fn into_streams_data(self) -> (GlobalStreamGens, RestrictedStreamGens) {
|
||||
// since it's called at the end of execution, streams contains only global ones,
|
||||
// because all private's been deleted after exiting a scope
|
||||
let global_streams = self
|
||||
.streams
|
||||
.into_iter()
|
||||
.map(|(name, mut descriptors)| {
|
||||
// unwrap is safe here because of invariant that streams contains non-empty vectors,
|
||||
// moreover it must contain only one value, because this method is called at the end
|
||||
// of the execution
|
||||
let generation = descriptors.pop().unwrap().stream.borrow().generations_count();
|
||||
(name, generation as u32)
|
||||
})
|
||||
.collect::<GlobalStreamGens>();
|
||||
|
||||
(global_streams, self.collected_restricted_stream_gens)
|
||||
}
|
||||
|
||||
fn stream_generation_from_data(&self, name: &str, position: u32, iteration: usize) -> Option<u32> {
|
||||
self.data_restr_stream_generations
|
||||
.get(name)
|
||||
.and_then(|scopes| {
|
||||
scopes
|
||||
.get(&position)
|
||||
.map(|iterations| iterations.get(iteration))
|
||||
.flatten()
|
||||
})
|
||||
.copied()
|
||||
}
|
||||
|
||||
fn collect_stream_generation(&mut self, name: String, position: u32, generation: u32) {
|
||||
match self.collected_restricted_stream_gens.entry(name) {
|
||||
Occupied(mut streams) => match streams.get_mut().entry(position) {
|
||||
Occupied(mut iterations) => iterations.get_mut().push(generation),
|
||||
Vacant(entry) => {
|
||||
entry.insert(vec![generation]);
|
||||
}
|
||||
},
|
||||
Vacant(entry) => {
|
||||
let iterations = maplit::hashmap! {
|
||||
position => vec![generation],
|
||||
};
|
||||
entry.insert(iterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamDescriptor {
|
||||
pub(self) fn global(stream: RefCell<Stream>) -> Self {
|
||||
Self {
|
||||
span: Span::new(0, usize::MAX),
|
||||
stream,
|
||||
}
|
||||
}
|
||||
|
||||
pub(self) fn restricted(stream: RefCell<Stream>, span: Span) -> Self {
|
||||
Self { span, stream }
|
||||
}
|
||||
}
|
||||
|
||||
fn find_closest<'d>(
|
||||
descriptors: impl DoubleEndedIterator<Item = &'d StreamDescriptor>,
|
||||
position: usize,
|
||||
) -> Option<&'d RefCell<Stream>> {
|
||||
// descriptors are placed in a order of decreasing scopes, so it's enough to get the latest suitable
|
||||
for descriptor in descriptors.rev() {
|
||||
if descriptor.span.contains(position) {
|
||||
return Some(&descriptor.stream);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
use air_parser::ast::Span;
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Streams {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (name, descriptors) in self.streams.iter() {
|
||||
if let Some(last_descriptor) = descriptors.last() {
|
||||
writeln!(f, "{} => {}", name, last_descriptor)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StreamDescriptor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
" <{}> - <{}>: {}",
|
||||
self.span.left,
|
||||
self.span.right,
|
||||
self.stream.borrow()
|
||||
)
|
||||
}
|
||||
}
|
@ -87,9 +87,13 @@ pub(crate) fn resolve_variable<'ctx, 'i>(
|
||||
use crate::execution_step::boxed_value::StreamJvaluableIngredients;
|
||||
|
||||
match variable {
|
||||
Variable::Scalar(name) => Ok(ctx.scalars.get(name)?.into_jvaluable()),
|
||||
Variable::Stream { name, generation } => {
|
||||
match ctx.streams.get(name) {
|
||||
Variable::Scalar { name, .. } => Ok(ctx.scalars.get(name)?.into_jvaluable()),
|
||||
Variable::Stream {
|
||||
name,
|
||||
generation,
|
||||
position,
|
||||
} => {
|
||||
match ctx.streams.get(name, position) {
|
||||
Some(stream) => {
|
||||
let ingredients = StreamJvaluableIngredients::new(stream.borrow(), generation);
|
||||
Ok(Box::new(ingredients))
|
||||
|
@ -88,13 +88,10 @@ fn make_exec_ctx(
|
||||
}
|
||||
|
||||
fn create_streams(ctx: &mut ExecutionCtx<'_>, prev_data: &InterpreterData) {
|
||||
use std::cell::RefCell;
|
||||
|
||||
for (stream_name, generation_count) in prev_data.streams.iter() {
|
||||
for (stream_name, generation_count) in prev_data.global_streams.iter() {
|
||||
let new_stream = Stream::from_generations_count(*generation_count as usize);
|
||||
let new_stream = RefCell::new(new_stream);
|
||||
|
||||
// it's impossible to have duplicates of streams in data because of HashMap in data
|
||||
ctx.streams.insert(stream_name.to_string(), new_stream);
|
||||
ctx.streams.add_global_stream(stream_name.to_string(), new_stream);
|
||||
}
|
||||
}
|
||||
|
@ -16,28 +16,25 @@
|
||||
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
use crate::execution_step::ExecutionError;
|
||||
use crate::execution_step::Stream;
|
||||
use crate::execution_step::TraceHandler;
|
||||
use crate::preparation_step::PreparationError;
|
||||
use crate::InterpreterOutcome;
|
||||
use crate::INTERPRETER_SUCCESS;
|
||||
|
||||
use air_interpreter_data::InterpreterData;
|
||||
use air_interpreter_data::StreamGenerations;
|
||||
use air_interpreter_interface::CallRequests;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Create InterpreterOutcome from supplied execution context and trace handler,
|
||||
/// set ret_code to INTERPRETER_SUCCESS.
|
||||
pub(crate) fn from_success_result(exec_ctx: ExecutionCtx<'_>, trace_handler: TraceHandler) -> InterpreterOutcome {
|
||||
let streams = extract_stream_generations(exec_ctx.streams);
|
||||
let (global_streams, restricted_streams) = exec_ctx.streams.into_streams_data();
|
||||
let data = InterpreterData::from_execution_result(
|
||||
trace_handler.into_result_trace(),
|
||||
streams,
|
||||
global_streams,
|
||||
restricted_streams,
|
||||
exec_ctx.last_call_request_id,
|
||||
);
|
||||
let data = serde_json::to_vec(&data).expect("default serializer shouldn't fail");
|
||||
@ -94,10 +91,11 @@ pub(crate) fn from_execution_error(
|
||||
) -> InterpreterOutcome {
|
||||
let ret_code = err.to_error_code() as i32;
|
||||
|
||||
let streams = extract_stream_generations(exec_ctx.streams);
|
||||
let (global_streams, restricted_streams) = exec_ctx.streams.into_streams_data();
|
||||
let data = InterpreterData::from_execution_result(
|
||||
trace_handler.into_result_trace(),
|
||||
streams,
|
||||
global_streams,
|
||||
restricted_streams,
|
||||
exec_ctx.last_call_request_id,
|
||||
);
|
||||
let data = serde_json::to_vec(&data).expect("default serializer shouldn't fail");
|
||||
@ -120,10 +118,3 @@ fn dedup<T: Eq + Hash>(mut vec: Vec<T>) -> Vec<T> {
|
||||
let set: HashSet<_> = vec.drain(..).collect();
|
||||
set.into_iter().collect()
|
||||
}
|
||||
|
||||
fn extract_stream_generations(streams: HashMap<String, RefCell<Stream>>) -> StreamGenerations {
|
||||
streams
|
||||
.into_iter()
|
||||
.map(|(name, stream)| (name, stream.borrow().generations_count() as u32))
|
||||
.collect::<_>()
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ mod call;
|
||||
mod fold;
|
||||
mod match_;
|
||||
mod mismatch;
|
||||
mod new;
|
||||
mod par;
|
||||
mod seq;
|
||||
mod xor;
|
||||
|
289
air/tests/test_module/instructions/new.rs
Normal file
289
air/tests/test_module/instructions/new.rs
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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 air_test_utils::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn new_with_global_streams_seq() {
|
||||
let set_variable_peer_id = "set_variable_peer_id";
|
||||
let local_vm_peer_id_1 = "local_vm_peer_id_1";
|
||||
let local_vm_peer_id_2 = "local_vm_peer_id_2";
|
||||
|
||||
let mut local_vm_1 = create_avm(echo_call_service(), local_vm_peer_id_1);
|
||||
let mut local_vm_2 = create_avm(echo_call_service(), local_vm_peer_id_2);
|
||||
|
||||
let variables_mapping = maplit::hashmap! {
|
||||
"1".to_string() => json!(1),
|
||||
"2".to_string() => json!(2),
|
||||
};
|
||||
let mut set_variable_vm = create_avm(set_variables_call_service(variables_mapping), set_variable_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(seq
|
||||
(call "{0}" ("" "") ["1"] $stream)
|
||||
(call "{0}" ("" "") ["2"] $stream)
|
||||
)
|
||||
(fold $stream i
|
||||
(seq
|
||||
(new $stream
|
||||
(seq
|
||||
(seq
|
||||
(call "{1}" ("" "") [i] $stream)
|
||||
(next i)
|
||||
)
|
||||
(call "{1}" ("" "") [$stream])
|
||||
)
|
||||
)
|
||||
(call "{2}" ("" "") [$stream])
|
||||
)
|
||||
)
|
||||
)"#,
|
||||
set_variable_peer_id, local_vm_peer_id_1, local_vm_peer_id_2
|
||||
);
|
||||
|
||||
let result = checked_call_vm!(set_variable_vm, "", &script, "", "");
|
||||
let vm_1_result = checked_call_vm!(local_vm_1, "", &script, "", result.data);
|
||||
let vm_2_result = checked_call_vm!(local_vm_2, "", &script, "", vm_1_result.data.clone());
|
||||
|
||||
let vm_1_result = checked_call_vm!(local_vm_1, "", &script, vm_1_result.data, vm_2_result.data.clone());
|
||||
let vm_2_result = checked_call_vm!(local_vm_2, "", script, vm_2_result.data, vm_1_result.data);
|
||||
|
||||
let actual_trace = trace_from_result(&vm_2_result);
|
||||
let expected_trace = vec![
|
||||
executed_state::stream_number(1, 0),
|
||||
executed_state::stream_number(2, 0),
|
||||
executed_state::fold(vec![
|
||||
executed_state::subtrace_lore(0, SubTraceDesc::new(3, 1), SubTraceDesc::new(7, 2)),
|
||||
executed_state::subtrace_lore(1, SubTraceDesc::new(4, 1), SubTraceDesc::new(5, 2)),
|
||||
]),
|
||||
executed_state::stream_number(1, 0),
|
||||
executed_state::stream_number(2, 0),
|
||||
executed_state::scalar(json!([2])),
|
||||
executed_state::scalar(json!([1, 2])),
|
||||
executed_state::scalar(json!([1])),
|
||||
executed_state::scalar(json!([1, 2])),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
|
||||
let data = data_from_result(&vm_2_result);
|
||||
let actual_restricted_streams = data.restricted_streams;
|
||||
let expected_restricted_streams = maplit::hashmap! {
|
||||
"$stream".to_string() => maplit::hashmap! {
|
||||
282 => vec![1,1]
|
||||
}
|
||||
};
|
||||
assert_eq!(actual_restricted_streams, expected_restricted_streams);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn several_restrictions() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let mut vm = create_avm(echo_call_service(), vm_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(new $stream
|
||||
(seq
|
||||
(new $stream
|
||||
(call "{0}" ("" "") ["test"] $stream)
|
||||
)
|
||||
(call "{0}" ("" "") [$stream])
|
||||
)
|
||||
)"#,
|
||||
vm_peer_id
|
||||
);
|
||||
|
||||
let result = checked_call_vm!(vm, "", script, "", "");
|
||||
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let expected_trace = vec![
|
||||
executed_state::stream_string("test", 0),
|
||||
executed_state::scalar(json!([])),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_influence_to_not_restricted() {
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
let mut vm = create_avm(echo_call_service(), vm_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(new $a
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(ap "push more" $a0)
|
||||
(ap "push more" $a1)
|
||||
)
|
||||
(ap "more" $a)
|
||||
)
|
||||
(call "{0}" ("op" "identity") [$a] a-fix)
|
||||
)
|
||||
)
|
||||
(seq
|
||||
(seq
|
||||
(call "{0}" ("callbackSrv" "response") [$a0]) ;; should be non-empty
|
||||
(call "{0}" ("callbackSrv" "response") [$a1]) ;; should be non-empty
|
||||
)
|
||||
(seq
|
||||
(call "{0}" ("callbackSrv" "response") [$a]) ;; should be empty
|
||||
(call "{0}" ("callbackSrv" "response") [a-fix]) ;; should be empty
|
||||
)
|
||||
)
|
||||
)
|
||||
"#,
|
||||
vm_peer_id
|
||||
);
|
||||
|
||||
let result = checked_call_vm!(vm, "", script, "", "");
|
||||
print_trace(&result, "initial");
|
||||
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let expected_trace = vec![
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar(json!(["more"])),
|
||||
executed_state::scalar(json!(["push more"])),
|
||||
executed_state::scalar(json!(["push more"])),
|
||||
executed_state::scalar(json!([])),
|
||||
executed_state::scalar(json!(["more"])),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_in_fold_with_ap() {
|
||||
let set_variable_peer_id = "set_variable_peer_id";
|
||||
let vm_peer_id = "vm_peer_id";
|
||||
|
||||
let mut set_variable_vm = create_avm(set_variable_call_service(json!([1, 2, 3, 4, 5])), set_variable_peer_id);
|
||||
let mut vm = create_avm(echo_call_service(), vm_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{0}" ("" "") [] iterable)
|
||||
(fold iterable x
|
||||
(seq
|
||||
(new $s1
|
||||
(seq
|
||||
(ap "none" $s1)
|
||||
(call "{1}" ("" "") [$s1] s-fix1) ;; should contains only "none" on each iteration
|
||||
)
|
||||
)
|
||||
(next x)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
"#,
|
||||
set_variable_peer_id, vm_peer_id
|
||||
);
|
||||
|
||||
let result = checked_call_vm!(set_variable_vm, "", &script, "", "");
|
||||
let result = checked_call_vm!(vm, "", script, "", result.data);
|
||||
|
||||
print_trace(&result, "initial");
|
||||
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let expected_trace = vec![
|
||||
executed_state::scalar(json!([1, 2, 3, 4, 5])),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar_string_array(vec!["none"]),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar_string_array(vec!["none"]),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar_string_array(vec!["none"]),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar_string_array(vec!["none"]),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar_string_array(vec!["none"]),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
|
||||
let data = data_from_result(&result);
|
||||
let actual_restricted_streams = data.restricted_streams;
|
||||
let expected_restricted_streams = maplit::hashmap! {
|
||||
"$s1".to_string() => maplit::hashmap! {
|
||||
146 => vec![1,1,1,1,1]
|
||||
}
|
||||
};
|
||||
assert_eq!(actual_restricted_streams, expected_restricted_streams);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_with_errors() {
|
||||
let faillible_peer_id = "failible_peer_id";
|
||||
let mut faillible_vm = create_avm(fallible_call_service("service_id_1"), faillible_peer_id);
|
||||
|
||||
let local_peer_id = "local_peer_id";
|
||||
let mut vm = create_avm(echo_call_service(), local_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{0}" ("" "") [1] $global_stream) ;; this stream should precense in a data
|
||||
(new $restricted_stream_1
|
||||
(seq
|
||||
(new $restricted_stream_2
|
||||
(seq
|
||||
(call "{0}" ("" "") [2] $restricted_stream_2) ;; should have generation 1 in a data
|
||||
(call "{1}" ("service_id_1" "local_fn_name") [] result)
|
||||
)
|
||||
)
|
||||
(call "{0}" ("" "") [2] restricted_stream_1) ;; should have generation 0 in a data
|
||||
)
|
||||
)
|
||||
)"#,
|
||||
local_peer_id, faillible_peer_id
|
||||
);
|
||||
|
||||
let result = checked_call_vm!(vm, "", &script, "", "");
|
||||
let result = call_vm!(faillible_vm, "", script, "", result.data);
|
||||
|
||||
let actual_trace = trace_from_result(&result);
|
||||
let expected_trace = vec![
|
||||
executed_state::stream_number(1, 0),
|
||||
executed_state::stream_number(2, 0),
|
||||
executed_state::service_failed(1, r#""error""#),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
|
||||
let data = data_from_result(&result);
|
||||
|
||||
let actual_restricted_streams = data.restricted_streams;
|
||||
let expected_restricted_streams = maplit::hashmap! {
|
||||
"$restricted_stream_2".to_string() => maplit::hashmap! {
|
||||
216 => vec![1]
|
||||
},
|
||||
"$restricted_stream_1".to_string() => maplit::hashmap! {
|
||||
141 => vec![0]
|
||||
}
|
||||
};
|
||||
assert_eq!(actual_restricted_streams, expected_restricted_streams);
|
||||
|
||||
let actual_global_streams = data.global_streams;
|
||||
let expected_global_streams = maplit::hashmap! {
|
||||
"$global_stream".to_string() => 1,
|
||||
};
|
||||
assert_eq!(actual_global_streams, expected_global_streams);
|
||||
}
|
@ -262,13 +262,13 @@ fn fold_merge() {
|
||||
|
||||
let data = InterpreterData::try_from_slice(&result_7.data).expect("data should be well-formed");
|
||||
let stream_1_generations = data
|
||||
.streams
|
||||
.global_streams
|
||||
.get("$stream_1")
|
||||
.expect("$stream_1 should presence in data");
|
||||
.expect("$stream_1 should be present in data");
|
||||
let stream_2_generations = data
|
||||
.streams
|
||||
.global_streams
|
||||
.get("$stream_2")
|
||||
.expect("$stream_2 should presence in data");
|
||||
.expect("$stream_2 should be present in data");
|
||||
|
||||
assert_eq!(*stream_1_generations, 4);
|
||||
assert_eq!(*stream_2_generations, 3);
|
||||
|
@ -74,7 +74,7 @@ fn join_chat() {
|
||||
let remote_actual_trace = trace_from_result(&remote_result);
|
||||
let remote_expected_trace = vec![
|
||||
executed_state::stream_string("test", 0),
|
||||
executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::stream(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])),
|
||||
executed_state::par(1, 2),
|
||||
executed_state::request_sent_by("Remote"),
|
||||
@ -97,7 +97,7 @@ fn join_chat() {
|
||||
|
||||
let relay_1_expected_trace = vec![
|
||||
executed_state::stream_string("test", 0),
|
||||
executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::stream(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])),
|
||||
executed_state::par(2, 2),
|
||||
executed_state::stream_string("test", 0),
|
||||
@ -115,7 +115,7 @@ fn join_chat() {
|
||||
|
||||
let client_1_expected_trace = vec![
|
||||
executed_state::stream_string("test", 0),
|
||||
executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::stream(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])),
|
||||
executed_state::par(2, 2),
|
||||
executed_state::stream_string("test", 0),
|
||||
@ -133,7 +133,7 @@ fn join_chat() {
|
||||
|
||||
let relay_2_expected_trace = vec![
|
||||
executed_state::stream_string("test", 0),
|
||||
executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::stream(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])),
|
||||
executed_state::par(1, 3),
|
||||
executed_state::request_sent_by("Remote"),
|
||||
@ -151,7 +151,7 @@ fn join_chat() {
|
||||
|
||||
let client_2_expected_trace = vec![
|
||||
executed_state::stream_string("test", 0),
|
||||
executed_state::stream_jvalue(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::stream(json!([["A", "Relay1"], ["B", "Relay2"]]), 0),
|
||||
executed_state::scalar(json!([["A", "Relay1"], ["B", "Relay2"]])),
|
||||
executed_state::par(1, 3),
|
||||
executed_state::request_sent_by("Remote"),
|
||||
|
@ -24,7 +24,6 @@ use std::rc::Rc;
|
||||
#[allow(clippy::large_enum_variant)] // for Null and Error variants
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum Instruction<'i> {
|
||||
Null(Null),
|
||||
Call(Call<'i>),
|
||||
Ap(Ap<'i>),
|
||||
Seq(Seq<'i>),
|
||||
@ -34,7 +33,9 @@ pub enum Instruction<'i> {
|
||||
MisMatch(MisMatch<'i>),
|
||||
FoldScalar(FoldScalar<'i>),
|
||||
FoldStream(FoldStream<'i>),
|
||||
New(New<'i>),
|
||||
Next(Next<'i>),
|
||||
Null(Null),
|
||||
Error,
|
||||
}
|
||||
|
||||
@ -88,6 +89,7 @@ pub struct FoldScalar<'i> {
|
||||
#[serde(borrow)]
|
||||
pub iterator: Scalar<'i>,
|
||||
pub instruction: Rc<Instruction<'i>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// (fold stream_iterable iterator instruction)
|
||||
@ -97,6 +99,7 @@ pub struct FoldStream<'i> {
|
||||
#[serde(borrow)]
|
||||
pub iterator: Scalar<'i>,
|
||||
pub instruction: Rc<Instruction<'i>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// (fold stream_iterable iterator instruction)
|
||||
@ -105,6 +108,14 @@ pub struct Next<'i> {
|
||||
pub iterator: Scalar<'i>,
|
||||
}
|
||||
|
||||
/// (new variable instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct New<'i> {
|
||||
pub variable: Variable<'i>,
|
||||
pub instruction: Box<Instruction<'i>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// (null)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Null;
|
||||
|
@ -21,3 +21,121 @@ impl<'i> Ap<'i> {
|
||||
Self { argument, result }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Call<'i> {
|
||||
pub fn new(
|
||||
triplet: Triplet<'i>,
|
||||
args: Rc<Vec<Value<'i>>>,
|
||||
output: CallOutputValue<'i>,
|
||||
) -> Self {
|
||||
Self {
|
||||
triplet,
|
||||
args,
|
||||
output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Seq<'i> {
|
||||
pub fn new(
|
||||
left_instruction: Box<Instruction<'i>>,
|
||||
right_instruction: Box<Instruction<'i>>,
|
||||
) -> Self {
|
||||
Self(left_instruction, right_instruction)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Par<'i> {
|
||||
pub fn new(
|
||||
left_instruction: Box<Instruction<'i>>,
|
||||
right_instruction: Box<Instruction<'i>>,
|
||||
) -> Self {
|
||||
Self(left_instruction, right_instruction)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Xor<'i> {
|
||||
pub fn new(
|
||||
left_instruction: Box<Instruction<'i>>,
|
||||
right_instruction: Box<Instruction<'i>>,
|
||||
) -> Self {
|
||||
Self(left_instruction, right_instruction)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Match<'i> {
|
||||
pub fn new(
|
||||
left_value: Value<'i>,
|
||||
right_value: Value<'i>,
|
||||
instruction: Box<Instruction<'i>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
left_value,
|
||||
right_value,
|
||||
instruction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> MisMatch<'i> {
|
||||
pub fn new(
|
||||
left_value: Value<'i>,
|
||||
right_value: Value<'i>,
|
||||
instruction: Box<Instruction<'i>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
left_value,
|
||||
right_value,
|
||||
instruction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> FoldScalar<'i> {
|
||||
pub fn new(
|
||||
iterable: ScalarWithLambda<'i>,
|
||||
iterator: Scalar<'i>,
|
||||
instruction: Instruction<'i>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
Self {
|
||||
iterable,
|
||||
iterator,
|
||||
instruction: Rc::new(instruction),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> FoldStream<'i> {
|
||||
pub fn new(
|
||||
iterable: Stream<'i>,
|
||||
iterator: Scalar<'i>,
|
||||
instruction: Instruction<'i>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
Self {
|
||||
iterable,
|
||||
iterator,
|
||||
instruction: Rc::new(instruction),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Next<'i> {
|
||||
pub fn new(iterator: Scalar<'i>) -> Self {
|
||||
Self { iterator }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> New<'i> {
|
||||
#[allow(clippy::self_named_constructors)]
|
||||
pub fn new(variable: Variable<'i>, instruction: Box<Instruction<'i>>, span: Span) -> Self {
|
||||
Self {
|
||||
variable,
|
||||
instruction,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ impl fmt::Display for Instruction<'_> {
|
||||
use Instruction::*;
|
||||
|
||||
match self {
|
||||
Null(null) => write!(f, "{}", null),
|
||||
Call(call) => write!(f, "{}", call),
|
||||
Ap(ap) => write!(f, "{}", ap),
|
||||
Seq(seq) => write!(f, "{}", seq),
|
||||
@ -34,6 +33,8 @@ impl fmt::Display for Instruction<'_> {
|
||||
FoldScalar(fold) => write!(f, "{}", fold),
|
||||
FoldStream(fold) => write!(f, "{}", fold),
|
||||
Next(next) => write!(f, "{}", next),
|
||||
New(new) => write!(f, "{}", new),
|
||||
Null(null) => write!(f, "{}", null),
|
||||
Error => Ok(()),
|
||||
}
|
||||
}
|
||||
@ -107,3 +108,9 @@ impl fmt::Display for Next<'_> {
|
||||
write!(f, "next")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for New<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "new {}", self.variable)
|
||||
}
|
||||
}
|
||||
|
@ -24,3 +24,4 @@ pub use values::*;
|
||||
|
||||
pub use crate::parser::lexer::LastErrorPath;
|
||||
pub use crate::parser::lexer::Number;
|
||||
pub use crate::parser::Span;
|
||||
|
@ -26,6 +26,7 @@ use serde::Serialize;
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Scalar<'i> {
|
||||
pub name: &'i str,
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
/// A scalar value with possible lambda expression.
|
||||
@ -34,12 +35,14 @@ pub struct ScalarWithLambda<'i> {
|
||||
pub name: &'i str,
|
||||
#[serde(borrow)]
|
||||
pub lambda: Option<LambdaAST<'i>>,
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
/// A stream without lambda.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Stream<'i> {
|
||||
pub name: &'i str,
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
/// A stream with possible lambda expression.
|
||||
@ -48,6 +51,7 @@ pub struct StreamWithLambda<'i> {
|
||||
pub name: &'i str,
|
||||
#[serde(borrow)]
|
||||
pub lambda: Option<LambdaAST<'i>>,
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
/// A variable that could be either scalar or stream without lambda.
|
||||
|
@ -19,55 +19,73 @@ use air_lambda_parser::LambdaAST;
|
||||
use air_lambda_parser::ValueAccessor;
|
||||
|
||||
impl<'i> ScalarWithLambda<'i> {
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>) -> Self {
|
||||
Self { name, lambda }
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
lambda,
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
// it's unsafe method that should be used only for tests
|
||||
pub(crate) fn from_raw_lambda(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
pub(crate) fn from_raw_lambda(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
||||
Self {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> StreamWithLambda<'i> {
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>) -> Self {
|
||||
Self { name, lambda }
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
lambda,
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
// it's unsafe method that should be used only for tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
pub(crate) fn from_raw_lambda(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
||||
Self {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Scalar<'i> {
|
||||
pub fn new(name: &'i str) -> Self {
|
||||
Self { name }
|
||||
pub fn new(name: &'i str, position: usize) -> Self {
|
||||
Self { name, position }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Stream<'i> {
|
||||
pub fn new(name: &'i str) -> Self {
|
||||
Self { name }
|
||||
pub fn new(name: &'i str, position: usize) -> Self {
|
||||
Self { name, position }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Variable<'i> {
|
||||
pub fn scalar(name: &'i str) -> Self {
|
||||
Self::Scalar(Scalar { name })
|
||||
pub fn scalar(name: &'i str, position: usize) -> Self {
|
||||
Self::Scalar(Scalar::new(name, position))
|
||||
}
|
||||
|
||||
pub fn stream(name: &'i str) -> Self {
|
||||
Self::Stream(Stream { name })
|
||||
pub fn stream(name: &'i str, position: usize) -> Self {
|
||||
Self::Stream(Stream::new(name, position))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
@ -79,26 +97,20 @@ impl<'i> Variable<'i> {
|
||||
}
|
||||
|
||||
impl<'i> VariableWithLambda<'i> {
|
||||
pub fn scalar(name: &'i str) -> Self {
|
||||
Self::Scalar(ScalarWithLambda { name, lambda: None })
|
||||
pub fn scalar(name: &'i str, position: usize) -> Self {
|
||||
Self::Scalar(ScalarWithLambda::new(name, None, position))
|
||||
}
|
||||
|
||||
pub fn scalar_wl(name: &'i str, lambda: LambdaAST<'i>) -> Self {
|
||||
Self::Scalar(ScalarWithLambda {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
})
|
||||
pub fn scalar_wl(name: &'i str, lambda: LambdaAST<'i>, position: usize) -> Self {
|
||||
Self::Scalar(ScalarWithLambda::new(name, Some(lambda), position))
|
||||
}
|
||||
|
||||
pub fn stream(name: &'i str) -> Self {
|
||||
Self::Stream(StreamWithLambda { name, lambda: None })
|
||||
pub fn stream(name: &'i str, position: usize) -> Self {
|
||||
Self::Stream(StreamWithLambda::new(name, None, position))
|
||||
}
|
||||
|
||||
pub fn stream_wl(name: &'i str, lambda: LambdaAST<'i>) -> Self {
|
||||
Self::Stream(StreamWithLambda {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
})
|
||||
pub fn stream_wl(name: &'i str, lambda: LambdaAST<'i>, position: usize) -> Self {
|
||||
Self::Stream(StreamWithLambda::new(name, Some(lambda), position))
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
@ -117,15 +129,23 @@ impl<'i> VariableWithLambda<'i> {
|
||||
|
||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda_scalar(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let scalar = ScalarWithLambda::from_raw_lambda(name, lambda);
|
||||
pub(crate) fn from_raw_lambda_scalar(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let scalar = ScalarWithLambda::from_raw_lambda(name, lambda, position);
|
||||
Self::Scalar(scalar)
|
||||
}
|
||||
|
||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda_stream(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let stream = StreamWithLambda::from_raw_lambda(name, lambda);
|
||||
pub(crate) fn from_raw_lambda_stream(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let stream = StreamWithLambda::from_raw_lambda(name, lambda, position);
|
||||
Self::Stream(stream)
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,11 @@ Instr: Box<Instruction<'input>> = {
|
||||
}
|
||||
};
|
||||
|
||||
let output = output.map(CallOutputValue::Variable).unwrap_or(CallOutputValue::None);
|
||||
let args = Rc::new(args);
|
||||
let call = Call { triplet, args, output};
|
||||
let span = Span { left, right };
|
||||
let output = output.map(CallOutputValue::Variable).unwrap_or(CallOutputValue::None);
|
||||
let call = Call::new(triplet, args, output);
|
||||
let span = Span::new(left, right);
|
||||
|
||||
validator.met_call(&call, span);
|
||||
|
||||
Box::new(Instruction::Call(call))
|
||||
@ -39,42 +40,51 @@ Instr: Box<Instruction<'input>> = {
|
||||
|
||||
<left: @L> "(" ap <arg:ApArgument> <result:ApResult> ")" <right: @R> => {
|
||||
let apply = Ap::new(arg, result);
|
||||
let span = Span { left, right };
|
||||
|
||||
let span = Span::new(left, right);
|
||||
validator.met_ap(&apply, span);
|
||||
|
||||
Box::new(Instruction::Ap(apply))
|
||||
},
|
||||
|
||||
"(" seq <l:Instr> <r:Instr> ")" => Box::new(Instruction::Seq(Seq(l, r))),
|
||||
"(" par <l:Instr> <r:Instr> ")" => Box::new(Instruction::Par(Par(l, r))),
|
||||
"(" seq <l:Instr> <r:Instr> ")" => Box::new(Instruction::Seq(Seq::new(l, r))),
|
||||
"(" par <l:Instr> <r:Instr> ")" => Box::new(Instruction::Par(Par::new(l, r))),
|
||||
"(" null ")" => Box::new(Instruction::Null(Null)),
|
||||
|
||||
<left: @L> "(" fold <iterable:FoldScalarIterable> <iterator_name:Scalar> <i:Instr> ")" <right: @R> => {
|
||||
let iterator = Scalar { name: iterator_name };
|
||||
let instruction = Rc::new(*i);
|
||||
let fold = FoldScalar { iterable, iterator, instruction };
|
||||
let span = Span { left, right };
|
||||
<left: @L> "(" new <variable: ScriptVariable> <instruction:Instr> ")" <right: @R> => {
|
||||
let span = Span::new(left, right);
|
||||
let new = New::new(variable, instruction, span);
|
||||
|
||||
validator.met_new(&new, span);
|
||||
|
||||
Box::new(Instruction::New(new))
|
||||
},
|
||||
|
||||
<left: @L> "(" fold <iterable:FoldScalarIterable> <iterator:Scalar> <i:Instr> ")" <right: @R> => {
|
||||
let iterator = Scalar::new(iterator.0, iterator.1);
|
||||
let span = Span::new(left, right);
|
||||
let fold = FoldScalar::new(iterable, iterator, *i, span);
|
||||
|
||||
validator.met_fold_scalar(&fold, span);
|
||||
|
||||
Box::new(Instruction::FoldScalar(fold))
|
||||
},
|
||||
|
||||
<left: @L> "(" fold <stream_name:Stream> <iterator_name:Scalar> <i:Instr> ")" <right: @R> => {
|
||||
let iterable = Stream { name: stream_name };
|
||||
let iterator = Scalar { name: iterator_name };
|
||||
let instruction = Rc::new(*i);
|
||||
let fold = FoldStream { iterable, iterator, instruction };
|
||||
<left: @L> "(" fold <stream:Stream> <iterator:Scalar> <i:Instr> ")" <right: @R> => {
|
||||
let iterable = Stream::new(stream.0, stream.1);
|
||||
let iterator = Scalar::new(iterator.0, iterator.1);
|
||||
let span = Span::new(left, right);
|
||||
let fold = FoldStream::new(iterable, iterator, *i, span);
|
||||
|
||||
let span = Span { left, right };
|
||||
validator.meet_fold_stream(&fold, span);
|
||||
|
||||
Box::new(Instruction::FoldStream(fold))
|
||||
},
|
||||
|
||||
<left: @L> "(" next <iterator_name:Scalar> ")" <right: @R> => {
|
||||
let iterator = Scalar { name: iterator_name };
|
||||
let next = Next { iterator };
|
||||
let span = Span { left, right };
|
||||
<left: @L> "(" next <iterator:Scalar> ")" <right: @R> => {
|
||||
let iterator = Scalar::new(iterator.0, iterator.1);
|
||||
let next = Next::new(iterator);
|
||||
let span = Span::new(left, right);
|
||||
validator.met_next(&next, span);
|
||||
|
||||
Box::new(Instruction::Next(next))
|
||||
@ -83,16 +93,16 @@ Instr: Box<Instruction<'input>> = {
|
||||
"(" xor <l:Instr> <r:Instr> ")" => Box::new(Instruction::Xor(Xor(l, r))),
|
||||
|
||||
<left: @L> "(" match_ <l:Value> <r:Value> <i:Instr> ")" <right: @R> => {
|
||||
let match_ = Match { left_value: l, right_value: r, instruction: i};
|
||||
let span = Span { left, right };
|
||||
let match_ = Match::new(l, r, i);
|
||||
let span = Span::new(left, right);
|
||||
validator.met_match(&match_, span);
|
||||
|
||||
Box::new(Instruction::Match(match_))
|
||||
},
|
||||
|
||||
<left: @L> "(" mismatch <l:Value> <r:Value> <i:Instr> ")" <right: @R> => {
|
||||
let mismatch = MisMatch { left_value: l, right_value: r, instruction: i};
|
||||
let span = Span { left, right };
|
||||
let mismatch = MisMatch::new(l, r, i);
|
||||
let span = Span::new(left, right);
|
||||
validator.met_mismatch(&mismatch, span);
|
||||
|
||||
Box::new(Instruction::MisMatch(mismatch))
|
||||
@ -119,13 +129,13 @@ ApResult = ScriptVariable;
|
||||
CallOutput = ScriptVariable;
|
||||
|
||||
ScriptVariable: Variable<'input> = {
|
||||
<scalar:Scalar> => Variable::scalar(scalar),
|
||||
<stream:Stream> => Variable::stream(stream),
|
||||
<scalar:Scalar> => Variable::scalar(scalar.0, scalar.1),
|
||||
<stream:Stream> => Variable::stream(stream.0, stream.1),
|
||||
};
|
||||
|
||||
FoldScalarIterable: ScalarWithLambda<'input> = {
|
||||
<scalar:Scalar> => ScalarWithLambda::new(scalar, None),
|
||||
<scalar:ScalarWithLambda> => ScalarWithLambda::new(scalar.0, Some(scalar.1)),
|
||||
<scalar:Scalar> => ScalarWithLambda::new(scalar.0, None, scalar.1),
|
||||
<scalar:ScalarWithLambda> => ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2),
|
||||
};
|
||||
|
||||
Function = CallInstrValue;
|
||||
@ -135,10 +145,10 @@ ServiceId = CallInstrValue;
|
||||
CallInstrValue: CallInstrValue<'input> = {
|
||||
InitPeerId => CallInstrValue::InitPeerId,
|
||||
<l:Literal> => CallInstrValue::Literal(l),
|
||||
<scalar:Scalar> => CallInstrValue::Variable(VariableWithLambda::scalar(scalar)),
|
||||
<scalar:ScalarWithLambda> => CallInstrValue::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1)),
|
||||
<stream:Stream> => CallInstrValue::Variable(VariableWithLambda::stream(stream)),
|
||||
<stream:StreamWithLambda> => CallInstrValue::Variable(VariableWithLambda::stream_wl(stream.0, stream.1)),
|
||||
<scalar:Scalar> => CallInstrValue::Variable(VariableWithLambda::scalar(scalar.0, scalar.1)),
|
||||
<scalar:ScalarWithLambda> => CallInstrValue::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1, scalar.2)),
|
||||
<stream:Stream> => CallInstrValue::Variable(VariableWithLambda::stream(stream.0, stream.1)),
|
||||
<stream:StreamWithLambda> => CallInstrValue::Variable(VariableWithLambda::stream_wl(stream.0, stream.1, stream.2)),
|
||||
}
|
||||
|
||||
Arg = Value;
|
||||
@ -150,10 +160,10 @@ Value: Value<'input> = {
|
||||
<n:Number> => Value::Number(n),
|
||||
<b:Boolean> => Value::Boolean(b),
|
||||
EmptyArray => Value::EmptyArray,
|
||||
<scalar_name:Scalar> => Value::Variable(VariableWithLambda::scalar(scalar_name)),
|
||||
<scalar:ScalarWithLambda> => Value::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1)),
|
||||
<stream_name:Stream> => Value::Variable(VariableWithLambda::stream(stream_name)),
|
||||
<stream:StreamWithLambda> => Value::Variable(VariableWithLambda::stream_wl(stream.0, stream.1)),
|
||||
<scalar:Scalar> => Value::Variable(VariableWithLambda::scalar(scalar.0, scalar.1)),
|
||||
<scalar:ScalarWithLambda> => Value::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1, scalar.2)),
|
||||
<stream:Stream> => Value::Variable(VariableWithLambda::stream(stream.0, stream.1)),
|
||||
<stream:StreamWithLambda> => Value::Variable(VariableWithLambda::stream_wl(stream.0, stream.1, stream.2)),
|
||||
}
|
||||
|
||||
ApArgument: ApArgument<'input> = {
|
||||
@ -163,8 +173,8 @@ ApArgument: ApArgument<'input> = {
|
||||
<n:Number> => ApArgument::Number(n),
|
||||
<b:Boolean> => ApArgument::Boolean(b),
|
||||
EmptyArray => ApArgument::EmptyArray,
|
||||
<scalar:Scalar> => ApArgument::Scalar(ScalarWithLambda { name: scalar, lambda: None }),
|
||||
<scalar:ScalarWithLambda> => ApArgument::Scalar(ScalarWithLambda { name: scalar.0, lambda: Some(scalar.1) }),
|
||||
<scalar:Scalar> => ApArgument::Scalar(ScalarWithLambda::new(scalar.0, None, scalar.1)),
|
||||
<scalar:ScalarWithLambda> => ApArgument::Scalar(ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2)),
|
||||
}
|
||||
|
||||
extern {
|
||||
@ -179,10 +189,10 @@ extern {
|
||||
EmptyArray => Token::SquareBrackets,
|
||||
|
||||
Literal => Token::StringLiteral(<&'input str>),
|
||||
Scalar => Token::Scalar { name:<&'input str> },
|
||||
ScalarWithLambda => Token::ScalarWithLambda { name: <&'input str>, lambda: <LambdaAST<'input>>},
|
||||
Stream => Token::Stream { name: <&'input str> },
|
||||
StreamWithLambda => Token::StreamWithLambda {name: <&'input str>, lambda:<LambdaAST<'input>>},
|
||||
Scalar => Token::Scalar { name:<&'input str>, position: <usize> },
|
||||
ScalarWithLambda => Token::ScalarWithLambda { name: <&'input str>, lambda: <LambdaAST<'input>>, position: <usize> },
|
||||
Stream => Token::Stream { name: <&'input str>, position: <usize> },
|
||||
StreamWithLambda => Token::StreamWithLambda {name: <&'input str>, lambda:<LambdaAST<'input>>, position: <usize>},
|
||||
Number => Token::Number(<Number>),
|
||||
Boolean => Token::Boolean(<bool>),
|
||||
|
||||
@ -193,10 +203,11 @@ extern {
|
||||
ap => Token::Ap,
|
||||
seq => Token::Seq,
|
||||
par => Token::Par,
|
||||
null => Token::Null,
|
||||
fold => Token::Fold,
|
||||
xor => Token::Xor,
|
||||
new => Token::New,
|
||||
next => Token::Next,
|
||||
null => Token::Null,
|
||||
match_ => Token::Match,
|
||||
mismatch => Token::MisMatch,
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -136,6 +136,9 @@ fn parser_error_to_label(file_id: usize, error: ParserError) -> Label<usize> {
|
||||
InvalidCallTriplet(start, end) => {
|
||||
Label::primary(file_id, start..end).with_message(error.to_string())
|
||||
}
|
||||
IteratorRestrictionNotAllowed(start, end, _) => {
|
||||
Label::primary(file_id, start..end).with_message(error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,9 @@ pub enum ParserError {
|
||||
/// Semantic errors in a call instructions.
|
||||
#[error("call should have service id specified by peer part or function part")]
|
||||
InvalidCallTriplet(usize, usize),
|
||||
|
||||
#[error("new can't be applied to a '{2}' because it's an iterator")]
|
||||
IteratorRestrictionNotAllowed(usize, usize, String),
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for ParserError {
|
||||
|
@ -192,10 +192,11 @@ fn string_to_token(input: &str, start_pos: usize) -> LexerResult<Token> {
|
||||
AP_INSTR => Ok(Token::Ap),
|
||||
SEQ_INSTR => Ok(Token::Seq),
|
||||
PAR_INSTR => Ok(Token::Par),
|
||||
NULL_INSTR => Ok(Token::Null),
|
||||
FOLD_INSTR => Ok(Token::Fold),
|
||||
XOR_INSTR => Ok(Token::Xor),
|
||||
NEW_INSTR => Ok(Token::New),
|
||||
NEXT_INSTR => Ok(Token::Next),
|
||||
NULL_INSTR => Ok(Token::Null),
|
||||
MATCH_INSTR => Ok(Token::Match),
|
||||
MISMATCH_INSTR => Ok(Token::MisMatch),
|
||||
|
||||
@ -237,10 +238,11 @@ const CALL_INSTR: &str = "call";
|
||||
const AP_INSTR: &str = "ap";
|
||||
const SEQ_INSTR: &str = "seq";
|
||||
const PAR_INSTR: &str = "par";
|
||||
const NULL_INSTR: &str = "null";
|
||||
const FOLD_INSTR: &str = "fold";
|
||||
const XOR_INSTR: &str = "xor";
|
||||
const NEW_INSTR: &str = "new";
|
||||
const NEXT_INSTR: &str = "next";
|
||||
const NULL_INSTR: &str = "null";
|
||||
const MATCH_INSTR: &str = "match";
|
||||
const MISMATCH_INSTR: &str = "mismatch";
|
||||
|
||||
|
@ -274,17 +274,31 @@ impl<'input> CallVariableParser<'input> {
|
||||
|
||||
fn to_variable_token<'v>(&self, name: &'v str) -> Token<'v> {
|
||||
if self.state.is_first_stream_tag {
|
||||
Token::Stream { name }
|
||||
Token::Stream {
|
||||
name,
|
||||
position: self.start_pos,
|
||||
}
|
||||
} else {
|
||||
Token::Scalar { name }
|
||||
Token::Scalar {
|
||||
name,
|
||||
position: self.start_pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_variable_token_with_lambda<'v>(&self, name: &'v str, lambda: LambdaAST<'v>) -> Token<'v> {
|
||||
if self.state.is_first_stream_tag {
|
||||
Token::StreamWithLambda { name, lambda }
|
||||
Token::StreamWithLambda {
|
||||
name,
|
||||
lambda,
|
||||
position: self.start_pos,
|
||||
}
|
||||
} else {
|
||||
Token::ScalarWithLambda { name, lambda }
|
||||
Token::ScalarWithLambda {
|
||||
name,
|
||||
lambda,
|
||||
position: self.start_pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,14 @@ fn stream() {
|
||||
|
||||
lexer_test(
|
||||
STREAM,
|
||||
Single(Ok((0, Token::Stream { name: STREAM }, STREAM.len()))),
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::Stream {
|
||||
name: STREAM,
|
||||
position: 0,
|
||||
},
|
||||
STREAM.len(),
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
@ -284,6 +291,7 @@ fn lambda() {
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
])
|
||||
},
|
||||
position: 0,
|
||||
},
|
||||
LAMBDA.len(),
|
||||
))),
|
||||
@ -453,6 +461,7 @@ fn booleans() {
|
||||
0,
|
||||
Token::Scalar {
|
||||
name: NON_BOOL_CONST,
|
||||
position: 0,
|
||||
},
|
||||
NON_BOOL_CONST.len(),
|
||||
))),
|
||||
|
@ -34,17 +34,21 @@ pub enum Token<'input> {
|
||||
StringLiteral(&'input str),
|
||||
Scalar {
|
||||
name: &'input str,
|
||||
position: usize,
|
||||
},
|
||||
ScalarWithLambda {
|
||||
name: &'input str,
|
||||
lambda: LambdaAST<'input>,
|
||||
position: usize,
|
||||
},
|
||||
Stream {
|
||||
name: &'input str,
|
||||
position: usize,
|
||||
},
|
||||
StreamWithLambda {
|
||||
name: &'input str,
|
||||
lambda: LambdaAST<'input>,
|
||||
position: usize,
|
||||
},
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
@ -56,10 +60,11 @@ pub enum Token<'input> {
|
||||
Ap,
|
||||
Seq,
|
||||
Par,
|
||||
Null,
|
||||
Fold,
|
||||
Xor,
|
||||
New,
|
||||
Next,
|
||||
Null,
|
||||
Match,
|
||||
MisMatch,
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
pub mod air_parser;
|
||||
mod air_utils;
|
||||
pub(crate) mod lexer;
|
||||
mod span;
|
||||
|
||||
// air is auto-generated, so exclude it from `cargo fmt -- --check` and `cargo clippy`
|
||||
#[rustfmt::skip]
|
||||
@ -32,12 +33,7 @@ pub mod tests;
|
||||
pub use self::air_parser::parse;
|
||||
pub use air::AIRParser;
|
||||
pub use lexer::AIRLexer;
|
||||
pub use span::Span;
|
||||
pub use validator::VariableValidator;
|
||||
|
||||
use errors::ParserError;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
pub left: usize,
|
||||
pub right: usize,
|
||||
}
|
||||
|
34
crates/air-lib/air-parser/src/parser/span.rs
Normal file
34
crates/air-lib/air-parser/src/parser/span.rs
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2021 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 serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Span {
|
||||
pub left: usize,
|
||||
pub right: usize,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn new(left: usize, right: usize) -> Self {
|
||||
Self { left, right }
|
||||
}
|
||||
|
||||
pub fn contains(&self, position: usize) -> bool {
|
||||
self.left < position && position < self.right
|
||||
}
|
||||
}
|
@ -27,7 +27,7 @@ fn ap_with_literal() {
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::Literal("some_string"),
|
||||
Variable::stream("$stream"),
|
||||
Variable::stream("$stream", 27),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
@ -42,7 +42,7 @@ fn ap_with_number() {
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::Number(Number::Int(-100)),
|
||||
Variable::stream("$stream"),
|
||||
Variable::stream("$stream", 18),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
@ -55,7 +55,7 @@ fn ap_with_bool() {
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(ApArgument::Boolean(true), Variable::stream("$stream"));
|
||||
let expected = ap(ApArgument::Boolean(true), Variable::stream("$stream", 18));
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -69,7 +69,7 @@ fn ap_with_last_error() {
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::LastError(LastErrorPath::Message),
|
||||
Variable::stream("$stream"),
|
||||
Variable::stream("$stream", 33),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
|
@ -35,14 +35,15 @@ fn parse_json_path() {
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"peer_id",
|
||||
vec![ValueAccessor::FieldAccess { field_name: "a" }],
|
||||
15,
|
||||
)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal("hello"),
|
||||
Value::Variable(VariableWithLambda::scalar("name")),
|
||||
Value::Variable(VariableWithLambda::scalar("name", 68)),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::stream("$void")),
|
||||
CallOutputValue::Variable(Variable::stream("$void", 74)),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
@ -55,13 +56,13 @@ fn parse_empty_array() {
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id", 15)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id", 24)),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal(""),
|
||||
Value::EmptyArray,
|
||||
Value::Variable(VariableWithLambda::scalar("arg")),
|
||||
Value::Variable(VariableWithLambda::scalar("arg", 59)),
|
||||
]),
|
||||
CallOutputValue::None,
|
||||
);
|
||||
@ -77,11 +78,11 @@ fn parse_empty_array_2() {
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id", 15)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Variable(VariableWithLambda::scalar("k")),
|
||||
Value::Variable(VariableWithLambda::scalar("k", 55)),
|
||||
Value::EmptyArray,
|
||||
Value::EmptyArray,
|
||||
]),
|
||||
@ -206,11 +207,12 @@ fn parse_json_path_complex() {
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"m",
|
||||
vec![ValueAccessor::ArrayAccess { idx: 1 }],
|
||||
32,
|
||||
)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("void")),
|
||||
CallOutputValue::Variable(Variable::scalar("void", 75)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
@ -224,11 +226,12 @@ fn parse_json_path_complex() {
|
||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
],
|
||||
99,
|
||||
)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("void")),
|
||||
CallOutputValue::Variable(Variable::scalar("void", 162)),
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
@ -246,6 +249,7 @@ fn json_path_square_braces() {
|
||||
vec![ValueAccessor::FieldAccess {
|
||||
field_name: "peer_id",
|
||||
}],
|
||||
15,
|
||||
)),
|
||||
CallInstrValue::Literal("return"),
|
||||
CallInstrValue::Literal(""),
|
||||
@ -259,13 +263,15 @@ fn json_path_square_braces() {
|
||||
ValueAccessor::ArrayAccess { idx: 0 },
|
||||
ValueAccessor::FieldAccess { field_name: "abc" },
|
||||
],
|
||||
43,
|
||||
)),
|
||||
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"u",
|
||||
vec![ValueAccessor::FieldAccess { field_name: "name" }],
|
||||
64,
|
||||
)),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::stream("$void")),
|
||||
CallOutputValue::Variable(Variable::stream("$void", 74)),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
@ -352,14 +358,14 @@ fn seq_par_call() {
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("result_1")),
|
||||
CallOutputValue::Variable(Variable::scalar("result_1", 108)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal(peer_id),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("g")),
|
||||
CallOutputValue::Variable(Variable::scalar("g", 183)),
|
||||
),
|
||||
),
|
||||
call(
|
||||
@ -367,7 +373,7 @@ fn seq_par_call() {
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("result_2")),
|
||||
CallOutputValue::Variable(Variable::scalar("result_2", 273)),
|
||||
),
|
||||
);
|
||||
|
||||
@ -407,14 +413,14 @@ fn seq_with_empty_and_dash() {
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Literal("module-bytes")]),
|
||||
CallOutputValue::Variable(Variable::scalar("module-bytes")),
|
||||
CallOutputValue::Variable(Variable::scalar("module-bytes", 119)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("set_variables"),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Literal("module_config")]),
|
||||
CallOutputValue::Variable(Variable::scalar("module_config")),
|
||||
CallOutputValue::Variable(Variable::scalar("module_config", 201)),
|
||||
),
|
||||
),
|
||||
call(
|
||||
@ -422,7 +428,7 @@ fn seq_with_empty_and_dash() {
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Literal("blueprint")]),
|
||||
CallOutputValue::Variable(Variable::scalar("blueprint")),
|
||||
CallOutputValue::Variable(Variable::scalar("blueprint", 294)),
|
||||
),
|
||||
),
|
||||
seq(
|
||||
@ -431,10 +437,10 @@ fn seq_with_empty_and_dash() {
|
||||
CallInstrValue::Literal("add_module"),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![
|
||||
Value::Variable(VariableWithLambda::scalar("module-bytes")),
|
||||
Value::Variable(VariableWithLambda::scalar("module_config")),
|
||||
Value::Variable(VariableWithLambda::scalar("module-bytes", 381)),
|
||||
Value::Variable(VariableWithLambda::scalar("module_config", 394)),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::scalar("module")),
|
||||
CallOutputValue::Variable(Variable::scalar("module", 409)),
|
||||
),
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
@ -445,8 +451,9 @@ fn seq_with_empty_and_dash() {
|
||||
},
|
||||
args: Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
|
||||
"blueprint",
|
||||
490,
|
||||
))]),
|
||||
output: CallOutputValue::Variable(Variable::scalar("blueprint_id")),
|
||||
output: CallOutputValue::Variable(Variable::scalar("blueprint_id", 501)),
|
||||
}),
|
||||
seq(
|
||||
call(
|
||||
@ -455,8 +462,9 @@ fn seq_with_empty_and_dash() {
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
|
||||
"blueprint_id",
|
||||
589,
|
||||
))]),
|
||||
CallOutputValue::Variable(Variable::scalar("service_id")),
|
||||
CallOutputValue::Variable(Variable::scalar("service_id", 603)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("remote_peer_id"),
|
||||
@ -464,8 +472,9 @@ fn seq_with_empty_and_dash() {
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
|
||||
"service_id",
|
||||
671,
|
||||
))]),
|
||||
CallOutputValue::Variable(Variable::scalar("client_result")),
|
||||
CallOutputValue::Variable(Variable::scalar("client_result", 683)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -484,9 +493,9 @@ fn no_output() {
|
||||
let actual = parse(source_code);
|
||||
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("fname")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer", 15)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service", 21)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("fname", 29)),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
);
|
||||
|
@ -53,31 +53,47 @@ pub(super) fn seqnn() -> Instruction<'static> {
|
||||
seq(null(), null())
|
||||
}
|
||||
|
||||
pub(super) fn new<'i>(
|
||||
variable: Variable<'i>,
|
||||
instruction: Instruction<'i>,
|
||||
span: Span,
|
||||
) -> Instruction<'i> {
|
||||
Instruction::New(New {
|
||||
variable,
|
||||
instruction: Box::new(instruction),
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn null() -> Instruction<'static> {
|
||||
Instruction::Null(Null)
|
||||
}
|
||||
|
||||
pub(super) fn fold_scalar<'a>(
|
||||
iterable: ScalarWithLambda<'a>,
|
||||
iterator: &'a str,
|
||||
iterator: Scalar<'a>,
|
||||
instruction: Instruction<'a>,
|
||||
span: Span,
|
||||
) -> Instruction<'a> {
|
||||
Instruction::FoldScalar(FoldScalar {
|
||||
iterable,
|
||||
iterator: Scalar::new(iterator),
|
||||
iterator,
|
||||
instruction: std::rc::Rc::new(instruction),
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn fold_stream<'a>(
|
||||
stream_name: &'a str,
|
||||
iterator: &'a str,
|
||||
iterable: Stream<'a>,
|
||||
iterator: Scalar<'a>,
|
||||
instruction: Instruction<'a>,
|
||||
span: Span,
|
||||
) -> Instruction<'a> {
|
||||
Instruction::FoldStream(FoldStream {
|
||||
iterable: Stream::new(stream_name),
|
||||
iterator: Scalar::new(iterator),
|
||||
iterable,
|
||||
iterator,
|
||||
instruction: std::rc::Rc::new(instruction),
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,12 @@ fn parse_fold() {
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = fold_scalar(ScalarWithLambda::new("iterable", None), "i", null());
|
||||
let expected = fold_scalar(
|
||||
ScalarWithLambda::new("iterable", None, 15),
|
||||
Scalar::new("i", 24),
|
||||
null(),
|
||||
Span::new(9, 54),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
@ -84,9 +89,11 @@ fn fold_json_path() {
|
||||
ScalarWithLambda::from_raw_lambda(
|
||||
"members",
|
||||
vec![ValueAccessor::ArrayAccess { idx: 123321 }],
|
||||
33,
|
||||
),
|
||||
"m",
|
||||
Scalar::new("m", 52),
|
||||
null(),
|
||||
Span::new(27, 61),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
@ -98,7 +105,12 @@ fn fold_on_stream() {
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = fold_stream("$stream", "iterator", null());
|
||||
let expected = fold_stream(
|
||||
Stream::new("$stream", 15),
|
||||
Scalar::new("iterator", 23),
|
||||
null(),
|
||||
Span::new(9, 39),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
@ -119,9 +131,11 @@ fn comments() {
|
||||
},
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
],
|
||||
33,
|
||||
),
|
||||
"m",
|
||||
Scalar::new("m", 52),
|
||||
null(),
|
||||
Span::new(27, 61),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
@ -138,9 +152,10 @@ fn parse_fold_with_xor_par_seq() {
|
||||
let instruction = parse(&source_code);
|
||||
let instr = binary_instruction(*name);
|
||||
let expected = fold_scalar(
|
||||
ScalarWithLambda::new("iterable", None),
|
||||
"i",
|
||||
ScalarWithLambda::new("iterable", None, 6),
|
||||
Scalar::new("i", 15),
|
||||
instr(null(), null()),
|
||||
Span::new(0, 58),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ fn parse_match() {
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = match_(
|
||||
Value::Variable(VariableWithLambda::scalar("v1")),
|
||||
Value::Variable(VariableWithLambda::scalar("v2")),
|
||||
Value::Variable(VariableWithLambda::scalar("v1", 16)),
|
||||
Value::Variable(VariableWithLambda::scalar("v2", 19)),
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
@ -43,7 +43,7 @@ fn parse_match_with_init_peer_id() {
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = match_(
|
||||
Value::Variable(VariableWithLambda::scalar("v1")),
|
||||
Value::Variable(VariableWithLambda::scalar("v1", 16)),
|
||||
Value::InitPeerId,
|
||||
null(),
|
||||
);
|
||||
@ -59,8 +59,8 @@ fn parse_mismatch() {
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = mismatch(
|
||||
Value::Variable(VariableWithLambda::scalar("v1")),
|
||||
Value::Variable(VariableWithLambda::scalar("v2")),
|
||||
Value::Variable(VariableWithLambda::scalar("v1", 19)),
|
||||
Value::Variable(VariableWithLambda::scalar("v2", 22)),
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
@ -74,7 +74,7 @@ fn match_with_bool() {
|
||||
)
|
||||
"#;
|
||||
|
||||
let left_value = Value::Variable(VariableWithLambda::scalar("isOnline"));
|
||||
let left_value = Value::Variable(VariableWithLambda::scalar("isOnline", 17));
|
||||
let right_value = Value::Boolean(true);
|
||||
let null = null();
|
||||
let expected = match_(left_value, right_value, null);
|
||||
@ -92,7 +92,7 @@ fn mismatch_with_bool() {
|
||||
"#;
|
||||
|
||||
let left_value = Value::Boolean(true);
|
||||
let right_value = Value::Variable(VariableWithLambda::scalar("isOnline"));
|
||||
let right_value = Value::Variable(VariableWithLambda::scalar("isOnline", 25));
|
||||
let null = null();
|
||||
let expected = mismatch(left_value, right_value, null);
|
||||
|
||||
|
@ -19,6 +19,7 @@ mod call;
|
||||
mod dsl;
|
||||
mod fold;
|
||||
mod match_;
|
||||
mod new;
|
||||
mod null;
|
||||
mod par;
|
||||
mod seq;
|
||||
|
84
crates/air-lib/air-parser/src/parser/tests/new.rs
Normal file
84
crates/air-lib/air-parser/src/parser/tests/new.rs
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2021 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::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
use crate::parser::ParserError;
|
||||
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
#[test]
|
||||
fn parse_new_with_scalar() {
|
||||
let source_code = r#"(new scalar
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = new(Variable::scalar("scalar", 5), null(), Span::new(0, 40));
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_new_with_stream() {
|
||||
let source_code = r#"(new $stream
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = new(Variable::stream("$stream", 5), null(), Span::new(0, 41));
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterators_cant_be_restricted() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(call "" ("" "") [] iterable)
|
||||
(fold iterable iterator
|
||||
(new iterator
|
||||
(call "" ("" "") [iterator])
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let lexer = crate::AIRLexer::new(source_code);
|
||||
|
||||
let parser = crate::AIRParser::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parser shouldn't fail");
|
||||
|
||||
let errors = validator.finalize();
|
||||
|
||||
assert_eq!(errors.len(), 1);
|
||||
println!("{:?}", errors);
|
||||
let error = &errors[0].error;
|
||||
let parser_error = match error {
|
||||
ParseError::User { error } => error,
|
||||
_ => panic!("unexpected error type"),
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
parser_error,
|
||||
ParserError::IteratorRestrictionNotAllowed(..)
|
||||
));
|
||||
}
|
@ -32,11 +32,11 @@ fn parse_seq() {
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id", 32)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id", 41)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name", 52)),
|
||||
Rc::new(vec![Value::EmptyArray, Value::EmptyArray]),
|
||||
CallOutputValue::Variable(Variable::scalar("output")),
|
||||
CallOutputValue::Variable(Variable::scalar("output", 75)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("peer_id"),
|
||||
@ -45,7 +45,7 @@ fn parse_seq() {
|
||||
Rc::new(vec![
|
||||
Value::Literal("hello"),
|
||||
Value::EmptyArray,
|
||||
Value::Variable(VariableWithLambda::scalar("name")),
|
||||
Value::Variable(VariableWithLambda::scalar("name", 154)),
|
||||
]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
@ -68,16 +68,16 @@ fn parse_seq_seq() {
|
||||
let expected = seq(
|
||||
seq(
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id", 53)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id", 62)),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name", 073)),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id", 115)),
|
||||
CallInstrValue::Literal("service_B"),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name", 147)),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
@ -88,9 +88,9 @@ fn parse_seq_seq() {
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal("hello"),
|
||||
Value::Variable(VariableWithLambda::scalar("name")),
|
||||
Value::Variable(VariableWithLambda::scalar("name", 248)),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::stream("$output")),
|
||||
CallOutputValue::Variable(Variable::stream("$output", 254)),
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
|
@ -47,6 +47,9 @@ pub struct VariableValidator<'i> {
|
||||
/// Contains all met iterable in call and next, they will be resolved after the whole parsing
|
||||
/// due to the way how lalrpop work.
|
||||
unresolved_iterables: MultiMap<&'i str, Span>,
|
||||
|
||||
/// Contains all names that should be checked that they are not iterators.
|
||||
check_for_non_iterators: Vec<(&'i str, Span)>,
|
||||
}
|
||||
|
||||
impl<'i> VariableValidator<'i> {
|
||||
@ -87,6 +90,13 @@ impl<'i> VariableValidator<'i> {
|
||||
self.met_iterator_definition(&fold.iterator, span);
|
||||
}
|
||||
|
||||
pub(super) fn met_new(&mut self, new: &New<'i>, span: Span) {
|
||||
self.check_for_non_iterators
|
||||
.push((variable_name(&new.variable), span));
|
||||
// new defines a new variable
|
||||
self.met_variable_definition(&new.variable, span);
|
||||
}
|
||||
|
||||
pub(super) fn met_next(&mut self, next: &Next<'i>, span: Span) {
|
||||
let iterable_name = next.iterator.name;
|
||||
// due to the right to left convolution in lalrpop, a next instruction will be met earlier
|
||||
@ -124,6 +134,12 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
for (name, span) in self.check_for_non_iterators.iter() {
|
||||
if self.contains_iterable(name, *span) {
|
||||
add_to_errors(*name, &mut errors, *span, Token::New);
|
||||
}
|
||||
}
|
||||
|
||||
errors
|
||||
}
|
||||
|
||||
@ -254,6 +270,9 @@ fn add_to_errors<'err, 'i>(
|
||||
let variable_name = variable_name.into();
|
||||
let error = match token {
|
||||
Token::Next => ParserError::UndefinedIterable(span.left, span.right, variable_name),
|
||||
Token::New => {
|
||||
ParserError::IteratorRestrictionNotAllowed(span.left, span.right, variable_name)
|
||||
}
|
||||
_ => ParserError::UndefinedVariable(span.left, span.right, variable_name),
|
||||
};
|
||||
let error = ParseError::User { error };
|
||||
|
@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Intended to track a number of executed instruction of each type. For instructions that
|
||||
/// have a corresponding state in data, it tracks number of executed instructions on
|
||||
/// current peer (executed) and overall number (seen) of met instructions of such type.
|
||||
@ -24,6 +26,7 @@ pub struct InstructionTracker {
|
||||
pub fold: FoldTracker,
|
||||
pub match_count: u32,
|
||||
pub mismatch_count: u32,
|
||||
pub new_tracker: NewTracker,
|
||||
pub next_count: u32,
|
||||
pub null_count: u32,
|
||||
pub par: ParTracker,
|
||||
@ -55,6 +58,14 @@ pub struct ParTracker {
|
||||
pub executed_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq)]
|
||||
pub struct NewTracker {
|
||||
/// Mapping from a new instruction position in a script
|
||||
/// to a number of their execution. This is needed to
|
||||
/// support private stream generation mappings.
|
||||
pub executed_count: HashMap<usize, u32>,
|
||||
}
|
||||
|
||||
impl InstructionTracker {
|
||||
pub fn meet_ap(&mut self) {
|
||||
self.ap.seen_count += 1;
|
||||
@ -111,4 +122,24 @@ impl InstructionTracker {
|
||||
pub fn meet_xor(&mut self) {
|
||||
self.xor_count += 1;
|
||||
}
|
||||
|
||||
pub fn meet_new(&mut self, position: usize) {
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
|
||||
match self.new_tracker.executed_count.entry(position) {
|
||||
Occupied(mut entry) => *entry.get_mut() += 1,
|
||||
Vacant(entry) => {
|
||||
entry.insert(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl NewTracker {
|
||||
pub fn get_iteration(&self, position: usize) -> u32 {
|
||||
self.executed_count
|
||||
.get(&position)
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
## Version 0.2.2
|
||||
|
||||
[PR 169](https://github.com/fluencelabs/aquavm/pull/169):
|
||||
- added a new field for tracking generations of private streams
|
||||
|
||||
## Version 0.2.1
|
||||
|
||||
[PR 130](https://github.com/fluencelabs/aquavm/pull/130):
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "air-interpreter-data"
|
||||
description = "Data format of the AIR interpreter"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
@ -15,13 +15,13 @@
|
||||
*/
|
||||
|
||||
use super::ExecutedState;
|
||||
use super::GlobalStreamGens;
|
||||
use super::RestrictedStreamGens;
|
||||
use super::DATA_FORMAT_VERSION;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub type StreamGenerations = HashMap<String, u32>;
|
||||
pub type ExecutionTrace = Vec<ExecutedState>;
|
||||
|
||||
/// The AIR interpreter could be considered as a function
|
||||
@ -33,9 +33,11 @@ pub struct InterpreterData {
|
||||
/// Trace of AIR execution, which contains executed call, par and fold states.
|
||||
pub trace: ExecutionTrace,
|
||||
|
||||
/// Contains maximum generation for each stream. This info will be used while merging
|
||||
/// values in streams.
|
||||
pub streams: StreamGenerations,
|
||||
/// Contains maximum generation for each global stream. This info will be used while merging
|
||||
/// values in streams. This field is also needed for backward compatibility with
|
||||
/// <= 0.2.1 versions.
|
||||
#[serde(rename = "streams")] // for compatibility with versions <= 0.2.1
|
||||
pub global_streams: GlobalStreamGens,
|
||||
|
||||
/// Version of this data format.
|
||||
pub version: semver::Version,
|
||||
@ -44,28 +46,37 @@ pub struct InterpreterData {
|
||||
#[serde(default)]
|
||||
#[serde(rename = "lcid")]
|
||||
pub last_call_request_id: u32,
|
||||
|
||||
/// Contains maximum generation for each private stream. This info will be used while merging
|
||||
/// values in streams.
|
||||
#[serde(default)]
|
||||
#[serde(rename = "r_streams")]
|
||||
pub restricted_streams: RestrictedStreamGens,
|
||||
}
|
||||
|
||||
impl InterpreterData {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
trace: <_>::default(),
|
||||
streams: <_>::default(),
|
||||
global_streams: <_>::default(),
|
||||
version: DATA_FORMAT_VERSION.deref().clone(),
|
||||
last_call_request_id: 0,
|
||||
restricted_streams: <_>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_execution_result(
|
||||
trace: ExecutionTrace,
|
||||
streams: StreamGenerations,
|
||||
streams: GlobalStreamGens,
|
||||
restricted_streams: RestrictedStreamGens,
|
||||
last_call_request_id: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
trace,
|
||||
streams,
|
||||
global_streams: streams,
|
||||
version: DATA_FORMAT_VERSION.deref().clone(),
|
||||
last_call_request_id,
|
||||
restricted_streams,
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,14 +109,14 @@ mod tests {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct InterpreterData0_2_0 {
|
||||
pub trace: ExecutionTrace,
|
||||
pub streams: StreamGenerations,
|
||||
pub streams: GlobalStreamGens,
|
||||
pub version: semver::Version,
|
||||
}
|
||||
|
||||
// test 0.2.0 to 0.2.1 conversion
|
||||
// test 0.2.0 to 0.2.2 conversion
|
||||
let data_0_2_0 = InterpreterData0_2_0 {
|
||||
trace: ExecutionTrace::default(),
|
||||
streams: StreamGenerations::default(),
|
||||
streams: GlobalStreamGens::default(),
|
||||
version: semver::Version::new(0, 2, 0),
|
||||
};
|
||||
|
||||
@ -113,10 +124,41 @@ mod tests {
|
||||
let data_0_2_1 = serde_json::from_slice::<InterpreterData>(&data_0_2_0_se);
|
||||
assert!(data_0_2_1.is_ok());
|
||||
|
||||
// test 0.2.1 to 0.2.1 conversion
|
||||
let data_0_2_1 = InterpreterData::default();
|
||||
// test 0.2.2 to 0.2.0 conversion
|
||||
let data_0_2_2 = InterpreterData::default();
|
||||
let data_0_2_2_se = serde_json::to_vec(&data_0_2_2).unwrap();
|
||||
let data_0_2_0 = serde_json::from_slice::<InterpreterData0_2_0>(&data_0_2_2_se);
|
||||
assert!(data_0_2_0.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatible_with_0_2_1_version() {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct InterpreterData0_2_1 {
|
||||
pub trace: ExecutionTrace,
|
||||
pub streams: GlobalStreamGens,
|
||||
pub version: semver::Version,
|
||||
#[serde(default)]
|
||||
#[serde(rename = "lcid")]
|
||||
pub last_call_request_id: u32,
|
||||
}
|
||||
|
||||
// test 0.2.1 to 0.2.2 conversion
|
||||
let data_0_2_1 = InterpreterData0_2_1 {
|
||||
trace: ExecutionTrace::default(),
|
||||
streams: GlobalStreamGens::default(),
|
||||
version: semver::Version::new(0, 2, 1),
|
||||
last_call_request_id: 1,
|
||||
};
|
||||
|
||||
let data_0_2_1_se = serde_json::to_vec(&data_0_2_1).unwrap();
|
||||
let data_0_2_0 = serde_json::from_slice::<InterpreterData0_2_0>(&data_0_2_1_se);
|
||||
let data_0_2_2 = serde_json::from_slice::<InterpreterData>(&data_0_2_1_se);
|
||||
assert!(data_0_2_2.is_ok());
|
||||
|
||||
// test 0.2.2 to 0.2.1 conversion
|
||||
let data_0_2_2 = InterpreterData::default();
|
||||
let data_0_2_2_se = serde_json::to_vec(&data_0_2_2).unwrap();
|
||||
let data_0_2_0 = serde_json::from_slice::<InterpreterData0_2_1>(&data_0_2_2_se);
|
||||
assert!(data_0_2_0.is_ok());
|
||||
}
|
||||
}
|
||||
|
@ -14,15 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![deny(
|
||||
dead_code,
|
||||
nonstandard_style,
|
||||
unused_imports,
|
||||
unused_mut,
|
||||
unused_variables,
|
||||
unused_unsafe,
|
||||
unreachable_patterns
|
||||
)]
|
||||
|
||||
mod executed_state;
|
||||
mod interpreter_data;
|
||||
mod stream_generations;
|
||||
|
||||
pub use executed_state::*;
|
||||
pub use interpreter_data::*;
|
||||
pub use stream_generations::*;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub static DATA_FORMAT_VERSION: Lazy<semver::Version> = Lazy::new(|| {
|
||||
semver::Version::from_str("0.2.1").expect("invalid data format version specified")
|
||||
semver::Version::from_str("0.2.2").expect("invalid data format version specified")
|
||||
});
|
||||
|
31
crates/air-lib/interpreter-data/src/stream_generations.rs
Normal file
31
crates/air-lib/interpreter-data/src/stream_generations.rs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2021 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 std::collections::HashMap;
|
||||
|
||||
/// Mapping from a stream name to it's generation count.
|
||||
/// Similar to pi-calculus non-restricted names/channels.
|
||||
pub type GlobalStreamGens = HashMap<String, u32>;
|
||||
|
||||
/// Mapping from a stream name to
|
||||
/// position of a new instruction in a script that creates a scope for a stream
|
||||
/// to vector where each position represents a corresponding iteration.
|
||||
///
|
||||
/// Vec<u32> is needed because a new instruction could be placed into a fold instruction,
|
||||
/// so it could be met several times during script execution. This field anchors iteration
|
||||
/// where it was met.
|
||||
/// Similar to pi-calculus restricted names/channels.
|
||||
pub type RestrictedStreamGens = HashMap<String, HashMap<u32, Vec<u32>>>;
|
@ -33,7 +33,14 @@ pub fn scalar(result: JValue) -> ExecutedState {
|
||||
ExecutedState::Call(CallResult::Executed(value))
|
||||
}
|
||||
|
||||
pub fn stream_jvalue(result: JValue, generation: u32) -> ExecutedState {
|
||||
pub fn scalar_number(result: impl Into<serde_json::Number>) -> ExecutedState {
|
||||
let result = JValue::Number(result.into());
|
||||
let value = Rc::new(result);
|
||||
|
||||
ExecutedState::Call(CallResult::executed_scalar(value))
|
||||
}
|
||||
|
||||
pub fn stream(result: JValue, generation: u32) -> ExecutedState {
|
||||
let call_result = CallResult::executed_stream(Rc::new(result), generation);
|
||||
ExecutedState::Call(call_result)
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ pub fn data_from_result(result: &RawAVMOutcome) -> InterpreterData {
|
||||
}
|
||||
|
||||
pub fn raw_data_from_trace(trace: ExecutionTrace) -> Vec<u8> {
|
||||
let data = InterpreterData::from_execution_result(trace, <_>::default(), 0);
|
||||
let data = InterpreterData::from_execution_result(trace, <_>::default(), <_>::default(), 0);
|
||||
serde_json::to_vec(&data).expect("default serializer shouldn't fail")
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ use super::KeeperError;
|
||||
use super::KeeperResult;
|
||||
use super::TraceSlider;
|
||||
|
||||
use air_interpreter_data::GlobalStreamGens;
|
||||
use air_interpreter_data::InterpreterData;
|
||||
use air_interpreter_data::StreamGenerations;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -28,7 +28,7 @@ use std::collections::HashMap;
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct MergeCtx {
|
||||
pub slider: TraceSlider,
|
||||
pub streams: StreamGenerations,
|
||||
pub streams: GlobalStreamGens,
|
||||
}
|
||||
|
||||
impl MergeCtx {
|
||||
@ -47,7 +47,7 @@ impl MergeCtx {
|
||||
|
||||
Self {
|
||||
slider,
|
||||
streams: data.streams,
|
||||
streams: data.global_streams,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user