mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 15:20:16 +00:00
Introduce canon
instruction (#292)
This commit is contained in:
parent
5c7e88e0f2
commit
5072fba9d6
39
CHANGELOG.md
39
CHANGELOG.md
@ -1,14 +1,41 @@
|
|||||||
## Version 0.24.0 (2021-04-21)
|
## Version 0.27.0 (2022-08-23)
|
||||||
|
|
||||||
|
[PR 292](https://github.com/fluencelabs/aquavm/pull/292):
|
||||||
|
Introduced a new `canon` instruction
|
||||||
|
|
||||||
|
[PR 296](https://github.com/fluencelabs/aquavm/pull/296):
|
||||||
|
A bug with an inappropriate check of states in the `Ap` merger was fixed
|
||||||
|
|
||||||
|
## Version 0.26.0 (2022-08-19)
|
||||||
|
|
||||||
|
[PR 294](https://github.com/fluencelabs/aquavm/pull/294):
|
||||||
|
Accompanying to air-interpreter update that makes interface more handy for `air-trace`
|
||||||
|
|
||||||
|
## Version 0.25.0 (2022-07-27)
|
||||||
|
|
||||||
|
[PR 287](https://github.com/fluencelabs/aquavm/pull/287):
|
||||||
|
Memory leak introduced by switching to reactor/command model in WASI fixed
|
||||||
|
|
||||||
|
[PR 276](https://github.com/fluencelabs/aquavm/pull/276):
|
||||||
|
AquaVM performance was improved by removing excess logging
|
||||||
|
|
||||||
|
[PR 273](https://github.com/fluencelabs/aquavm/pull/273):
|
||||||
|
Introduced `TracePos` for `TraceHandler` positions
|
||||||
|
|
||||||
|
[PR 270](https://github.com/fluencelabs/aquavm/pull/270):
|
||||||
|
A bug with empty subtrace lore in TraceHandler was fixed
|
||||||
|
|
||||||
|
## Version 0.24.0 (2022-04-21)
|
||||||
|
|
||||||
[PR 253](https://github.com/fluencelabs/aquavm/pull/253):
|
[PR 253](https://github.com/fluencelabs/aquavm/pull/253):
|
||||||
Introduced %ttl% keyword
|
Introduced %ttl% keyword
|
||||||
|
|
||||||
## Version 0.24.0 (2021-04-20)
|
## Version 0.24.0 (2022-04-20)
|
||||||
|
|
||||||
[PR 250](https://github.com/fluencelabs/aquavm/pull/250):
|
[PR 250](https://github.com/fluencelabs/aquavm/pull/250):
|
||||||
Introduced %timestamp% keyword
|
Introduced %timestamp% keyword
|
||||||
|
|
||||||
## Version 0.23.0 (2021-04-20)
|
## Version 0.23.0 (2022-04-20)
|
||||||
|
|
||||||
[PR 248](https://github.com/fluencelabs/aquavm/pull/248):
|
[PR 248](https://github.com/fluencelabs/aquavm/pull/248):
|
||||||
Introduced new for scalars
|
Introduced new for scalars
|
||||||
@ -16,7 +43,7 @@ Introduced new for scalars
|
|||||||
[PR 244](https://github.com/fluencelabs/aquavm/pull/244):
|
[PR 244](https://github.com/fluencelabs/aquavm/pull/244):
|
||||||
Stack size was increased to 50 MiB
|
Stack size was increased to 50 MiB
|
||||||
|
|
||||||
## Version 0.22.0 (2021-04-14)
|
## Version 0.22.0 (2022-04-14)
|
||||||
|
|
||||||
[PR 243](https://github.com/fluencelabs/aquavm/pull/243):
|
[PR 243](https://github.com/fluencelabs/aquavm/pull/243):
|
||||||
Clean scalars at the end of scope, only one next in a fold over scalar is possible now
|
Clean scalars at the end of scope, only one next in a fold over scalar is possible now
|
||||||
@ -27,13 +54,13 @@ Test refactoring
|
|||||||
[PR 228](https://github.com/fluencelabs/aquavm/pull/228):
|
[PR 228](https://github.com/fluencelabs/aquavm/pull/228):
|
||||||
Improve stream determinism
|
Improve stream determinism
|
||||||
|
|
||||||
## Version 0.21.0 (2021-02-26)
|
## Version 0.21.0 (2022-02-26)
|
||||||
|
|
||||||
[PR 225](https://github.com/fluencelabs/aquavm/pull/225):
|
[PR 225](https://github.com/fluencelabs/aquavm/pull/225):
|
||||||
Introduce recursive streams
|
Introduce recursive streams
|
||||||
|
|
||||||
[PR 224](https://github.com/fluencelabs/aquavm/pull/224) [PR 220](https://github.com/fluencelabs/aquavm/pull/224) [PR 217](https://github.com/fluencelabs/aquavm/pull/217) [PR 215](https://github.com/fluencelabs/aquavm/pull/215) [PR 212](https://github.com/fluencelabs/aquavm/pull/212) [PR 207](https://github.com/fluencelabs/aquavm/pull/207):
|
[PR 224](https://github.com/fluencelabs/aquavm/pull/224) [PR 220](https://github.com/fluencelabs/aquavm/pull/224) [PR 217](https://github.com/fluencelabs/aquavm/pull/217) [PR 215](https://github.com/fluencelabs/aquavm/pull/215) [PR 212](https://github.com/fluencelabs/aquavm/pull/212) [PR 207](https://github.com/fluencelabs/aquavm/pull/207):
|
||||||
Various bugs fixed
|
Various bugs were fixed
|
||||||
|
|
||||||
[PR 210](https://github.com/fluencelabs/aquavm/pull/210):
|
[PR 210](https://github.com/fluencelabs/aquavm/pull/210):
|
||||||
Add API for returning AquaVM consumed memory size
|
Add API for returning AquaVM consumed memory size
|
||||||
|
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -13,7 +13,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "air"
|
name = "air"
|
||||||
version = "0.26.0"
|
version = "0.27.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"air-execution-info-collector",
|
"air-execution-info-collector",
|
||||||
"air-interpreter-data",
|
"air-interpreter-data",
|
||||||
@ -72,7 +72,7 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "air-interpreter"
|
name = "air-interpreter"
|
||||||
version = "0.26.0"
|
version = "0.27.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"air",
|
"air",
|
||||||
"air-interpreter-interface",
|
"air-interpreter-interface",
|
||||||
@ -88,7 +88,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "air-interpreter-data"
|
name = "air-interpreter-data"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"air-utils",
|
"air-utils",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -199,6 +199,7 @@ dependencies = [
|
|||||||
"air-interpreter-data",
|
"air-interpreter-data",
|
||||||
"air-log-targets",
|
"air-log-targets",
|
||||||
"air-parser",
|
"air-parser",
|
||||||
|
"bimap",
|
||||||
"log",
|
"log",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -307,6 +308,12 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bimap"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bincode"
|
name = "bincode"
|
||||||
version = "1.3.3"
|
version = "1.3.3"
|
||||||
@ -1829,7 +1836,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyplets"
|
name = "polyplets"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"marine-macro",
|
"marine-macro",
|
||||||
"marine-rs-sdk-main",
|
"marine-rs-sdk-main",
|
||||||
|
26
README.md
26
README.md
@ -86,6 +86,24 @@ Example:
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### canon
|
||||||
|
|
||||||
|
```wasm
|
||||||
|
(canon "peer_id" <$stream> <#canon_stream>)
|
||||||
|
```
|
||||||
|
|
||||||
|
- executes on peer_id, takes $stream as it is on the moment of first canonicalization
|
||||||
|
- every next execution #canon_stream will be the same — as first seen by peer_id
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```wasm
|
||||||
|
(seq
|
||||||
|
(ap user $users)
|
||||||
|
(canon "peer_id" $stream #canon_stream)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
#### match/mismath
|
#### match/mismath
|
||||||
|
|
||||||
```wasm
|
```wasm
|
||||||
@ -178,3 +196,11 @@ Example
|
|||||||
- versioned
|
- versioned
|
||||||
- could be used only by call and fold instructions (more instructions for streams to come)
|
- could be used only by call and fold instructions (more instructions for streams to come)
|
||||||
- could be turned to scalar (canonicalized)
|
- could be turned to scalar (canonicalized)
|
||||||
|
|
||||||
|
#### Canonicalized streams
|
||||||
|
|
||||||
|
- contains an array of elements that was in a stream at the moment of canonicalization
|
||||||
|
- canonicalized streams are imutable and fully consistent as scalars
|
||||||
|
- has the same algebra as a stream for `match`/`mismatch` and `call` argument
|
||||||
|
- has the same algebra as a scalar for `new`
|
||||||
|
- has mixed behaviour for with other instructions
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "air-interpreter"
|
name = "air-interpreter"
|
||||||
version = "0.26.0"
|
version = "0.27.0"
|
||||||
description = "Crate-wrapper for air"
|
description = "Crate-wrapper for air"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "air"
|
name = "air"
|
||||||
version = "0.26.0"
|
version = "0.27.0"
|
||||||
description = "Interpreter of AIR scripts intended to coordinate request flow in the Fluence network"
|
description = "Interpreter of AIR scripts intended to coordinate request flow in the Fluence network"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -30,8 +30,8 @@ use crate::SecurityTetraplet;
|
|||||||
use apply_to_arguments::*;
|
use apply_to_arguments::*;
|
||||||
use utils::*;
|
use utils::*;
|
||||||
|
|
||||||
use air_parser::ast;
|
|
||||||
use air_parser::ast::Ap;
|
use air_parser::ast::Ap;
|
||||||
|
use air_parser::ast::ApResult;
|
||||||
use air_trace_handler::MergerApResult;
|
use air_trace_handler::MergerApResult;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -57,7 +57,7 @@ impl<'i> super::ExecutableInstruction<'i> for Ap<'i> {
|
|||||||
/// This function is intended to check whether a Ap instruction should produce
|
/// This function is intended to check whether a Ap instruction should produce
|
||||||
/// a new state in data.
|
/// a new state in data.
|
||||||
fn should_touch_trace(ap: &Ap<'_>) -> bool {
|
fn should_touch_trace(ap: &Ap<'_>) -> bool {
|
||||||
matches!(ap.result, ast::Variable::Stream(_))
|
matches!(ap.result, ApResult::Stream(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_merger_ap_result(
|
fn to_merger_ap_result(
|
||||||
@ -77,16 +77,14 @@ fn to_merger_ap_result(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_context<'ctx>(
|
fn update_context<'ctx>(
|
||||||
ap_result_type: &ast::Variable<'ctx>,
|
ap_result_type: &ApResult<'ctx>,
|
||||||
merger_ap_result: &MergerApResult,
|
merger_ap_result: &MergerApResult,
|
||||||
result: ValueAggregate,
|
result: ValueAggregate,
|
||||||
exec_ctx: &mut ExecutionCtx<'ctx>,
|
exec_ctx: &mut ExecutionCtx<'ctx>,
|
||||||
) -> ExecutionResult<Option<u32>> {
|
) -> ExecutionResult<Option<u32>> {
|
||||||
use ast::Variable::*;
|
|
||||||
|
|
||||||
match ap_result_type {
|
match ap_result_type {
|
||||||
Scalar(scalar) => exec_ctx.scalars.set_value(scalar.name, result).map(|_| None),
|
ApResult::Scalar(scalar) => exec_ctx.scalars.set_scalar_value(scalar.name, result).map(|_| None),
|
||||||
Stream(stream) => {
|
ApResult::Stream(stream) => {
|
||||||
let generation = ap_result_to_generation(merger_ap_result);
|
let generation = ap_result_to_generation(merger_ap_result);
|
||||||
exec_ctx
|
exec_ctx
|
||||||
.streams
|
.streams
|
||||||
|
@ -36,6 +36,7 @@ pub(super) fn apply_to_arg(
|
|||||||
Boolean(value) => apply_const(*value, exec_ctx, trace_ctx),
|
Boolean(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||||
EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx),
|
EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx),
|
||||||
Scalar(scalar) => apply_scalar(scalar, exec_ctx, trace_ctx, should_touch_trace)?,
|
Scalar(scalar) => apply_scalar(scalar, exec_ctx, trace_ctx, should_touch_trace)?,
|
||||||
|
CanonStream(canon_stream) => apply_canon_stream(canon_stream, exec_ctx)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -71,7 +72,7 @@ fn apply_scalar(
|
|||||||
) -> ExecutionResult<ValueAggregate> {
|
) -> ExecutionResult<ValueAggregate> {
|
||||||
// TODO: refactor this code after boxed value
|
// TODO: refactor this code after boxed value
|
||||||
match &scalar.lambda {
|
match &scalar.lambda {
|
||||||
Some(lambda) => apply_scalar_wl_impl(scalar.name, scalar.position, lambda, exec_ctx, trace_ctx),
|
Some(lambda) => apply_scalar_wl_impl(scalar.name, lambda, exec_ctx, trace_ctx),
|
||||||
None => apply_scalar_impl(scalar.name, exec_ctx, trace_ctx, should_touch_trace),
|
None => apply_scalar_impl(scalar.name, exec_ctx, trace_ctx, should_touch_trace),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,15 +107,53 @@ fn apply_scalar_impl(
|
|||||||
|
|
||||||
fn apply_scalar_wl_impl(
|
fn apply_scalar_wl_impl(
|
||||||
scalar_name: &str,
|
scalar_name: &str,
|
||||||
position: usize,
|
|
||||||
lambda: &LambdaAST<'_>,
|
lambda: &LambdaAST<'_>,
|
||||||
exec_ctx: &ExecutionCtx<'_>,
|
exec_ctx: &ExecutionCtx<'_>,
|
||||||
trace_ctx: &TraceHandler,
|
trace_ctx: &TraceHandler,
|
||||||
) -> ExecutionResult<ValueAggregate> {
|
) -> ExecutionResult<ValueAggregate> {
|
||||||
let variable = Variable::scalar(scalar_name, position);
|
let variable = Variable::scalar(scalar_name);
|
||||||
let (jvalue, tetraplet) = apply_lambda(variable, lambda, exec_ctx)?;
|
let (jvalue, tetraplet) = apply_lambda(variable, lambda, exec_ctx)?;
|
||||||
let tetraplet = Rc::new(tetraplet);
|
let tetraplet = Rc::new(tetraplet);
|
||||||
let result = ValueAggregate::new(Rc::new(jvalue), tetraplet, trace_ctx.trace_pos());
|
let result = ValueAggregate::new(Rc::new(jvalue), tetraplet, trace_ctx.trace_pos());
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_canon_stream(
|
||||||
|
canon_stream: &ast::CanonStreamWithLambda<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'_>,
|
||||||
|
) -> ExecutionResult<ValueAggregate> {
|
||||||
|
match &canon_stream.lambda {
|
||||||
|
Some(lambda) => apply_canon_stream_with_lambda(canon_stream.name, lambda, exec_ctx),
|
||||||
|
None => apply_canon_stream_without_lambda(canon_stream.name, exec_ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_canon_stream_with_lambda(
|
||||||
|
stream_name: &str,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'_>,
|
||||||
|
) -> ExecutionResult<ValueAggregate> {
|
||||||
|
use crate::execution_step::boxed_value::JValuable;
|
||||||
|
|
||||||
|
let canon_stream = exec_ctx.scalars.get_canon_stream(stream_name)?;
|
||||||
|
let (result, tetraplet) = JValuable::apply_lambda_with_tetraplets(&canon_stream, lambda, exec_ctx)?;
|
||||||
|
// TODO: refactor this code after boxed value
|
||||||
|
let value = ValueAggregate::new(Rc::new(result.clone()), Rc::new(tetraplet), canon_stream.position());
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_canon_stream_without_lambda(
|
||||||
|
stream_name: &str,
|
||||||
|
exec_ctx: &ExecutionCtx<'_>,
|
||||||
|
) -> ExecutionResult<ValueAggregate> {
|
||||||
|
use crate::execution_step::boxed_value::JValuable;
|
||||||
|
|
||||||
|
let canon_stream = exec_ctx.scalars.get_canon_stream(stream_name)?;
|
||||||
|
// TODO: refactor this code after boxed value
|
||||||
|
let value = JValuable::as_jvalue(&canon_stream).into_owned();
|
||||||
|
|
||||||
|
let tetraplet = canon_stream.tetraplet().clone();
|
||||||
|
let value = ValueAggregate::new(Rc::new(value), tetraplet, canon_stream.position());
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
@ -39,12 +39,12 @@ pub(super) fn try_match_trace_to_instr(merger_ap_result: &MergerApResult, instr:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn match_position_variable(
|
fn match_position_variable(
|
||||||
variable: &ast::Variable<'_>,
|
variable: &ast::ApResult<'_>,
|
||||||
generation: Option<u32>,
|
generation: Option<u32>,
|
||||||
ap_result: &MergerApResult,
|
ap_result: &MergerApResult,
|
||||||
) -> ExecutionResult<()> {
|
) -> ExecutionResult<()> {
|
||||||
use crate::execution_step::UncatchableError::ApResultNotCorrespondToInstr;
|
use crate::execution_step::UncatchableError::ApResultNotCorrespondToInstr;
|
||||||
use ast::Variable::*;
|
use ast::ApResult::*;
|
||||||
|
|
||||||
match (variable, generation) {
|
match (variable, generation) {
|
||||||
(Stream(_), Some(_)) => Ok(()),
|
(Stream(_), Some(_)) => Ok(()),
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
pub(crate) mod call_result_setter;
|
pub(crate) mod call_result_setter;
|
||||||
mod prev_result_handler;
|
mod prev_result_handler;
|
||||||
mod resolved_call;
|
mod resolved_call;
|
||||||
mod triplet;
|
pub(crate) mod triplet;
|
||||||
|
|
||||||
use resolved_call::ResolvedCall;
|
use resolved_call::ResolvedCall;
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ use air_interpreter_data::CallResult;
|
|||||||
use air_interpreter_data::TracePos;
|
use air_interpreter_data::TracePos;
|
||||||
use air_interpreter_data::Value;
|
use air_interpreter_data::Value;
|
||||||
use air_parser::ast::CallOutputValue;
|
use air_parser::ast::CallOutputValue;
|
||||||
use air_parser::ast::Variable;
|
|
||||||
use air_trace_handler::TraceHandler;
|
use air_trace_handler::TraceHandler;
|
||||||
|
|
||||||
/// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`.
|
/// Writes result of a local `Call` instruction to `ExecutionCtx` at `output`.
|
||||||
@ -35,11 +34,11 @@ pub(crate) fn set_local_result<'i>(
|
|||||||
) -> ExecutionResult<CallResult> {
|
) -> ExecutionResult<CallResult> {
|
||||||
let result_value = executed_result.result.clone();
|
let result_value = executed_result.result.clone();
|
||||||
match output {
|
match output {
|
||||||
CallOutputValue::Variable(Variable::Scalar(scalar)) => {
|
CallOutputValue::Scalar(scalar) => {
|
||||||
exec_ctx.scalars.set_value(scalar.name, executed_result)?;
|
exec_ctx.scalars.set_scalar_value(scalar.name, executed_result)?;
|
||||||
Ok(CallResult::executed_scalar(result_value))
|
Ok(CallResult::executed_scalar(result_value))
|
||||||
}
|
}
|
||||||
CallOutputValue::Variable(Variable::Stream(stream)) => {
|
CallOutputValue::Stream(stream) => {
|
||||||
let generation =
|
let generation =
|
||||||
exec_ctx
|
exec_ctx
|
||||||
.streams
|
.streams
|
||||||
@ -58,11 +57,11 @@ pub(crate) fn set_result_from_value<'i>(
|
|||||||
exec_ctx: &mut ExecutionCtx<'i>,
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
) -> ExecutionResult<()> {
|
) -> ExecutionResult<()> {
|
||||||
match (output, value) {
|
match (output, value) {
|
||||||
(CallOutputValue::Variable(Variable::Scalar(scalar)), Value::Scalar(value)) => {
|
(CallOutputValue::Scalar(scalar), Value::Scalar(value)) => {
|
||||||
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
||||||
exec_ctx.scalars.set_value(scalar.name, result)?;
|
exec_ctx.scalars.set_scalar_value(scalar.name, result)?;
|
||||||
}
|
}
|
||||||
(CallOutputValue::Variable(Variable::Stream(stream)), Value::Stream { value, generation }) => {
|
(CallOutputValue::Stream(stream), Value::Stream { value, generation }) => {
|
||||||
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
||||||
let generation = Generation::Nth(generation);
|
let generation = Generation::Nth(generation);
|
||||||
let _ = exec_ctx
|
let _ = exec_ctx
|
||||||
|
@ -217,7 +217,7 @@ fn check_output_name(output: &ast::CallOutputValue<'_>, exec_ctx: &ExecutionCtx<
|
|||||||
use crate::execution_step::boxed_value::ScalarRef;
|
use crate::execution_step::boxed_value::ScalarRef;
|
||||||
|
|
||||||
let scalar_name = match output {
|
let scalar_name = match output {
|
||||||
ast::CallOutputValue::Variable(ast::Variable::Scalar(scalar)) => scalar.name,
|
ast::CallOutputValue::Scalar(scalar) => scalar.name,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,7 +43,11 @@ pub(crate) fn resolve<'i>(triplet: &ast::Triplet<'i>, ctx: &ExecutionCtx<'i>) ->
|
|||||||
|
|
||||||
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
||||||
// TODO: return Rc<String> to avoid excess cloning
|
// TODO: return Rc<String> to avoid excess cloning
|
||||||
fn resolve_to_string<'i>(value: &ast::CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
// TODO: move this function into resolve in boxed value PR
|
||||||
|
pub(crate) fn resolve_to_string<'i>(
|
||||||
|
value: &ast::CallInstrValue<'i>,
|
||||||
|
ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<String> {
|
||||||
use crate::execution_step::resolver::resolve_ast_variable_wl;
|
use crate::execution_step::resolver::resolve_ast_variable_wl;
|
||||||
use ast::CallInstrValue::*;
|
use ast::CallInstrValue::*;
|
||||||
|
|
||||||
|
170
air/src/execution_step/air/canon.rs
Normal file
170
air/src/execution_step/air/canon.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::execution_step::boxed_value::CanonStream;
|
||||||
|
use crate::execution_step::Generation;
|
||||||
|
use crate::log_instruction;
|
||||||
|
use crate::trace_to_exec_err;
|
||||||
|
use crate::CatchableError;
|
||||||
|
use crate::ExecutionError;
|
||||||
|
use crate::UncatchableError;
|
||||||
|
|
||||||
|
use air_interpreter_data::CanonResult;
|
||||||
|
use air_interpreter_data::TracePos;
|
||||||
|
use air_parser::ast;
|
||||||
|
use air_trace_handler::MergerCanonResult;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
impl<'i> super::ExecutableInstruction<'i> for ast::Canon<'i> {
|
||||||
|
#[tracing::instrument(level = "debug", skip(exec_ctx, trace_ctx))]
|
||||||
|
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||||
|
log_instruction!(call, exec_ctx, trace_ctx);
|
||||||
|
let canon_result = trace_to_exec_err!(trace_ctx.meet_canon_start(), self)?;
|
||||||
|
|
||||||
|
match canon_result {
|
||||||
|
MergerCanonResult::CanonResult { stream_elements_pos } => {
|
||||||
|
handle_seen_canon(self, stream_elements_pos, exec_ctx, trace_ctx)
|
||||||
|
}
|
||||||
|
MergerCanonResult::Empty => handle_unseen_canon(self, exec_ctx, trace_ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_seen_canon(
|
||||||
|
ast_canon: &ast::Canon<'_>,
|
||||||
|
stream_elements_pos: Vec<TracePos>,
|
||||||
|
exec_ctx: &mut ExecutionCtx<'_>,
|
||||||
|
trace_ctx: &mut TraceHandler,
|
||||||
|
) -> ExecutionResult<()> {
|
||||||
|
let canon_stream = create_canon_stream_from_pos(&stream_elements_pos, ast_canon, exec_ctx)?;
|
||||||
|
let stream_with_positions = StreamWithPositions {
|
||||||
|
canon_stream,
|
||||||
|
stream_elements_pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
epilog(ast_canon.canon_stream.name, stream_with_positions, exec_ctx, trace_ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_unseen_canon(
|
||||||
|
ast_canon: &ast::Canon<'_>,
|
||||||
|
exec_ctx: &mut ExecutionCtx<'_>,
|
||||||
|
trace_ctx: &mut TraceHandler,
|
||||||
|
) -> ExecutionResult<()> {
|
||||||
|
let peer_id = crate::execution_step::air::resolve_to_string(&ast_canon.peer_pk, exec_ctx)?;
|
||||||
|
|
||||||
|
if exec_ctx.run_parameters.current_peer_id.as_str() != peer_id {
|
||||||
|
exec_ctx.subgraph_complete = false;
|
||||||
|
exec_ctx.next_peer_pks.push(peer_id);
|
||||||
|
//this branch is executed only when
|
||||||
|
// this canon instruction executes for the first time
|
||||||
|
// a peer is different from one set in peer_id of a this canon instruction
|
||||||
|
//
|
||||||
|
// the former means that there wasn't canon associated state in data, the latter that it
|
||||||
|
// can't be obtained on this peer, so it's intended not to call meet_canon_end here.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream_with_positions = create_canon_stream_from_name(ast_canon, peer_id, exec_ctx)?;
|
||||||
|
epilog(ast_canon.canon_stream.name, stream_with_positions, exec_ctx, trace_ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_canon_stream_from_pos(
|
||||||
|
stream_elements_pos: &[TracePos],
|
||||||
|
ast_canon: &ast::Canon<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'_>,
|
||||||
|
) -> ExecutionResult<CanonStream> {
|
||||||
|
let stream = exec_ctx
|
||||||
|
.streams
|
||||||
|
.get(ast_canon.stream.name, ast_canon.stream.position)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ExecutionError::Catchable(Rc::new(CatchableError::StreamsForCanonNotFound(
|
||||||
|
ast_canon.stream.name.to_string(),
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let values = stream_elements_pos
|
||||||
|
.iter()
|
||||||
|
.map(|&position| {
|
||||||
|
stream
|
||||||
|
.get_value_by_pos(position)
|
||||||
|
.ok_or(ExecutionError::Uncatchable(UncatchableError::VariableNotFoundByPos(
|
||||||
|
position,
|
||||||
|
)))
|
||||||
|
.cloned()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
let peer_id = crate::execution_step::air::resolve_to_string(&ast_canon.peer_pk, exec_ctx)?;
|
||||||
|
let canon_stream = CanonStream::new(values, peer_id, ast_canon.stream.position.into());
|
||||||
|
Ok(canon_stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn epilog(
|
||||||
|
canon_stream_name: &str,
|
||||||
|
stream_with_positions: StreamWithPositions,
|
||||||
|
exec_ctx: &mut ExecutionCtx<'_>,
|
||||||
|
trace_ctx: &mut TraceHandler,
|
||||||
|
) -> ExecutionResult<()> {
|
||||||
|
let StreamWithPositions {
|
||||||
|
canon_stream,
|
||||||
|
stream_elements_pos,
|
||||||
|
} = stream_with_positions;
|
||||||
|
|
||||||
|
exec_ctx
|
||||||
|
.scalars
|
||||||
|
.set_canon_value(canon_stream_name, canon_stream)
|
||||||
|
.map(|_| ())?;
|
||||||
|
trace_ctx.meet_canon_end(CanonResult::new(stream_elements_pos));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StreamWithPositions {
|
||||||
|
canon_stream: CanonStream,
|
||||||
|
stream_elements_pos: Vec<TracePos>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_canon_stream_from_name(
|
||||||
|
ast_canon: &ast::Canon<'_>,
|
||||||
|
peer_id: String,
|
||||||
|
exec_ctx: &ExecutionCtx<'_>,
|
||||||
|
) -> ExecutionResult<StreamWithPositions> {
|
||||||
|
let stream = exec_ctx
|
||||||
|
.streams
|
||||||
|
.get(ast_canon.stream.name, ast_canon.stream.position)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ExecutionError::Catchable(Rc::new(CatchableError::StreamsForCanonNotFound(
|
||||||
|
ast_canon.stream.name.to_string(),
|
||||||
|
)))
|
||||||
|
})?;
|
||||||
|
let canon_stream = CanonStream::from_stream(stream, peer_id, ast_canon.canon_stream.position.into());
|
||||||
|
let stream_elements_pos = stream
|
||||||
|
.iter(Generation::Last)
|
||||||
|
// it's always safe to iter over all generations
|
||||||
|
.unwrap()
|
||||||
|
.map(|value| value.trace_pos)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let result = StreamWithPositions {
|
||||||
|
canon_stream,
|
||||||
|
stream_elements_pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
@ -18,7 +18,7 @@ use super::ExecutionCtx;
|
|||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::TraceHandler;
|
use super::TraceHandler;
|
||||||
use crate::execution_step::execution_context::check_error_object;
|
use crate::execution_step::execution_context::check_error_object;
|
||||||
use crate::execution_step::resolver::resolve_ast_scalar_wl;
|
use crate::execution_step::resolver::{apply_lambda, resolve_ast_scalar_wl};
|
||||||
use crate::execution_step::CatchableError;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::execution_step::LastError;
|
use crate::execution_step::LastError;
|
||||||
use crate::execution_step::RcSecurityTetraplet;
|
use crate::execution_step::RcSecurityTetraplet;
|
||||||
@ -30,6 +30,8 @@ use air_parser::ast;
|
|||||||
use air_parser::ast::Fail;
|
use air_parser::ast::Fail;
|
||||||
use polyplets::SecurityTetraplet;
|
use polyplets::SecurityTetraplet;
|
||||||
|
|
||||||
|
use crate::execution_step::boxed_value::Variable;
|
||||||
|
use air_lambda_ast::LambdaAST;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
impl<'i> super::ExecutableInstruction<'i> for Fail<'i> {
|
impl<'i> super::ExecutableInstruction<'i> for Fail<'i> {
|
||||||
@ -42,6 +44,7 @@ impl<'i> super::ExecutableInstruction<'i> for Fail<'i> {
|
|||||||
ret_code,
|
ret_code,
|
||||||
error_message,
|
error_message,
|
||||||
} => fail_with_literals(ret_code, error_message, self, exec_ctx),
|
} => fail_with_literals(ret_code, error_message, self, exec_ctx),
|
||||||
|
Fail::CanonStream { name, lambda } => fail_with_canon_stream(name, lambda, exec_ctx),
|
||||||
// bubble last error up
|
// bubble last error up
|
||||||
Fail::LastError => fail_with_last_error(exec_ctx),
|
Fail::LastError => fail_with_last_error(exec_ctx),
|
||||||
}
|
}
|
||||||
@ -76,6 +79,20 @@ fn fail_with_literals<'i>(
|
|||||||
fail_with_error_object(exec_ctx, Rc::new(error_object), Some(literal_tetraplet))
|
fail_with_error_object(exec_ctx, Rc::new(error_object), Some(literal_tetraplet))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fail_with_canon_stream<'i>(
|
||||||
|
name: &'i str,
|
||||||
|
lambda: &LambdaAST<'i>,
|
||||||
|
exec_ctx: &mut ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<()> {
|
||||||
|
let variable = Variable::CanonStream { name };
|
||||||
|
|
||||||
|
let (value, tetraplet) = apply_lambda(variable, lambda, exec_ctx)?;
|
||||||
|
// tetraplets always have one element here and it'll be refactored after boxed value
|
||||||
|
check_error_object(&value).map_err(CatchableError::InvalidLastErrorObjectError)?;
|
||||||
|
|
||||||
|
fail_with_error_object(exec_ctx, Rc::new(value), Some(Rc::new(tetraplet)))
|
||||||
|
}
|
||||||
|
|
||||||
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
fn fail_with_last_error(exec_ctx: &mut ExecutionCtx<'_>) -> ExecutionResult<()> {
|
||||||
let LastError { error, tetraplet } = exec_ctx.last_error_descriptor.last_error();
|
let LastError { error, tetraplet } = exec_ctx.last_error_descriptor.last_error();
|
||||||
|
|
||||||
|
@ -25,5 +25,4 @@ use super::ExecutionCtx;
|
|||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::Instruction;
|
use super::Instruction;
|
||||||
use super::ScalarRef;
|
use super::ScalarRef;
|
||||||
use super::ValueAggregate;
|
|
||||||
use crate::execution_step::boxed_value::*;
|
use crate::execution_step::boxed_value::*;
|
||||||
|
@ -31,7 +31,7 @@ pub(crate) type IterableValue = Box<dyn for<'ctx> Iterable<'ctx, Item = Iterable
|
|||||||
|
|
||||||
pub(crate) enum FoldIterableScalar {
|
pub(crate) enum FoldIterableScalar {
|
||||||
Empty,
|
Empty,
|
||||||
Scalar(IterableValue),
|
ScalarBased(IterableValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs iterable value for given scalar iterable.
|
/// Constructs iterable value for given scalar iterable.
|
||||||
@ -45,6 +45,18 @@ pub(crate) fn construct_scalar_iterable_value<'ctx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs iterable value for given canon stream.
|
||||||
|
pub(crate) fn construct_canon_stream_iterable_value<'ctx>(
|
||||||
|
ast_canon_stream: &ast::CanonStream<'ctx>,
|
||||||
|
exec_ctx: &ExecutionCtx<'ctx>,
|
||||||
|
) -> ExecutionResult<FoldIterableScalar> {
|
||||||
|
let canon_stream = exec_ctx.scalars.get_canon_stream(ast_canon_stream.name)?;
|
||||||
|
// TODO: this one is a relatively long operation and will be refactored in Boxed Value
|
||||||
|
let iterable_ingredients = CanonStreamIterableIngredients::init(canon_stream.clone());
|
||||||
|
let iterable = Box::new(iterable_ingredients);
|
||||||
|
Ok(FoldIterableScalar::ScalarBased(iterable))
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs iterable value for given stream iterable.
|
/// Constructs iterable value for given stream iterable.
|
||||||
pub(crate) fn construct_stream_iterable_values(
|
pub(crate) fn construct_stream_iterable_values(
|
||||||
stream: &Stream,
|
stream: &Stream,
|
||||||
@ -72,17 +84,17 @@ fn create_scalar_iterable<'ctx>(
|
|||||||
variable_name: &str,
|
variable_name: &str,
|
||||||
) -> ExecutionResult<FoldIterableScalar> {
|
) -> ExecutionResult<FoldIterableScalar> {
|
||||||
match exec_ctx.scalars.get_value(variable_name)? {
|
match exec_ctx.scalars.get_value(variable_name)? {
|
||||||
ScalarRef::Value(call_result) => from_call_result(call_result.clone(), variable_name),
|
ScalarRef::Value(call_result) => from_value(call_result.clone(), variable_name),
|
||||||
ScalarRef::IterableValue(fold_state) => {
|
ScalarRef::IterableValue(fold_state) => {
|
||||||
let iterable_value = fold_state.iterable.peek().unwrap();
|
let iterable_value = fold_state.iterable.peek().unwrap();
|
||||||
let call_result = iterable_value.into_resolved_result();
|
let call_result = iterable_value.into_resolved_result();
|
||||||
from_call_result(call_result, variable_name)
|
from_value(call_result, variable_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs iterable value from resolved call result.
|
/// Constructs iterable value from resolved call result.
|
||||||
fn from_call_result(call_result: ValueAggregate, variable_name: &str) -> ExecutionResult<FoldIterableScalar> {
|
fn from_value(call_result: ValueAggregate, variable_name: &str) -> ExecutionResult<FoldIterableScalar> {
|
||||||
let len = match &call_result.result.deref() {
|
let len = match &call_result.result.deref() {
|
||||||
JValue::Array(array) => {
|
JValue::Array(array) => {
|
||||||
if array.is_empty() {
|
if array.is_empty() {
|
||||||
@ -103,7 +115,7 @@ fn from_call_result(call_result: ValueAggregate, variable_name: &str) -> Executi
|
|||||||
|
|
||||||
let foldable = IterableResolvedCall::init(call_result, len);
|
let foldable = IterableResolvedCall::init(call_result, len);
|
||||||
let foldable = Box::new(foldable);
|
let foldable = Box::new(foldable);
|
||||||
let iterable = FoldIterableScalar::Scalar(foldable);
|
let iterable = FoldIterableScalar::ScalarBased(foldable);
|
||||||
|
|
||||||
Ok(iterable)
|
Ok(iterable)
|
||||||
}
|
}
|
||||||
@ -154,7 +166,7 @@ fn from_jvalue(
|
|||||||
|
|
||||||
let iterable = iterable.to_vec();
|
let iterable = iterable.to_vec();
|
||||||
let foldable = IterableLambdaResult::init(iterable, tetraplet);
|
let foldable = IterableLambdaResult::init(iterable, tetraplet);
|
||||||
let iterable = FoldIterableScalar::Scalar(Box::new(foldable));
|
let iterable = FoldIterableScalar::ScalarBased(Box::new(foldable));
|
||||||
Ok(iterable)
|
Ok(iterable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,17 +33,21 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> {
|
|||||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||||
log_instruction!(fold, exec_ctx, trace_ctx);
|
log_instruction!(fold, exec_ctx, trace_ctx);
|
||||||
|
|
||||||
let scalar = match &self.iterable {
|
let iterable = match &self.iterable {
|
||||||
FoldScalarIterable::Scalar(scalar) => scalar,
|
FoldScalarIterable::Scalar(scalar) => {
|
||||||
|
joinable!(construct_scalar_iterable_value(scalar, exec_ctx), exec_ctx)?
|
||||||
|
}
|
||||||
|
FoldScalarIterable::CanonStream(canon_stream) => {
|
||||||
|
construct_canon_stream_iterable_value(canon_stream, exec_ctx)?
|
||||||
|
}
|
||||||
// just do nothing on an empty array
|
// just do nothing on an empty array
|
||||||
FoldScalarIterable::EmptyArray => return Ok(()),
|
FoldScalarIterable::EmptyArray => return Ok(()),
|
||||||
};
|
};
|
||||||
let scalar_iterable = joinable!(construct_scalar_iterable_value(scalar, exec_ctx), exec_ctx)?;
|
|
||||||
|
|
||||||
match scalar_iterable {
|
match iterable {
|
||||||
// just exit on empty iterable
|
// just exit on empty iterable
|
||||||
FoldIterableScalar::Empty => Ok(()),
|
FoldIterableScalar::Empty => Ok(()),
|
||||||
FoldIterableScalar::Scalar(iterable) => fold(
|
FoldIterableScalar::ScalarBased(iterable) => fold(
|
||||||
iterable,
|
iterable,
|
||||||
IterableType::Scalar,
|
IterableType::Scalar,
|
||||||
self.iterator.name,
|
self.iterator.name,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
mod ap;
|
mod ap;
|
||||||
mod call;
|
mod call;
|
||||||
|
mod canon;
|
||||||
mod compare_matchable;
|
mod compare_matchable;
|
||||||
mod fail;
|
mod fail;
|
||||||
mod fold;
|
mod fold;
|
||||||
@ -30,6 +31,7 @@ mod par;
|
|||||||
mod seq;
|
mod seq;
|
||||||
mod xor;
|
mod xor;
|
||||||
|
|
||||||
|
pub(crate) use call::triplet::resolve_to_string;
|
||||||
pub(crate) use fold::FoldState;
|
pub(crate) use fold::FoldState;
|
||||||
|
|
||||||
use super::boxed_value::ScalarRef;
|
use super::boxed_value::ScalarRef;
|
||||||
@ -74,6 +76,7 @@ impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
|||||||
Instruction::Call(call) => call.execute(exec_ctx, trace_ctx),
|
Instruction::Call(call) => call.execute(exec_ctx, trace_ctx),
|
||||||
|
|
||||||
Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx),
|
Instruction::Ap(ap) => execute!(self, ap, exec_ctx, trace_ctx),
|
||||||
|
Instruction::Canon(canon) => execute!(self, canon, exec_ctx, trace_ctx),
|
||||||
Instruction::Fail(fail) => execute!(self, fail, exec_ctx, trace_ctx),
|
Instruction::Fail(fail) => execute!(self, fail, exec_ctx, trace_ctx),
|
||||||
Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
Instruction::FoldScalar(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||||
Instruction::FoldStream(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
Instruction::FoldStream(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||||
|
@ -20,7 +20,7 @@ use super::TraceHandler;
|
|||||||
use crate::log_instruction;
|
use crate::log_instruction;
|
||||||
|
|
||||||
use air_parser::ast::New;
|
use air_parser::ast::New;
|
||||||
use air_parser::ast::Variable;
|
use air_parser::ast::NewArgument;
|
||||||
|
|
||||||
impl<'i> super::ExecutableInstruction<'i> for New<'i> {
|
impl<'i> super::ExecutableInstruction<'i> for New<'i> {
|
||||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||||
@ -48,12 +48,15 @@ impl<'i> super::ExecutableInstruction<'i> for New<'i> {
|
|||||||
|
|
||||||
fn prolog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) {
|
fn prolog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) {
|
||||||
let position = new.span.left;
|
let position = new.span.left;
|
||||||
match &new.variable {
|
match &new.argument {
|
||||||
Variable::Stream(stream) => {
|
NewArgument::Stream(stream) => {
|
||||||
let iteration = exec_ctx.tracker.new_tracker.get_iteration(position);
|
let iteration = exec_ctx.tracker.new_tracker.get_iteration(position);
|
||||||
exec_ctx.streams.meet_scope_start(stream.name, new.span, iteration);
|
exec_ctx.streams.meet_scope_start(stream.name, new.span, iteration);
|
||||||
}
|
}
|
||||||
Variable::Scalar(scalar) => exec_ctx.scalars.meet_new_start(scalar.name),
|
NewArgument::Scalar(scalar) => exec_ctx.scalars.meet_new_start_scalar(scalar.name.to_string()),
|
||||||
|
NewArgument::CanonStream(canon_stream) => exec_ctx
|
||||||
|
.scalars
|
||||||
|
.meet_new_start_canon_stream(canon_stream.name.to_string()),
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_ctx.tracker.meet_new(position);
|
exec_ctx.tracker.meet_new(position);
|
||||||
@ -61,13 +64,14 @@ fn prolog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) {
|
|||||||
|
|
||||||
fn epilog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) -> ExecutionResult<()> {
|
fn epilog<'i>(new: &New<'i>, exec_ctx: &mut ExecutionCtx<'i>) -> ExecutionResult<()> {
|
||||||
let position = new.span.left;
|
let position = new.span.left;
|
||||||
match &new.variable {
|
match &new.argument {
|
||||||
Variable::Stream(stream) => {
|
NewArgument::Stream(stream) => {
|
||||||
exec_ctx
|
exec_ctx
|
||||||
.streams
|
.streams
|
||||||
.meet_scope_end(stream.name.to_string(), position as u32);
|
.meet_scope_end(stream.name.to_string(), position as u32);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Variable::Scalar(scalar) => exec_ctx.scalars.meet_new_end(scalar.name),
|
NewArgument::Scalar(scalar) => exec_ctx.scalars.meet_new_end_scalar(scalar.name),
|
||||||
|
NewArgument::CanonStream(canon_stream) => exec_ctx.scalars.meet_new_end_canon_stream(canon_stream.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
102
air/src/execution_step/boxed_value/canon_stream.rs
Normal file
102
air/src/execution_step/boxed_value/canon_stream.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::Stream;
|
||||||
|
use super::ValueAggregate;
|
||||||
|
use crate::execution_step::Generation;
|
||||||
|
use crate::JValue;
|
||||||
|
|
||||||
|
use air_interpreter_data::TracePos;
|
||||||
|
use polyplets::SecurityTetraplet;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// Canon stream is a value type lies between a scalar and a stream, it has the same algebra as
|
||||||
|
/// scalars, and represent a stream fixed at some execution point.
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct CanonStream {
|
||||||
|
values: Vec<ValueAggregate>,
|
||||||
|
// tetraplet is needed to handle adding canon streams as a whole to a stream
|
||||||
|
tetraplet: Rc<SecurityTetraplet>,
|
||||||
|
position: TracePos,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanonStream {
|
||||||
|
pub(crate) fn new(values: Vec<ValueAggregate>, peer_pk: String, position: TracePos) -> Self {
|
||||||
|
// tetraplet is comprised only from peer_pk here
|
||||||
|
let tetraplet = SecurityTetraplet::new(peer_pk, "", "", "");
|
||||||
|
Self {
|
||||||
|
values,
|
||||||
|
tetraplet: Rc::new(tetraplet),
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_stream(stream: &Stream, peer_pk: String, position: TracePos) -> Self {
|
||||||
|
// it's always possible to iter over all generations of a stream
|
||||||
|
let values = stream.iter(Generation::Last).unwrap().cloned().collect::<Vec<_>>();
|
||||||
|
let tetraplet = SecurityTetraplet::new(peer_pk, "", "", "");
|
||||||
|
Self {
|
||||||
|
values,
|
||||||
|
tetraplet: Rc::new(tetraplet),
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn len(&self) -> usize {
|
||||||
|
self.values.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
|
self.values.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_jvalue(&self) -> JValue {
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
// TODO: this clone will be removed after boxed values
|
||||||
|
let jvalue_array = self.values.iter().map(|r| r.result.deref().clone()).collect::<Vec<_>>();
|
||||||
|
JValue::Array(jvalue_array)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter(&self) -> impl ExactSizeIterator<Item = &ValueAggregate> {
|
||||||
|
self.values.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn nth(&self, idx: usize) -> Option<&ValueAggregate> {
|
||||||
|
self.values.get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn tetraplet(&self) -> &Rc<SecurityTetraplet> {
|
||||||
|
&self.tetraplet
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn position(&self) -> TracePos {
|
||||||
|
self.position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
impl fmt::Display for CanonStream {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "[")?;
|
||||||
|
for value in self.values.iter() {
|
||||||
|
write!(f, "{}, ", value)?;
|
||||||
|
}
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
}
|
@ -14,11 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod canon_stream;
|
||||||
mod json_path_result;
|
mod json_path_result;
|
||||||
mod resolved_call;
|
mod resolved_call;
|
||||||
mod vec_json_path_result;
|
|
||||||
mod vec_resolved_call;
|
mod vec_resolved_call;
|
||||||
|
|
||||||
|
pub(crate) use canon_stream::CanonStreamIterableIngredients;
|
||||||
pub(crate) use json_path_result::IterableLambdaResult;
|
pub(crate) use json_path_result::IterableLambdaResult;
|
||||||
pub(crate) use resolved_call::IterableResolvedCall;
|
pub(crate) use resolved_call::IterableResolvedCall;
|
||||||
pub(crate) use vec_resolved_call::IterableVecResolvedCall;
|
pub(crate) use vec_resolved_call::IterableVecResolvedCall;
|
||||||
|
@ -16,36 +16,31 @@
|
|||||||
|
|
||||||
use super::Iterable;
|
use super::Iterable;
|
||||||
use super::IterableItem;
|
use super::IterableItem;
|
||||||
use crate::execution_step::RcSecurityTetraplets;
|
use crate::execution_step::boxed_value::CanonStream;
|
||||||
use crate::foldable_next;
|
use crate::foldable_next;
|
||||||
use crate::foldable_prev;
|
use crate::foldable_prev;
|
||||||
use crate::JValue;
|
|
||||||
|
|
||||||
/// Used for iterating over a result of applied to a stream lambda.
|
const EXPECT_VALUE_IN_STREAM: &str = "value must exist, because length checked before creation and canonicalized stream can't be modified during iteration";
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub(crate) struct IterableVecJsonPathResult {
|
pub(crate) struct CanonStreamIterableIngredients {
|
||||||
pub(crate) jvalues: Vec<JValue>,
|
canon_stream: CanonStream,
|
||||||
pub(crate) tetraplets: RcSecurityTetraplets,
|
cursor: usize,
|
||||||
pub(crate) cursor: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IterableVecJsonPathResult {
|
impl CanonStreamIterableIngredients {
|
||||||
#[allow(dead_code)]
|
pub(crate) fn init(canon_stream: CanonStream) -> Self {
|
||||||
pub(crate) fn init(jvalues: Vec<JValue>, tetraplets: RcSecurityTetraplets) -> Self {
|
|
||||||
// TODO: add assert on length
|
|
||||||
Self {
|
Self {
|
||||||
jvalues,
|
canon_stream,
|
||||||
tetraplets,
|
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Iterable<'ctx> for IterableVecJsonPathResult {
|
impl<'ctx> Iterable<'ctx> for CanonStreamIterableIngredients {
|
||||||
type Item = IterableItem<'ctx>;
|
type Item = IterableItem<'ctx>;
|
||||||
|
|
||||||
fn next(&mut self) -> bool {
|
fn next(&mut self) -> bool {
|
||||||
foldable_next!(self, self.jvalues.len())
|
foldable_next!(self, self.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self) -> bool {
|
fn prev(&mut self) -> bool {
|
||||||
@ -53,18 +48,16 @@ impl<'ctx> Iterable<'ctx> for IterableVecJsonPathResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&'ctx self) -> Option<Self::Item> {
|
fn peek(&'ctx self) -> Option<Self::Item> {
|
||||||
if self.jvalues.is_empty() {
|
if self.canon_stream.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let jvalue = &self.jvalues[self.cursor];
|
let value = self.canon_stream.nth(self.cursor).expect(EXPECT_VALUE_IN_STREAM);
|
||||||
let tetraplet = &self.tetraplets[self.cursor];
|
let result = IterableItem::RefRef((&value.result, &value.tetraplet, value.trace_pos));
|
||||||
let result = IterableItem::RefRef((jvalue, tetraplet, 0.into()));
|
|
||||||
|
|
||||||
Some(result)
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.jvalues.len()
|
self.canon_stream.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod canon_stream;
|
||||||
mod cell_vec_resolved_call_result;
|
mod cell_vec_resolved_call_result;
|
||||||
mod empty_stream;
|
mod empty_stream;
|
||||||
mod iterable_item;
|
mod iterable_item;
|
||||||
|
68
air/src/execution_step/boxed_value/jvaluable/canon_stream.rs
Normal file
68
air/src/execution_step/boxed_value/jvaluable/canon_stream.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::select_from_stream;
|
||||||
|
use super::ExecutionResult;
|
||||||
|
use super::JValuable;
|
||||||
|
use crate::execution_step::boxed_value::CanonStream;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
|
use crate::execution_step::RcSecurityTetraplets;
|
||||||
|
use crate::JValue;
|
||||||
|
use crate::LambdaAST;
|
||||||
|
use crate::SecurityTetraplet;
|
||||||
|
|
||||||
|
use air_lambda_ast::format_ast;
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
impl JValuable for &CanonStream {
|
||||||
|
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
|
let iter = self.iter().map(|v| v.result.deref());
|
||||||
|
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||||
|
|
||||||
|
Ok(select_result.result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
|
&self,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||||
|
let iter = self.iter().map(|v| v.result.deref());
|
||||||
|
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||||
|
|
||||||
|
// unwrap is safe here because each value has a tetraplet and a lambda always returns a valid index
|
||||||
|
let resolved_call = self.nth(select_result.tetraplet_idx).unwrap();
|
||||||
|
let mut tetraplet = resolved_call.tetraplet.as_ref().clone();
|
||||||
|
tetraplet.add_lambda(&format_ast(lambda));
|
||||||
|
|
||||||
|
Ok((select_result.result, tetraplet))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_jvalue(&self) -> Cow<'_, JValue> {
|
||||||
|
let jvalue = CanonStream::as_jvalue(self);
|
||||||
|
Cow::Owned(jvalue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_jvalue(self: Box<Self>) -> JValue {
|
||||||
|
CanonStream::as_jvalue(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_tetraplets(&self) -> RcSecurityTetraplets {
|
||||||
|
self.iter().map(|r| r.tetraplet.clone()).collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
}
|
@ -14,12 +14,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod canon_stream;
|
||||||
mod iterable;
|
mod iterable;
|
||||||
mod jvaluable;
|
mod jvaluable;
|
||||||
mod scalar;
|
mod scalar;
|
||||||
mod stream;
|
mod stream;
|
||||||
mod variable;
|
mod variable;
|
||||||
|
|
||||||
|
pub(crate) use canon_stream::*;
|
||||||
pub(crate) use iterable::*;
|
pub(crate) use iterable::*;
|
||||||
pub(crate) use jvaluable::*;
|
pub(crate) use jvaluable::*;
|
||||||
pub(crate) use scalar::ScalarRef;
|
pub(crate) use scalar::ScalarRef;
|
||||||
|
@ -23,8 +23,6 @@ use air_interpreter_data::TracePos;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use std::fmt::Display;
|
|
||||||
use std::fmt::Formatter;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
@ -61,8 +59,20 @@ impl ValueAggregate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i> Display for ScalarRef<'i> {
|
use std::fmt;
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
|
impl fmt::Display for ValueAggregate {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"value: {}, tetraplet: {}, position: {} ",
|
||||||
|
self.result, self.tetraplet, self.trace_pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> fmt::Display for ScalarRef<'i> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
ScalarRef::Value(value) => write!(f, "{:?}", value)?,
|
ScalarRef::Value(value) => write!(f, "{:?}", value)?,
|
||||||
ScalarRef::IterableValue(cursor) => {
|
ScalarRef::IterableValue(cursor) => {
|
||||||
|
@ -19,70 +19,90 @@ use super::ValueAggregate;
|
|||||||
use crate::execution_step::CatchableError;
|
use crate::execution_step::CatchableError;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
|
|
||||||
|
use air_interpreter_data::TracePos;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
/// Streams are CRDT-like append only data structures. They are guaranteed to have the same order
|
/// Streams are CRDT-like append only data structures. They are guaranteed to have the same order
|
||||||
/// of values on each peer.
|
/// of values on each peer.
|
||||||
///
|
|
||||||
/// The first Vec represents generations, the second values in a generation. Generation is a set
|
|
||||||
/// of values that interpreter obtained from one particle. It means that number of generation on
|
|
||||||
/// a peer is equal to number of the interpreter runs in context of one particle. And each set of
|
|
||||||
/// obtained values from a current_data that were not present in prev_data becomes a new generation.
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Stream(Vec<Vec<ValueAggregate>>);
|
pub struct Stream {
|
||||||
|
/// The first Vec represents generations, the second values in a generation. Generation is a set
|
||||||
|
/// of values that interpreter obtained from one particle. It means that number of generation on
|
||||||
|
/// a peer is equal to number of the interpreter runs in context of one particle. And each set of
|
||||||
|
/// obtained values from a current_data that were not present in prev_data becomes a new generation.
|
||||||
|
values: Vec<Vec<ValueAggregate>>,
|
||||||
|
|
||||||
|
/// This map is intended to support canonicalized stream creation, such streams has
|
||||||
|
/// corresponding value positions in a data and this field are used to create such streams.
|
||||||
|
values_by_pos: HashMap<TracePos, StreamValueLocation>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub(crate) fn from_generations_count(count: usize) -> Self {
|
pub(crate) fn from_generations_count(count: usize) -> Self {
|
||||||
Self(vec![vec![]; count + 1])
|
Self {
|
||||||
|
values: vec![vec![]; count + 1],
|
||||||
|
values_by_pos: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_value(value: ValueAggregate) -> Self {
|
pub(crate) fn from_value(value: ValueAggregate) -> Self {
|
||||||
Self(vec![vec![value]])
|
let values_by_pos = maplit::hashmap! {
|
||||||
|
value.trace_pos => StreamValueLocation::new(0, 0),
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
values: vec![vec![value]],
|
||||||
|
values_by_pos,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if generation is None, value would be added to the last generation, otherwise it would
|
// if generation is None, value would be added to the last generation, otherwise it would
|
||||||
// be added to given generation
|
// be added to given generation
|
||||||
pub(crate) fn add_value(&mut self, value: ValueAggregate, generation: Generation) -> ExecutionResult<u32> {
|
pub(crate) fn add_value(&mut self, value: ValueAggregate, generation: Generation) -> ExecutionResult<u32> {
|
||||||
let generation = match generation {
|
let generation = match generation {
|
||||||
Generation::Last => self.0.len() - 1,
|
Generation::Last => self.values.len() - 1,
|
||||||
Generation::Nth(id) => id as usize,
|
Generation::Nth(id) => id as usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
if generation >= self.0.len() {
|
if generation >= self.values.len() {
|
||||||
return Err(CatchableError::StreamDontHaveSuchGeneration(self.clone(), generation).into());
|
return Err(CatchableError::StreamDontHaveSuchGeneration(self.clone(), generation).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0[generation].push(value);
|
let values = &mut self.values[generation];
|
||||||
|
self.values_by_pos
|
||||||
|
.insert(value.trace_pos, StreamValueLocation::new(generation, values.len()));
|
||||||
|
values.push(value);
|
||||||
Ok(generation as u32)
|
Ok(generation as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generations_count(&self) -> usize {
|
pub(crate) fn generations_count(&self) -> usize {
|
||||||
// the last generation could be empty due to the logic of from_generations_count ctor
|
// the last generation could be empty due to the logic of from_generations_count ctor
|
||||||
self.0.iter().filter(|gen| !gen.is_empty()).count()
|
self.values.iter().filter(|gen| !gen.is_empty()).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new empty generation if the latest isn't empty.
|
/// Add a new empty generation if the latest isn't empty.
|
||||||
pub(crate) fn add_new_generation_if_non_empty(&mut self) -> bool {
|
pub(crate) fn add_new_generation_if_non_empty(&mut self) -> bool {
|
||||||
let should_add_generation = match self.0.last() {
|
let should_add_generation = match self.values.last() {
|
||||||
Some(last) => !last.is_empty(),
|
Some(last) => !last.is_empty(),
|
||||||
None => true,
|
None => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_add_generation {
|
if should_add_generation {
|
||||||
self.0.push(vec![]);
|
self.values.push(vec![]);
|
||||||
}
|
}
|
||||||
should_add_generation
|
should_add_generation
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a last generation if it's empty.
|
/// Remove a last generation if it's empty.
|
||||||
pub(crate) fn remove_last_generation_if_empty(&mut self) -> bool {
|
pub(crate) fn remove_last_generation_if_empty(&mut self) -> bool {
|
||||||
let should_remove_generation = match self.0.last() {
|
let should_remove_generation = match self.values.last() {
|
||||||
Some(last) => last.is_empty(),
|
Some(last) => last.is_empty(),
|
||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_remove_generation {
|
if should_remove_generation {
|
||||||
self.0.pop();
|
self.values.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
should_remove_generation
|
should_remove_generation
|
||||||
@ -91,17 +111,17 @@ impl Stream {
|
|||||||
pub(crate) fn elements_count(&self, generation: Generation) -> Option<usize> {
|
pub(crate) fn elements_count(&self, generation: Generation) -> Option<usize> {
|
||||||
match generation {
|
match generation {
|
||||||
Generation::Nth(generation) if generation as usize > self.generations_count() => None,
|
Generation::Nth(generation) if generation as usize > self.generations_count() => None,
|
||||||
Generation::Nth(generation) => Some(self.0.iter().take(generation as usize).map(|v| v.len()).sum()),
|
Generation::Nth(generation) => Some(self.values.iter().take(generation as usize).map(|v| v.len()).sum()),
|
||||||
Generation::Last => Some(self.0.iter().map(|v| v.len()).sum()),
|
Generation::Last => Some(self.values.iter().map(|v| v.len()).sum()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_empty(&self) -> bool {
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
if self.0.is_empty() {
|
if self.values.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.0.iter().all(|v| v.is_empty())
|
self.values.iter().all(|v| v.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn as_jvalue(&self, generation: Generation) -> Option<JValue> {
|
pub(crate) fn as_jvalue(&self, generation: Generation) -> Option<JValue> {
|
||||||
@ -113,11 +133,22 @@ impl Stream {
|
|||||||
Some(JValue::Array(jvalue_array))
|
Some(JValue::Array(jvalue_array))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_value_by_pos(&self, position: TracePos) -> Option<&ValueAggregate> {
|
||||||
|
let StreamValueLocation {
|
||||||
|
generation,
|
||||||
|
position_in_generation,
|
||||||
|
} = self.values_by_pos.get(&position)?;
|
||||||
|
let value = &self.values[*generation][*position_in_generation];
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn iter(&self, generation: Generation) -> Option<StreamIter<'_>> {
|
pub(crate) fn iter(&self, generation: Generation) -> Option<StreamIter<'_>> {
|
||||||
let iter: Box<dyn Iterator<Item = &ValueAggregate>> = match generation {
|
let iter: Box<dyn Iterator<Item = &ValueAggregate>> = match generation {
|
||||||
Generation::Nth(generation) if generation as usize >= self.generations_count() => return None,
|
Generation::Nth(generation) if generation as usize >= self.generations_count() => return None,
|
||||||
Generation::Nth(generation) => Box::new(self.0.iter().take(generation as usize + 1).flat_map(|v| v.iter())),
|
Generation::Nth(generation) => {
|
||||||
Generation::Last => Box::new(self.0.iter().flat_map(|v| v.iter())),
|
Box::new(self.values.iter().take(generation as usize + 1).flat_map(|v| v.iter()))
|
||||||
|
}
|
||||||
|
Generation::Last => Box::new(self.values.iter().flat_map(|v| v.iter())),
|
||||||
};
|
};
|
||||||
// unwrap is safe here, because generation's been already checked
|
// unwrap is safe here, because generation's been already checked
|
||||||
let len = self.elements_count(generation).unwrap();
|
let len = self.elements_count(generation).unwrap();
|
||||||
@ -146,7 +177,7 @@ impl Stream {
|
|||||||
|
|
||||||
let len = (end - start) as usize + 1;
|
let len = (end - start) as usize + 1;
|
||||||
let iter: Box<dyn Iterator<Item = &[ValueAggregate]>> =
|
let iter: Box<dyn Iterator<Item = &[ValueAggregate]>> =
|
||||||
Box::new(self.0.iter().skip(start as usize).take(len).map(|v| v.as_slice()));
|
Box::new(self.values.iter().skip(start as usize).take(len).map(|v| v.as_slice()));
|
||||||
let iter = StreamSliceIter { iter, len };
|
let iter = StreamSliceIter { iter, len };
|
||||||
|
|
||||||
Some(iter)
|
Some(iter)
|
||||||
@ -210,16 +241,31 @@ impl<'slice> Iterator for StreamSliceIter<'slice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
struct StreamValueLocation {
|
||||||
|
pub generation: usize,
|
||||||
|
pub position_in_generation: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamValueLocation {
|
||||||
|
pub(super) fn new(generation: usize, position_in_generation: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
generation,
|
||||||
|
position_in_generation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
impl fmt::Display for Stream {
|
impl fmt::Display for Stream {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
if self.0.is_empty() {
|
if self.values.is_empty() {
|
||||||
return write!(f, "[]");
|
return write!(f, "[]");
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(f, "[")?;
|
writeln!(f, "[")?;
|
||||||
for (id, generation) in self.0.iter().enumerate() {
|
for (id, generation) in self.values.iter().enumerate() {
|
||||||
write!(f, " -- {}: ", id)?;
|
write!(f, " -- {}: ", id)?;
|
||||||
for value in generation.iter() {
|
for value in generation.iter() {
|
||||||
write!(f, "{:?}, ", value)?;
|
write!(f, "{:?}, ", value)?;
|
||||||
|
@ -19,19 +19,22 @@ use air_parser::ast;
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) enum Variable<'i> {
|
pub(crate) enum Variable<'i> {
|
||||||
#[allow(dead_code)]
|
Scalar {
|
||||||
// position will be needed to implement new for operators
|
name: &'i str,
|
||||||
Scalar { name: &'i str, position: usize },
|
},
|
||||||
Stream {
|
Stream {
|
||||||
name: &'i str,
|
name: &'i str,
|
||||||
generation: Generation,
|
generation: Generation,
|
||||||
position: usize,
|
position: usize,
|
||||||
},
|
},
|
||||||
|
CanonStream {
|
||||||
|
name: &'i str,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i> Variable<'i> {
|
impl<'i> Variable<'i> {
|
||||||
pub(crate) fn scalar(name: &'i str, position: usize) -> Self {
|
pub(crate) fn scalar(name: &'i str) -> Self {
|
||||||
Self::Scalar { name, position }
|
Self::Scalar { name }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn stream(name: &'i str, generation: Generation, position: usize) -> Self {
|
pub(crate) fn stream(name: &'i str, generation: Generation, position: usize) -> Self {
|
||||||
@ -41,6 +44,10 @@ impl<'i> Variable<'i> {
|
|||||||
position,
|
position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn canon_stream(name: &'i str) -> Self {
|
||||||
|
Self::CanonStream { name }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i> From<&ast::Variable<'i>> for Variable<'i> {
|
impl<'i> From<&ast::Variable<'i>> for Variable<'i> {
|
||||||
@ -48,8 +55,9 @@ impl<'i> From<&ast::Variable<'i>> for Variable<'i> {
|
|||||||
use ast::Variable::*;
|
use ast::Variable::*;
|
||||||
|
|
||||||
match ast_variable {
|
match ast_variable {
|
||||||
Scalar(scalar) => Self::scalar(scalar.name, scalar.position),
|
Scalar(scalar) => Self::scalar(scalar.name),
|
||||||
Stream(stream) => Self::stream(stream.name, Generation::Last, stream.position),
|
Stream(stream) => Self::stream(stream.name, Generation::Last, stream.position),
|
||||||
|
CanonStream(canon_stream) => Self::canon_stream(canon_stream.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,8 +67,9 @@ impl<'i> From<&ast::VariableWithLambda<'i>> for Variable<'i> {
|
|||||||
use ast::VariableWithLambda::*;
|
use ast::VariableWithLambda::*;
|
||||||
|
|
||||||
match ast_variable {
|
match ast_variable {
|
||||||
Scalar(scalar) => Self::scalar(scalar.name, scalar.position),
|
Scalar(scalar) => Self::scalar(scalar.name),
|
||||||
Stream(stream) => Self::stream(stream.name, Generation::Last, stream.position),
|
Stream(stream) => Self::stream(stream.name, Generation::Last, stream.position),
|
||||||
|
CanonStream(canon_stream) => Self::canon_stream(canon_stream.name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,10 @@ pub enum CatchableError {
|
|||||||
/// that is prohibited.
|
/// that is prohibited.
|
||||||
#[error("variable with name '{0}' was cleared by new and then wasn't set")]
|
#[error("variable with name '{0}' was cleared by new and then wasn't set")]
|
||||||
VariableWasNotInitializedAfterNew(String),
|
VariableWasNotInitializedAfterNew(String),
|
||||||
|
|
||||||
|
/// Canon instruction can't canonicalize a stream since it's been found.
|
||||||
|
#[error("stream with name {0} wasn't defined, so canon instruction can't canonicalize it")]
|
||||||
|
StreamsForCanonNotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LambdaError> for Rc<CatchableError> {
|
impl From<LambdaError> for Rc<CatchableError> {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use crate::ToErrorCode;
|
use crate::ToErrorCode;
|
||||||
|
|
||||||
|
use air_interpreter_data::TracePos;
|
||||||
use air_trace_handler::MergerApResult;
|
use air_trace_handler::MergerApResult;
|
||||||
use air_trace_handler::TraceHandlerError;
|
use air_trace_handler::TraceHandlerError;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
@ -65,6 +66,12 @@ pub enum UncatchableError {
|
|||||||
/// be caught by a xor instruction.
|
/// be caught by a xor instruction.
|
||||||
#[error("new end block tries to pop up a variable '{scalar_name}' that wasn't defined at depth {depth}")]
|
#[error("new end block tries to pop up a variable '{scalar_name}' that wasn't defined at depth {depth}")]
|
||||||
ScalarsStateCorrupted { scalar_name: String, depth: usize },
|
ScalarsStateCorrupted { scalar_name: String, depth: usize },
|
||||||
|
|
||||||
|
/// Variable with such a position wasn't defined during AIR script execution.
|
||||||
|
/// Canon instruction requires this value to be present in data, otherwise it's considered
|
||||||
|
/// as a hard error.
|
||||||
|
#[error("variable with position '{0}' wasn't defined during script execution")]
|
||||||
|
VariableNotFoundByPos(TracePos),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToErrorCode for UncatchableError {
|
impl ToErrorCode for UncatchableError {
|
||||||
|
@ -14,20 +14,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod values_sparse_matrix;
|
||||||
|
|
||||||
|
use crate::execution_step::boxed_value::CanonStream;
|
||||||
use crate::execution_step::boxed_value::ScalarRef;
|
use crate::execution_step::boxed_value::ScalarRef;
|
||||||
use crate::execution_step::errors_prelude::*;
|
use crate::execution_step::errors_prelude::*;
|
||||||
use crate::execution_step::ExecutionResult;
|
use crate::execution_step::ExecutionResult;
|
||||||
use crate::execution_step::FoldState;
|
use crate::execution_step::FoldState;
|
||||||
use crate::execution_step::ValueAggregate;
|
use crate::execution_step::ValueAggregate;
|
||||||
|
use values_sparse_matrix::ValuesSparseMatrix;
|
||||||
use non_empty_vec::NonEmpty;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// Depth of a global scope.
|
|
||||||
const GLOBAL_DEPTH: usize = 0;
|
|
||||||
|
|
||||||
// TODO: move this code snippet to documentation when it's ready
|
// TODO: move this code snippet to documentation when it's ready
|
||||||
|
|
||||||
@ -87,85 +84,32 @@ pub(crate) struct Scalars<'i> {
|
|||||||
/// - global variables have 0 depth
|
/// - global variables have 0 depth
|
||||||
/// - cells in a row are sorted by depth
|
/// - cells in a row are sorted by depth
|
||||||
/// - all depths in cell in one row are unique
|
/// - all depths in cell in one row are unique
|
||||||
pub(crate) non_iterable_variables: HashMap<String, NonEmpty<SparseCell>>,
|
pub(crate) non_iterable_variables: ValuesSparseMatrix<ValueAggregate>,
|
||||||
|
|
||||||
/// This set contains depths were invalidated at the certain moment of script execution.
|
pub(crate) canon_streams: ValuesSparseMatrix<CanonStream>,
|
||||||
/// They are needed for careful isolation of scopes produced by iterations in fold blocks,
|
|
||||||
/// precisely to limit access of non iterable variables defined on one depths to ones
|
|
||||||
/// defined on another.
|
|
||||||
pub(crate) allowed_depths: HashSet<usize>,
|
|
||||||
|
|
||||||
pub(crate) iterable_variables: HashMap<String, FoldState<'i>>,
|
pub(crate) iterable_variables: HashMap<String, FoldState<'i>>,
|
||||||
|
|
||||||
/// Count of met scopes at the particular moment of execution.
|
|
||||||
pub(crate) current_depth: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct SparseCell {
|
|
||||||
/// Scope depth where the value was set.
|
|
||||||
pub(crate) depth: usize,
|
|
||||||
pub(crate) value: Option<ValueAggregate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SparseCell {
|
|
||||||
pub(crate) fn from_value(depth: usize, value: ValueAggregate) -> Self {
|
|
||||||
Self {
|
|
||||||
depth,
|
|
||||||
value: Some(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_met_new(depth: usize) -> Self {
|
|
||||||
Self { depth, value: None }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i> Scalars<'i> {
|
impl<'i> Scalars<'i> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let allowed_depths = maplit::hashset! { GLOBAL_DEPTH };
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
non_iterable_variables: HashMap::new(),
|
non_iterable_variables: ValuesSparseMatrix::new(),
|
||||||
allowed_depths,
|
canon_streams: ValuesSparseMatrix::new(),
|
||||||
iterable_variables: HashMap::new(),
|
iterable_variables: HashMap::new(),
|
||||||
current_depth: GLOBAL_DEPTH,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if there was a previous value for the provided key on the same
|
/// Returns true if there was a previous value for the provided key on the same
|
||||||
/// fold block.
|
/// fold block.
|
||||||
pub(crate) fn set_value(&mut self, name: impl Into<String>, value: ValueAggregate) -> ExecutionResult<bool> {
|
pub(crate) fn set_scalar_value(&mut self, name: impl Into<String>, value: ValueAggregate) -> ExecutionResult<bool> {
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
self.non_iterable_variables.set_value(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
let name = name.into();
|
/// Returns true if there was a previous value for the provided key on the same
|
||||||
let variable_could_be_set = self.variable_could_be_set(&name);
|
/// fold block.
|
||||||
match self.non_iterable_variables.entry(name) {
|
pub(crate) fn set_canon_value(&mut self, name: impl Into<String>, value: CanonStream) -> ExecutionResult<bool> {
|
||||||
Vacant(entry) => {
|
self.canon_streams.set_value(name, value)
|
||||||
let cell = SparseCell::from_value(self.current_depth, value);
|
|
||||||
let cells = NonEmpty::new(cell);
|
|
||||||
entry.insert(cells);
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
Occupied(entry) => {
|
|
||||||
if !variable_could_be_set {
|
|
||||||
return Err(UncatchableError::ShadowingIsNotAllowed(entry.key().clone()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let values = entry.into_mut();
|
|
||||||
let last_cell = values.last_mut();
|
|
||||||
if last_cell.depth == self.current_depth {
|
|
||||||
// just rewrite a value if fold level is the same
|
|
||||||
last_cell.value = Some(value);
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
let new_cell = SparseCell::from_value(self.current_depth, value);
|
|
||||||
values.push(new_cell);
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_iterable_value(
|
pub(crate) fn set_iterable_value(
|
||||||
@ -188,20 +132,8 @@ impl<'i> Scalars<'i> {
|
|||||||
self.iterable_variables.remove(name);
|
self.iterable_variables.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_non_iterable_value(&'i self, name: &str) -> ExecutionResult<Option<&'i ValueAggregate>> {
|
pub(crate) fn get_non_iterable_scalar(&'i self, name: &str) -> ExecutionResult<Option<&'i ValueAggregate>> {
|
||||||
self.non_iterable_variables
|
self.non_iterable_variables.get_value(name)
|
||||||
.get(name)
|
|
||||||
.and_then(|values| {
|
|
||||||
let last_cell = values.last();
|
|
||||||
let depth_allowed = self.allowed_depths.contains(&last_cell.depth);
|
|
||||||
|
|
||||||
if depth_allowed {
|
|
||||||
Some(last_cell.value.as_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok_or_else(|| ExecutionError::Catchable(Rc::new(CatchableError::VariableNotFound(name.to_string()))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> {
|
pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> {
|
||||||
@ -210,8 +142,14 @@ impl<'i> Scalars<'i> {
|
|||||||
.ok_or_else(|| UncatchableError::FoldStateNotFound(name.to_string()).into())
|
.ok_or_else(|| UncatchableError::FoldStateNotFound(name.to_string()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_canon_stream(&'i self, name: &str) -> ExecutionResult<&'i CanonStream> {
|
||||||
|
self.canon_streams
|
||||||
|
.get_value(name)?
|
||||||
|
.ok_or_else(|| CatchableError::VariableWasNotInitializedAfterNew(name.to_string()).into())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_value(&'i self, name: &str) -> ExecutionResult<ScalarRef<'i>> {
|
pub(crate) fn get_value(&'i self, name: &str) -> ExecutionResult<ScalarRef<'i>> {
|
||||||
let value = self.get_non_iterable_value(name);
|
let value = self.get_non_iterable_scalar(name);
|
||||||
let iterable_value = self.iterable_variables.get(name);
|
let iterable_value = self.iterable_variables.get(name);
|
||||||
|
|
||||||
match (value, iterable_value) {
|
match (value, iterable_value) {
|
||||||
@ -223,141 +161,62 @@ impl<'i> Scalars<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variable_could_be_set(&self, variable_name: &str) -> bool {
|
||||||
|
self.non_iterable_variables.variable_could_be_set(variable_name)
|
||||||
|
|| self.canon_streams.variable_could_be_set(variable_name)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn meet_fold_start(&mut self) {
|
pub(crate) fn meet_fold_start(&mut self) {
|
||||||
self.current_depth += 1;
|
self.non_iterable_variables.meet_fold_start();
|
||||||
self.allowed_depths.insert(self.current_depth);
|
self.canon_streams.meet_fold_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// meet next before recursion
|
// meet next before recursion
|
||||||
pub(crate) fn meet_next_before(&mut self) {
|
pub(crate) fn meet_next_before(&mut self) {
|
||||||
self.allowed_depths.remove(&self.current_depth);
|
self.non_iterable_variables.meet_next_before();
|
||||||
self.current_depth += 1;
|
self.canon_streams.meet_next_before();
|
||||||
self.allowed_depths.insert(self.current_depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// meet next after recursion
|
// meet next after recursion
|
||||||
pub(crate) fn meet_next_after(&mut self) {
|
pub(crate) fn meet_next_after(&mut self) {
|
||||||
self.allowed_depths.remove(&self.current_depth);
|
self.non_iterable_variables.meet_next_after();
|
||||||
self.current_depth -= 1;
|
self.canon_streams.meet_next_after();
|
||||||
self.allowed_depths.insert(self.current_depth);
|
|
||||||
|
|
||||||
self.cleanup_obsolete_values();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn meet_fold_end(&mut self) {
|
pub(crate) fn meet_fold_end(&mut self) {
|
||||||
self.allowed_depths.remove(&self.current_depth);
|
self.non_iterable_variables.meet_fold_end();
|
||||||
self.current_depth -= 1;
|
self.canon_streams.meet_fold_end();
|
||||||
self.cleanup_obsolete_values();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn meet_new_start(&mut self, scalar_name: &str) {
|
pub(crate) fn meet_new_start_scalar(&mut self, scalar_name: String) {
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
self.non_iterable_variables.meet_new_start(scalar_name);
|
||||||
|
|
||||||
let new_cell = SparseCell::from_met_new(self.current_depth);
|
|
||||||
match self.non_iterable_variables.entry(scalar_name.to_string()) {
|
|
||||||
Vacant(entry) => {
|
|
||||||
let ne_vec = NonEmpty::new(new_cell);
|
|
||||||
entry.insert(ne_vec);
|
|
||||||
}
|
|
||||||
Occupied(entry) => {
|
|
||||||
let entry = entry.into_mut();
|
|
||||||
entry.push(new_cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn meet_new_end(&mut self, scalar_name: &str) -> ExecutionResult<()> {
|
pub(crate) fn meet_new_start_canon_stream(&mut self, canon_stream_name: String) {
|
||||||
let current_depth = self.current_depth;
|
self.canon_streams.meet_new_start(canon_stream_name);
|
||||||
let should_remove_values = self
|
|
||||||
.non_iterable_variables
|
|
||||||
.get_mut(scalar_name)
|
|
||||||
.and_then(|values| {
|
|
||||||
// carefully check that we're popping up an appropriate value,
|
|
||||||
// returning None means an error here
|
|
||||||
match values.pop() {
|
|
||||||
Some(value) if value.depth == current_depth => Some(false),
|
|
||||||
Some(_) => None,
|
|
||||||
// None means that the value was last in a row
|
|
||||||
None if values.last().depth == current_depth => Some(true),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok_or_else(|| UncatchableError::ScalarsStateCorrupted {
|
|
||||||
scalar_name: scalar_name.to_string(),
|
|
||||||
depth: self.current_depth,
|
|
||||||
})
|
|
||||||
.map_err(Into::<ExecutionError>::into)?;
|
|
||||||
|
|
||||||
if should_remove_values {
|
|
||||||
self.non_iterable_variables.remove(scalar_name);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn variable_could_be_set(&self, variable_name: &str) -> bool {
|
pub(crate) fn meet_new_end_scalar(&mut self, scalar_name: &str) -> ExecutionResult<()> {
|
||||||
if self.shadowing_allowed() {
|
self.non_iterable_variables.meet_new_end(scalar_name)
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.non_iterable_variables.get(variable_name) {
|
|
||||||
Some(values) => values.last().value.is_none(),
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn shadowing_allowed(&self) -> bool {
|
pub(crate) fn meet_new_end_canon_stream(&mut self, canon_name: &str) -> ExecutionResult<()> {
|
||||||
// shadowing is allowed only inside a fold block, 0 here means that execution flow
|
self.canon_streams.meet_new_end(canon_name)
|
||||||
// is in a global scope
|
|
||||||
self.current_depth != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cleanup_obsolete_values(&mut self) {
|
|
||||||
// TODO: it takes O(N) where N is a count of all scalars, but it could be optimized
|
|
||||||
// by maintaining array of value indices that should be removed on each depth level
|
|
||||||
let mut values_to_delete = Vec::new();
|
|
||||||
for (name, values) in self.non_iterable_variables.iter_mut() {
|
|
||||||
let value_depth = values.last().depth;
|
|
||||||
if !is_global_value(value_depth) && is_value_obsolete(value_depth, self.current_depth) {
|
|
||||||
// it can't be empty, so it returns None if it contains 1 element
|
|
||||||
if values.pop().is_none() {
|
|
||||||
// TODO: optimize this cloning in next PR
|
|
||||||
values_to_delete.push(name.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for value_name in values_to_delete {
|
|
||||||
self.non_iterable_variables.remove(&value_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_global_value(value_depth: usize) -> bool {
|
|
||||||
value_depth == GLOBAL_DEPTH
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_value_obsolete(value_depth: usize, current_scope_depth: usize) -> bool {
|
|
||||||
value_depth > current_scope_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
impl Default for Scalars<'_> {
|
impl Default for Scalars<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Scalars::new()
|
Scalars::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
impl<'i> fmt::Display for Scalars<'i> {
|
impl<'i> fmt::Display for Scalars<'i> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f, "fold_block_id: {}", self.current_depth)?;
|
writeln!(f, "scalars:\n{}", self.non_iterable_variables)?;
|
||||||
|
writeln!(f, "canon_streams:\n{}", self.canon_streams)?;
|
||||||
for (name, _) in self.non_iterable_variables.iter() {
|
|
||||||
let value = self.get_non_iterable_value(name);
|
|
||||||
if let Ok(Some(last_value)) = value {
|
|
||||||
writeln!(f, "{} => {}", name, last_value.result)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (name, _) in self.iterable_variables.iter() {
|
for (name, _) in self.iterable_variables.iter() {
|
||||||
// it's impossible to print an iterable value for now
|
// it's impossible to print an iterable value for now
|
||||||
@ -367,46 +226,3 @@ impl<'i> fmt::Display for Scalars<'i> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
use polyplets::SecurityTetraplet;
|
|
||||||
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use std::num::NonZeroUsize;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_local_cleanup() {
|
|
||||||
let mut scalars = Scalars::default();
|
|
||||||
|
|
||||||
let tetraplet = SecurityTetraplet::default();
|
|
||||||
let rc_tetraplet = Rc::new(tetraplet);
|
|
||||||
let value = json!(1u64);
|
|
||||||
let rc_value = Rc::new(value);
|
|
||||||
let value_aggregate = ValueAggregate::new(rc_value, rc_tetraplet, 1.into());
|
|
||||||
let value_1_name = "name_1";
|
|
||||||
scalars.set_value(value_1_name, value_aggregate.clone()).unwrap();
|
|
||||||
|
|
||||||
let value_2_name = "name_2";
|
|
||||||
scalars.meet_fold_start();
|
|
||||||
scalars.set_value(value_2_name, value_aggregate.clone()).unwrap();
|
|
||||||
scalars.meet_fold_start();
|
|
||||||
scalars.set_value(value_2_name, value_aggregate.clone()).unwrap();
|
|
||||||
|
|
||||||
let expected_values_count = scalars.non_iterable_variables.get(value_2_name).unwrap().len();
|
|
||||||
assert_eq!(expected_values_count, NonZeroUsize::new(2).unwrap());
|
|
||||||
|
|
||||||
scalars.meet_fold_end();
|
|
||||||
let expected_values_count = scalars.non_iterable_variables.get(value_2_name).unwrap().len();
|
|
||||||
assert_eq!(expected_values_count, NonZeroUsize::new(1).unwrap());
|
|
||||||
|
|
||||||
scalars.meet_fold_end();
|
|
||||||
assert!(scalars.non_iterable_variables.get(value_2_name).is_none());
|
|
||||||
|
|
||||||
let expected_values_count = scalars.non_iterable_variables.get(value_1_name).unwrap().len();
|
|
||||||
assert_eq!(expected_values_count, NonZeroUsize::new(1).unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,323 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::CatchableError;
|
||||||
|
use crate::execution_step::ExecutionError;
|
||||||
|
use crate::execution_step::ExecutionResult;
|
||||||
|
use crate::execution_step::UncatchableError;
|
||||||
|
|
||||||
|
use non_empty_vec::NonEmpty;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// Depth of a global scope.
|
||||||
|
const GLOBAL_DEPTH: usize = 0;
|
||||||
|
|
||||||
|
pub(crate) struct ValuesSparseMatrix<T> {
|
||||||
|
cells: HashMap<String, NonEmpty<SparseCell<T>>>,
|
||||||
|
|
||||||
|
/// This set contains depths were invalidated at the certain moment of script execution.
|
||||||
|
/// They are needed for careful isolation of scopes produced by iterations in fold blocks,
|
||||||
|
/// precisely to limit access of non iterable variables defined on one depths to ones
|
||||||
|
/// defined on another.
|
||||||
|
allowed_depths: HashSet<usize>,
|
||||||
|
|
||||||
|
/// Count of met scopes at the particular moment of execution.
|
||||||
|
current_depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ValuesSparseMatrix<T> {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
let allowed_depths = maplit::hashset! { GLOBAL_DEPTH };
|
||||||
|
|
||||||
|
Self {
|
||||||
|
cells: HashMap::new(),
|
||||||
|
allowed_depths,
|
||||||
|
current_depth: GLOBAL_DEPTH,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_value(&mut self, name: impl Into<String>, value: T) -> ExecutionResult<bool> {
|
||||||
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
|
||||||
|
let name = name.into();
|
||||||
|
let variable_could_be_set = self.variable_could_be_set(&name);
|
||||||
|
match self.cells.entry(name) {
|
||||||
|
Vacant(entry) => {
|
||||||
|
let cell = SparseCell::from_value(self.current_depth, value);
|
||||||
|
let cells = NonEmpty::new(cell);
|
||||||
|
entry.insert(cells);
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
Occupied(entry) => {
|
||||||
|
if !variable_could_be_set {
|
||||||
|
return Err(UncatchableError::ShadowingIsNotAllowed(entry.key().clone()).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let values = entry.into_mut();
|
||||||
|
let last_cell = values.last_mut();
|
||||||
|
if last_cell.depth == self.current_depth {
|
||||||
|
// just rewrite a value if fold level is the same
|
||||||
|
last_cell.value = Some(value);
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
let new_cell = SparseCell::from_value(self.current_depth, value);
|
||||||
|
values.push(new_cell);
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_value(&self, name: &str) -> ExecutionResult<Option<&T>> {
|
||||||
|
self.cells
|
||||||
|
.get(name)
|
||||||
|
.and_then(|values| {
|
||||||
|
let last_cell = values.last();
|
||||||
|
let depth_allowed = self.allowed_depths.contains(&last_cell.depth);
|
||||||
|
|
||||||
|
if depth_allowed {
|
||||||
|
Some(last_cell.value.as_ref())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or_else(|| ExecutionError::Catchable(Rc::new(CatchableError::VariableNotFound(name.to_string()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn meet_fold_start(&mut self) {
|
||||||
|
self.current_depth += 1;
|
||||||
|
self.allowed_depths.insert(self.current_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// meet next before recursion
|
||||||
|
pub(super) fn meet_next_before(&mut self) {
|
||||||
|
self.allowed_depths.remove(&self.current_depth);
|
||||||
|
self.current_depth += 1;
|
||||||
|
self.allowed_depths.insert(self.current_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// meet next after recursion
|
||||||
|
pub(super) fn meet_next_after(&mut self) {
|
||||||
|
self.allowed_depths.remove(&self.current_depth);
|
||||||
|
self.current_depth -= 1;
|
||||||
|
self.allowed_depths.insert(self.current_depth);
|
||||||
|
|
||||||
|
self.cleanup_obsolete_values();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn meet_fold_end(&mut self) {
|
||||||
|
self.allowed_depths.remove(&self.current_depth);
|
||||||
|
self.current_depth -= 1;
|
||||||
|
self.cleanup_obsolete_values();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn meet_new_start(&mut self, scalar_name: String) {
|
||||||
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
|
||||||
|
let new_cell = SparseCell::from_met_new(self.current_depth);
|
||||||
|
match self.cells.entry(scalar_name) {
|
||||||
|
Vacant(entry) => {
|
||||||
|
let ne_vec = NonEmpty::new(new_cell);
|
||||||
|
entry.insert(ne_vec);
|
||||||
|
}
|
||||||
|
Occupied(entry) => {
|
||||||
|
let entry = entry.into_mut();
|
||||||
|
entry.push(new_cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn meet_new_end(&mut self, scalar_name: &str) -> ExecutionResult<()> {
|
||||||
|
let current_depth = self.current_depth;
|
||||||
|
let should_remove_values = self
|
||||||
|
.cells
|
||||||
|
.get_mut(scalar_name)
|
||||||
|
.and_then(|values| {
|
||||||
|
// carefully check that we're popping up an appropriate value,
|
||||||
|
// returning None means an error here
|
||||||
|
match values.pop() {
|
||||||
|
Some(value) if value.depth == current_depth => Some(false),
|
||||||
|
Some(_) => None,
|
||||||
|
// None means that the value was last in a row
|
||||||
|
None if values.last().depth == current_depth => Some(true),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok_or_else(|| UncatchableError::ScalarsStateCorrupted {
|
||||||
|
scalar_name: scalar_name.to_string(),
|
||||||
|
depth: self.current_depth,
|
||||||
|
})
|
||||||
|
.map_err(Into::<ExecutionError>::into)?;
|
||||||
|
|
||||||
|
if should_remove_values {
|
||||||
|
self.cells.remove(scalar_name);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn variable_could_be_set(&self, variable_name: &str) -> bool {
|
||||||
|
if self.shadowing_allowed() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.cells.get(variable_name) {
|
||||||
|
Some(values) => values.last().value.is_none(),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn shadowing_allowed(&self) -> bool {
|
||||||
|
// shadowing is allowed only inside a fold block, 0 here means that execution flow
|
||||||
|
// is in a global scope
|
||||||
|
self.current_depth != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup_obsolete_values(&mut self) {
|
||||||
|
// TODO: it takes O(N) where N is a count of all scalars, but it could be optimized
|
||||||
|
// by maintaining array of value indices that should be removed on each depth level
|
||||||
|
let mut values_to_delete = Vec::new();
|
||||||
|
for (name, values) in self.cells.iter_mut() {
|
||||||
|
let value_depth = values.last().depth;
|
||||||
|
if !is_global_value(value_depth) && is_value_obsolete(value_depth, self.current_depth) {
|
||||||
|
// it can't be empty, so it returns None if it contains 1 element
|
||||||
|
if values.pop().is_none() {
|
||||||
|
// TODO: optimize this cloning in next PR
|
||||||
|
values_to_delete.push(name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for value_name in values_to_delete {
|
||||||
|
self.cells.remove(&value_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for ValuesSparseMatrix<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_global_value(value_depth: usize) -> bool {
|
||||||
|
value_depth == GLOBAL_DEPTH
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_value_obsolete(value_depth: usize, current_scope_depth: usize) -> bool {
|
||||||
|
value_depth > current_scope_depth
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct SparseCell<T> {
|
||||||
|
/// Scope depth where the value was set.
|
||||||
|
pub(crate) depth: usize,
|
||||||
|
pub(crate) value: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SparseCell<T> {
|
||||||
|
pub(crate) fn from_value(depth: usize, value: T) -> Self {
|
||||||
|
Self {
|
||||||
|
depth,
|
||||||
|
value: Some(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_met_new(depth: usize) -> Self {
|
||||||
|
Self { depth, value: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
impl<T> fmt::Display for SparseCell<T>
|
||||||
|
where
|
||||||
|
T: fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match &self.value {
|
||||||
|
Some(value) => write!(f, "{}", value),
|
||||||
|
None => write!(f, "none"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Display for ValuesSparseMatrix<T>
|
||||||
|
where
|
||||||
|
T: fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
writeln!(f, "current_depth: {}", self.current_depth)?;
|
||||||
|
|
||||||
|
for (name, values) in self.cells.iter() {
|
||||||
|
write!(f, "{}: ", name)?;
|
||||||
|
|
||||||
|
for value in values.iter() {
|
||||||
|
write!(f, "{} ", value)?;
|
||||||
|
}
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::execution_step::ValueAggregate;
|
||||||
|
use polyplets::SecurityTetraplet;
|
||||||
|
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_local_cleanup() {
|
||||||
|
let mut scalars = ValuesSparseMatrix::new();
|
||||||
|
|
||||||
|
let tetraplet = SecurityTetraplet::default();
|
||||||
|
let rc_tetraplet = Rc::new(tetraplet);
|
||||||
|
let value = json!(1u64);
|
||||||
|
let rc_value = Rc::new(value);
|
||||||
|
let value_aggregate = ValueAggregate::new(rc_value, rc_tetraplet, 1.into());
|
||||||
|
let value_1_name = "name_1";
|
||||||
|
scalars.set_value(value_1_name, value_aggregate.clone()).unwrap();
|
||||||
|
|
||||||
|
let value_2_name = "name_2";
|
||||||
|
scalars.meet_fold_start();
|
||||||
|
scalars.set_value(value_2_name, value_aggregate.clone()).unwrap();
|
||||||
|
scalars.meet_fold_start();
|
||||||
|
scalars.set_value(value_2_name, value_aggregate.clone()).unwrap();
|
||||||
|
|
||||||
|
let expected_values_count = scalars.cells.get(value_2_name).unwrap().len();
|
||||||
|
assert_eq!(expected_values_count, NonZeroUsize::new(2).unwrap());
|
||||||
|
|
||||||
|
scalars.meet_fold_end();
|
||||||
|
let expected_values_count = scalars.cells.get(value_2_name).unwrap().len();
|
||||||
|
assert_eq!(expected_values_count, NonZeroUsize::new(1).unwrap());
|
||||||
|
|
||||||
|
scalars.meet_fold_end();
|
||||||
|
assert!(scalars.cells.get(value_2_name).is_none());
|
||||||
|
|
||||||
|
let expected_values_count = scalars.cells.get(value_1_name).unwrap().len();
|
||||||
|
assert_eq!(expected_values_count, NonZeroUsize::new(1).unwrap());
|
||||||
|
}
|
||||||
|
}
|
@ -111,6 +111,10 @@ pub(crate) fn resolve_variable<'ctx, 'i>(
|
|||||||
None => Ok(Box::new(())),
|
None => Ok(Box::new(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Variable::CanonStream { name, .. } => {
|
||||||
|
let canon_stream = ctx.scalars.get_canon_stream(name)?;
|
||||||
|
Ok(Box::new(canon_stream))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,3 +246,23 @@ fn fold_with_join_behaviour() {
|
|||||||
let trace = trace_from_result(&result);
|
let trace = trace_from_result(&result);
|
||||||
assert_eq!(trace.len(), 2);
|
assert_eq!(trace.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_with_join_behaviour() {
|
||||||
|
let peer_1_id = "peer_1_id";
|
||||||
|
let peer_2_id = "peer_2_id";
|
||||||
|
|
||||||
|
let mut peer_2 = create_avm(unit_call_service(), peer_2_id);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(par
|
||||||
|
(call "{peer_1_id}" ("" "") [] $stream)
|
||||||
|
(canon "{peer_2_id}" $stream #canon_stream))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(peer_2, <_>::default(), script, "", "");
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let expected_trace = vec![executed_state::par(1, 0), executed_state::request_sent_by(peer_2_id)];
|
||||||
|
|
||||||
|
assert_eq!(actual_trace, expected_trace);
|
||||||
|
}
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
use air_test_utils::prelude::*;
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ap_with_scalars() {
|
fn ap_with_scalars() {
|
||||||
let vm_1_peer_id = "vm_1_peer_id";
|
let vm_1_peer_id = "vm_1_peer_id";
|
||||||
@ -210,3 +212,80 @@ fn ap_with_dst_stream() {
|
|||||||
assert_eq!(actual_trace, expected_state);
|
assert_eq!(actual_trace, expected_state);
|
||||||
assert!(result.next_peer_pks.is_empty());
|
assert!(result.next_peer_pks.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ap_canon_stream_with_lambda() {
|
||||||
|
let vm_1_peer_id = "vm_1_peer_id";
|
||||||
|
let (echo_call_service, tetraplet_checker) = tetraplet_host_function(echo_call_service());
|
||||||
|
let mut vm_1 = create_avm(echo_call_service, vm_1_peer_id);
|
||||||
|
|
||||||
|
let service_name = "some_service_name";
|
||||||
|
let function_name = "some_function_name";
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{vm_1_peer_id}" ("" "") [0] $stream)
|
||||||
|
(call "{vm_1_peer_id}" ("{service_name}" "{function_name}") [1] $stream))
|
||||||
|
(seq
|
||||||
|
(canon "{vm_1_peer_id}" $stream #canon_stream)
|
||||||
|
(seq
|
||||||
|
(ap #canon_stream.$.[1] $stream_2)
|
||||||
|
(call "{vm_1_peer_id}" ("" "") [$stream_2]))))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let expected_state = vec![
|
||||||
|
executed_state::stream_number(0, 0),
|
||||||
|
executed_state::stream_number(1, 1),
|
||||||
|
executed_state::canon(vec![0.into(), 1.into()]),
|
||||||
|
executed_state::ap(Some(0)),
|
||||||
|
executed_state::scalar(json!([1])),
|
||||||
|
];
|
||||||
|
assert_eq!(actual_trace, expected_state);
|
||||||
|
|
||||||
|
let expected_tetraplet = RefCell::new(vec![vec![SecurityTetraplet::new(
|
||||||
|
vm_1_peer_id,
|
||||||
|
service_name,
|
||||||
|
function_name,
|
||||||
|
".[1]",
|
||||||
|
)]]);
|
||||||
|
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ap_canon_stream() {
|
||||||
|
let vm_1_peer_id = "vm_1_peer_id";
|
||||||
|
let (echo_call_service, tetraplet_checker) = tetraplet_host_function(echo_call_service());
|
||||||
|
let mut vm_1 = create_avm(echo_call_service, vm_1_peer_id);
|
||||||
|
|
||||||
|
let service_name = "some_service_name";
|
||||||
|
let function_name = "some_function_name";
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{vm_1_peer_id}" ("" "") [0] $stream)
|
||||||
|
(call "{vm_1_peer_id}" ("{service_name}" "{function_name}") [1] $stream))
|
||||||
|
(seq
|
||||||
|
(canon "{vm_1_peer_id}" $stream #canon_stream)
|
||||||
|
(seq
|
||||||
|
(ap #canon_stream $stream_2)
|
||||||
|
(call "{vm_1_peer_id}" ("" "") [$stream_2]))))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let expected_state = vec![
|
||||||
|
executed_state::stream_number(0, 0),
|
||||||
|
executed_state::stream_number(1, 1),
|
||||||
|
executed_state::canon(vec![0.into(), 1.into()]),
|
||||||
|
executed_state::ap(Some(0)),
|
||||||
|
executed_state::scalar(json!([[0, 1]])),
|
||||||
|
];
|
||||||
|
assert_eq!(actual_trace, expected_state);
|
||||||
|
|
||||||
|
let expected_tetraplet = RefCell::new(vec![vec![SecurityTetraplet::new(vm_1_peer_id, "", "", "")]]);
|
||||||
|
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||||
|
}
|
||||||
|
235
air/tests/test_module/instructions/canon.rs
Normal file
235
air/tests/test_module/instructions/canon.rs
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::*;
|
||||||
|
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_moves_execution_flow() {
|
||||||
|
let mut vm = create_avm(echo_call_service(), "A");
|
||||||
|
let peer_id_1 = "peer_id_1";
|
||||||
|
let peer_id_2 = "peer_id_2";
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(par
|
||||||
|
(call "{peer_id_1}" ("" "") [] $stream)
|
||||||
|
(canon "{peer_id_2}" $stream #canon_stream)
|
||||||
|
)"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm, <_>::default(), script, "", "");
|
||||||
|
|
||||||
|
assert_next_pks!(&result.next_peer_pks, &[peer_id_1, peer_id_2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_canon() {
|
||||||
|
let mut vm = create_avm(echo_call_service(), "A");
|
||||||
|
let mut set_variable_vm = create_avm(
|
||||||
|
set_variable_call_service(json!(["1", "2", "3", "4", "5"])),
|
||||||
|
"set_variable",
|
||||||
|
);
|
||||||
|
|
||||||
|
let script = r#"
|
||||||
|
(seq
|
||||||
|
(call "set_variable" ("" "") [] Iterable)
|
||||||
|
(seq
|
||||||
|
(fold Iterable i
|
||||||
|
(seq
|
||||||
|
(call "A" ("" "") [i] $stream)
|
||||||
|
(next i)))
|
||||||
|
(canon "A" $stream #canon_stream)))
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let result = checked_call_vm!(set_variable_vm, <_>::default(), script, "", "");
|
||||||
|
let result = checked_call_vm!(vm, <_>::default(), script, "", result.data);
|
||||||
|
let actual_state = &trace_from_result(&result)[6.into()];
|
||||||
|
|
||||||
|
let expected_state = executed_state::canon(vec![1.into(), 2.into(), 3.into(), 4.into(), 5.into()]);
|
||||||
|
assert_eq!(actual_state, &expected_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_fixes_stream_correct() {
|
||||||
|
let peer_id_1 = "peer_id_1";
|
||||||
|
let mut vm_1 = create_avm(echo_call_service(), peer_id_1);
|
||||||
|
let peer_id_2 = "peer_id_2";
|
||||||
|
let mut vm_2 = create_avm(echo_call_service(), peer_id_2);
|
||||||
|
let peer_id_3 = "peer_id_3";
|
||||||
|
let mut vm_3 = create_avm(echo_call_service(), peer_id_3);
|
||||||
|
let peer_id_4 = "peer_id_4";
|
||||||
|
let mut vm_4 = create_avm(echo_call_service(), peer_id_4);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(par
|
||||||
|
(call "{peer_id_1}" ("" "") [1] $stream)
|
||||||
|
(par
|
||||||
|
(call "{peer_id_2}" ("" "") [2] $stream)
|
||||||
|
(call "{peer_id_3}" ("" "") [3] $stream)))
|
||||||
|
(seq
|
||||||
|
(call "{peer_id_4}" ("" "") [4])
|
||||||
|
(seq
|
||||||
|
(canon "{peer_id_3}" $stream #canon_stream)
|
||||||
|
(par
|
||||||
|
(call "{peer_id_3}" ("" "") [#canon_stream])
|
||||||
|
(call "{peer_id_1}" ("" "") [#canon_stream])))))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let vm_1_result_1 = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||||
|
let vm_2_result = checked_call_vm!(vm_2, <_>::default(), &script, "", "");
|
||||||
|
let vm_3_result_1 = checked_call_vm!(vm_3, <_>::default(), &script, "", vm_2_result.data);
|
||||||
|
let vm_4_result = checked_call_vm!(vm_4, <_>::default(), &script, "", vm_3_result_1.data.clone());
|
||||||
|
let vm_3_result_2 = checked_call_vm!(vm_3, <_>::default(), &script, vm_3_result_1.data, vm_4_result.data);
|
||||||
|
let actual_vm_3_result_2_trace = trace_from_result(&vm_3_result_2);
|
||||||
|
let expected_vm_3_result_2_trace = vec![
|
||||||
|
executed_state::par(1, 3),
|
||||||
|
executed_state::request_sent_by(peer_id_2),
|
||||||
|
executed_state::par(1, 1),
|
||||||
|
executed_state::stream_number(2, 0),
|
||||||
|
executed_state::stream_number(3, 1),
|
||||||
|
executed_state::scalar_number(4),
|
||||||
|
executed_state::canon(vec![3.into(), 4.into()]),
|
||||||
|
executed_state::par(1, 1),
|
||||||
|
executed_state::scalar(json!([2, 3])),
|
||||||
|
executed_state::request_sent_by(peer_id_3),
|
||||||
|
];
|
||||||
|
assert_eq!(actual_vm_3_result_2_trace, expected_vm_3_result_2_trace);
|
||||||
|
|
||||||
|
let vm_1_result_2 = checked_call_vm!(vm_1, <_>::default(), script, vm_1_result_1.data, vm_3_result_2.data);
|
||||||
|
let vm_1_result_2_trace = trace_from_result(&vm_1_result_2);
|
||||||
|
let expected_vm_1_result_2_trace = vec![
|
||||||
|
executed_state::par(1, 3),
|
||||||
|
executed_state::stream_number(1, 0),
|
||||||
|
executed_state::par(1, 1),
|
||||||
|
executed_state::stream_number(2, 1),
|
||||||
|
executed_state::stream_number(3, 1),
|
||||||
|
executed_state::scalar_number(4),
|
||||||
|
executed_state::canon(vec![3.into(), 4.into()]),
|
||||||
|
executed_state::par(1, 1),
|
||||||
|
executed_state::scalar(json!([2, 3])),
|
||||||
|
executed_state::scalar(json!([2, 3])),
|
||||||
|
];
|
||||||
|
assert_eq!(vm_1_result_2_trace, expected_vm_1_result_2_trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_stream_can_be_created_from_aps() {
|
||||||
|
let vm_1_peer_id = "vm_1_peer_id";
|
||||||
|
let mut vm_1 = create_avm(echo_call_service(), vm_1_peer_id);
|
||||||
|
|
||||||
|
let vm_2_peer_id = "vm_2_peer_id";
|
||||||
|
let mut vm_2 = create_avm(echo_call_service(), vm_2_peer_id);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(ap 0 $stream)
|
||||||
|
(ap 1 $stream))
|
||||||
|
(seq
|
||||||
|
(canon "{vm_1_peer_id}" $stream #canon_stream)
|
||||||
|
(seq
|
||||||
|
(ap #canon_stream $stream_2)
|
||||||
|
(call "{vm_2_peer_id}" ("" "") [$stream_2]))))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result_1 = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||||
|
let result_2 = checked_call_vm!(vm_2, <_>::default(), &script, "", result_1.data.clone());
|
||||||
|
// it fails on this call if canon merger can't handle ap results
|
||||||
|
let _ = checked_call_vm!(vm_2, <_>::default(), &script, result_1.data, result_2.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_gates() {
|
||||||
|
let peer_id_1 = "peer_id_1";
|
||||||
|
let mut vm_1 = create_avm(set_variable_call_service(json!([1, 2, 3, 4, 5])), peer_id_1);
|
||||||
|
|
||||||
|
let peer_id_2 = "peer_id_2";
|
||||||
|
let mut vm_2 = create_avm(echo_call_service(), peer_id_2);
|
||||||
|
|
||||||
|
let peer_id_3 = "peer_id_3";
|
||||||
|
let stop_len_count = 2;
|
||||||
|
let vm_3_call_service: CallServiceClosure = Box::new(move |params: CallRequestParams| -> CallServiceResult {
|
||||||
|
let value = params.arguments[0].as_array().unwrap().len();
|
||||||
|
if value >= stop_len_count {
|
||||||
|
CallServiceResult::ok(json!(true))
|
||||||
|
} else {
|
||||||
|
CallServiceResult::ok(json!(false))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut vm_3 = create_avm(vm_3_call_service, peer_id_3);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{peer_id_1}" ("" "") [] iterable)
|
||||||
|
(fold iterable iterator
|
||||||
|
(par
|
||||||
|
(call "{peer_id_2}" ("" "") [iterator] $stream)
|
||||||
|
(next iterator))))
|
||||||
|
(new $tmp
|
||||||
|
(fold $stream s
|
||||||
|
(xor
|
||||||
|
(seq
|
||||||
|
(ap s $tmp)
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(canon "{peer_id_3}" $tmp #t)
|
||||||
|
(call "{peer_id_3}" ("" "") [#t] x))
|
||||||
|
(match x true
|
||||||
|
(call "{peer_id_3}" ("" "") [#t]))))
|
||||||
|
(next s)))))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let vm_1_result = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||||
|
let vm_2_result = checked_call_vm!(vm_2, <_>::default(), &script, "", vm_1_result.data);
|
||||||
|
let vm_3_result = checked_call_vm!(vm_3, <_>::default(), &script, "", vm_2_result.data);
|
||||||
|
|
||||||
|
let actual_trace = trace_from_result(&vm_3_result);
|
||||||
|
let fold = match &actual_trace[11.into()] {
|
||||||
|
ExecutedState::Fold(fold_result) => fold_result,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// fold should stop at the correspond len
|
||||||
|
assert_eq!(fold.lore.len(), stop_len_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_empty_stream() {
|
||||||
|
let peer_id_1 = "peer_id_1";
|
||||||
|
let mut vm_1 = create_avm(echo_call_service(), peer_id_1);
|
||||||
|
let peer_id_2 = "peer_id_2";
|
||||||
|
let mut vm_2 = create_avm(echo_call_service(), peer_id_2);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(new $stream
|
||||||
|
(seq
|
||||||
|
(canon "{peer_id_1}" $stream #canon_stream)
|
||||||
|
(call "{peer_id_1}" ("" "") [#canon_stream])))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm_1, <_>::default(), &script, "", "");
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let expected_trace = vec![executed_state::canon(vec![]), executed_state::scalar(json!([]))];
|
||||||
|
assert_eq!(actual_trace, expected_trace);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(vm_2, <_>::default(), script, "", result.data);
|
||||||
|
let actual_trace = trace_from_result(&result);
|
||||||
|
let expected_trace = vec![executed_state::canon(vec![]), executed_state::scalar(json!([]))];
|
||||||
|
assert_eq!(actual_trace, expected_trace);
|
||||||
|
}
|
@ -115,3 +115,34 @@ fn fail_with_literals_tetraplets() {
|
|||||||
SecurityTetraplet::literal_tetraplet(local_peer_id)
|
SecurityTetraplet::literal_tetraplet(local_peer_id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_with_canon_stream() {
|
||||||
|
let vm_peer_id = "local_peer_id";
|
||||||
|
let error_code = 1337i64;
|
||||||
|
let error_message = "error message";
|
||||||
|
let mut vm = create_avm(
|
||||||
|
set_variable_call_service(json!({"error_code": error_code, "message": error_message})),
|
||||||
|
vm_peer_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{vm_peer_id}" ("" "") [] $stream)
|
||||||
|
(canon "{vm_peer_id}" $stream #canon_stream)
|
||||||
|
)
|
||||||
|
(fail #canon_stream.$.[0])
|
||||||
|
)"#);
|
||||||
|
|
||||||
|
let test_params = TestRunParameters::from_init_peer_id("init_peer_id");
|
||||||
|
let result = call_vm!(vm, test_params.clone(), script, "", "");
|
||||||
|
|
||||||
|
let expected_error = CatchableError::UserError {
|
||||||
|
error: rc!(json!( {
|
||||||
|
"error_code": error_code,
|
||||||
|
"message": error_message,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
assert!(check_error(&result, expected_error));
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
mod ap;
|
mod ap;
|
||||||
mod call;
|
mod call;
|
||||||
|
mod canon;
|
||||||
mod fail;
|
mod fail;
|
||||||
mod fold;
|
mod fold;
|
||||||
mod match_;
|
mod match_;
|
||||||
|
@ -14,17 +14,22 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mod impls;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
use super::Variable;
|
use super::CanonStream;
|
||||||
|
use super::Scalar;
|
||||||
|
use super::ScalarWithLambda;
|
||||||
|
use super::Stream;
|
||||||
use super::VariableWithLambda;
|
use super::VariableWithLambda;
|
||||||
use crate::ast::ScalarWithLambda;
|
|
||||||
|
|
||||||
use air_lambda_ast::LambdaAST;
|
use air_lambda_ast::LambdaAST;
|
||||||
|
|
||||||
|
use crate::ast::CanonStreamWithLambda;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
// TODO: rename CallInstrValue, since it'd used by the canon instruction
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
pub enum CallInstrValue<'i> {
|
pub enum CallInstrValue<'i> {
|
||||||
InitPeerId,
|
InitPeerId,
|
||||||
@ -60,7 +65,10 @@ pub enum Value<'i> {
|
|||||||
|
|
||||||
#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum CallOutputValue<'i> {
|
pub enum CallOutputValue<'i> {
|
||||||
Variable(Variable<'i>),
|
#[serde(borrow)]
|
||||||
|
Scalar(Scalar<'i>),
|
||||||
|
#[serde(borrow)]
|
||||||
|
Stream(Stream<'i>),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +83,15 @@ pub enum ApArgument<'i> {
|
|||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
EmptyArray,
|
EmptyArray,
|
||||||
Scalar(ScalarWithLambda<'i>),
|
Scalar(ScalarWithLambda<'i>),
|
||||||
|
CanonStream(CanonStreamWithLambda<'i>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum ApResult<'i> {
|
||||||
|
#[serde(borrow)]
|
||||||
|
Scalar(Scalar<'i>),
|
||||||
|
#[serde(borrow)]
|
||||||
|
Stream(Stream<'i>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
@ -87,5 +104,18 @@ pub enum Number {
|
|||||||
pub enum FoldScalarIterable<'i> {
|
pub enum FoldScalarIterable<'i> {
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
Scalar(ScalarWithLambda<'i>),
|
Scalar(ScalarWithLambda<'i>),
|
||||||
|
// it's important not to have lambda here
|
||||||
|
#[serde(borrow)]
|
||||||
|
CanonStream(CanonStream<'i>),
|
||||||
EmptyArray,
|
EmptyArray,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum NewArgument<'i> {
|
||||||
|
#[serde(borrow)]
|
||||||
|
Scalar(Scalar<'i>),
|
||||||
|
#[serde(borrow)]
|
||||||
|
Stream(Stream<'i>),
|
||||||
|
#[serde(borrow)]
|
||||||
|
CanonStream(CanonStream<'i>),
|
||||||
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::ApResult;
|
||||||
|
use super::CallOutputValue;
|
||||||
|
use super::NewArgument;
|
||||||
|
use super::Scalar;
|
||||||
|
use super::Stream;
|
||||||
|
|
||||||
|
impl<'i> NewArgument<'i> {
|
||||||
|
pub fn name(&self) -> &'i str {
|
||||||
|
match self {
|
||||||
|
Self::Scalar(scalar) => scalar.name,
|
||||||
|
Self::Stream(stream) => stream.name,
|
||||||
|
Self::CanonStream(canon_stream) => canon_stream.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> ApResult<'i> {
|
||||||
|
pub fn scalar(name: &'i str, position: usize) -> Self {
|
||||||
|
Self::Scalar(Scalar { name, position })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stream(name: &'i str, position: usize) -> Self {
|
||||||
|
Self::Stream(Stream { name, position })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &'i str {
|
||||||
|
match self {
|
||||||
|
Self::Scalar(scalar) => scalar.name,
|
||||||
|
Self::Stream(stream) => stream.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> CallOutputValue<'i> {
|
||||||
|
pub fn scalar(name: &'i str, position: usize) -> Self {
|
||||||
|
Self::Scalar(Scalar { name, position })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stream(name: &'i str, position: usize) -> Self {
|
||||||
|
Self::Stream(Stream { name, position })
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,17 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
impl fmt::Display for ApResult<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use ApResult::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Scalar(scalar) => write!(f, "{}", scalar),
|
||||||
|
Stream(stream) => write!(f, "{}", stream),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Value<'_> {
|
impl fmt::Display for Value<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
@ -52,7 +63,8 @@ impl fmt::Display for CallOutputValue<'_> {
|
|||||||
use CallOutputValue::*;
|
use CallOutputValue::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Variable(variable) => write!(f, "{}", variable),
|
Scalar(scalar) => write!(f, "{}", scalar),
|
||||||
|
Stream(stream) => write!(f, "{}", stream),
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,6 +84,7 @@ impl fmt::Display for ApArgument<'_> {
|
|||||||
Boolean(bool) => write!(f, "{}", bool),
|
Boolean(bool) => write!(f, "{}", bool),
|
||||||
EmptyArray => write!(f, "[]"),
|
EmptyArray => write!(f, "[]"),
|
||||||
Scalar(scalar) => write!(f, "{}", scalar),
|
Scalar(scalar) => write!(f, "{}", scalar),
|
||||||
|
CanonStream(canon_stream) => write!(f, "{}", canon_stream),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +99,16 @@ impl fmt::Display for Triplet<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NewArgument<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Scalar(scalar) => write!(f, "{}", scalar),
|
||||||
|
Self::Stream(stream) => write!(f, "{}", stream),
|
||||||
|
Self::CanonStream(canon_stream) => write!(f, "{}", canon_stream),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Number {
|
impl fmt::Display for Number {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use Number::*;
|
use Number::*;
|
||||||
@ -103,6 +126,7 @@ impl fmt::Display for FoldScalarIterable<'_> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Scalar(variable) => write!(f, "{}", variable),
|
Scalar(variable) => write!(f, "{}", variable),
|
||||||
|
CanonStream(canon_stream) => write!(f, "{}", canon_stream),
|
||||||
EmptyArray => write!(f, "[]"),
|
EmptyArray => write!(f, "[]"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ mod impls;
|
|||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use air_lambda_ast::LambdaAST;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ use std::rc::Rc;
|
|||||||
pub enum Instruction<'i> {
|
pub enum Instruction<'i> {
|
||||||
Call(Call<'i>),
|
Call(Call<'i>),
|
||||||
Ap(Ap<'i>),
|
Ap(Ap<'i>),
|
||||||
|
Canon(Canon<'i>),
|
||||||
Seq(Seq<'i>),
|
Seq(Seq<'i>),
|
||||||
Par(Par<'i>),
|
Par(Par<'i>),
|
||||||
Xor(Xor<'i>),
|
Xor(Xor<'i>),
|
||||||
@ -53,7 +55,15 @@ pub struct Call<'i> {
|
|||||||
#[derive(Serialize, Debug, PartialEq)]
|
#[derive(Serialize, Debug, PartialEq)]
|
||||||
pub struct Ap<'i> {
|
pub struct Ap<'i> {
|
||||||
pub argument: ApArgument<'i>,
|
pub argument: ApArgument<'i>,
|
||||||
pub result: Variable<'i>,
|
pub result: ApResult<'i>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (canon peer_id $stream #canon_stream)
|
||||||
|
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Canon<'i> {
|
||||||
|
pub peer_pk: CallInstrValue<'i>,
|
||||||
|
pub stream: Stream<'i>,
|
||||||
|
pub canon_stream: CanonStream<'i>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (seq instruction instruction)
|
/// (seq instruction instruction)
|
||||||
@ -93,6 +103,10 @@ pub enum Fail<'i> {
|
|||||||
ret_code: i64,
|
ret_code: i64,
|
||||||
error_message: &'i str,
|
error_message: &'i str,
|
||||||
},
|
},
|
||||||
|
CanonStream {
|
||||||
|
name: &'i str,
|
||||||
|
lambda: LambdaAST<'i>,
|
||||||
|
},
|
||||||
LastError,
|
LastError,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +140,7 @@ pub struct Next<'i> {
|
|||||||
/// (new variable instruction)
|
/// (new variable instruction)
|
||||||
#[derive(Serialize, Debug, PartialEq)]
|
#[derive(Serialize, Debug, PartialEq)]
|
||||||
pub struct New<'i> {
|
pub struct New<'i> {
|
||||||
pub variable: Variable<'i>,
|
pub argument: NewArgument<'i>,
|
||||||
pub instruction: Box<Instruction<'i>>,
|
pub instruction: Box<Instruction<'i>>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl<'i> Ap<'i> {
|
impl<'i> Ap<'i> {
|
||||||
pub fn new(argument: ApArgument<'i>, result: Variable<'i>) -> Self {
|
pub fn new(argument: ApArgument<'i>, result: ApResult<'i>) -> Self {
|
||||||
Self { argument, result }
|
Self { argument, result }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,6 +36,20 @@ impl<'i> Call<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'i> Canon<'i> {
|
||||||
|
pub fn new(
|
||||||
|
peer_pk: CallInstrValue<'i>,
|
||||||
|
stream: Stream<'i>,
|
||||||
|
canon_stream: CanonStream<'i>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
peer_pk,
|
||||||
|
stream,
|
||||||
|
canon_stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'i> Seq<'i> {
|
impl<'i> Seq<'i> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
left_instruction: Box<Instruction<'i>>,
|
left_instruction: Box<Instruction<'i>>,
|
||||||
@ -131,9 +145,9 @@ impl<'i> Next<'i> {
|
|||||||
|
|
||||||
impl<'i> New<'i> {
|
impl<'i> New<'i> {
|
||||||
#[allow(clippy::self_named_constructors)]
|
#[allow(clippy::self_named_constructors)]
|
||||||
pub fn new(variable: Variable<'i>, instruction: Box<Instruction<'i>>, span: Span) -> Self {
|
pub fn new(argument: NewArgument<'i>, instruction: Box<Instruction<'i>>, span: Span) -> Self {
|
||||||
Self {
|
Self {
|
||||||
variable,
|
argument,
|
||||||
instruction,
|
instruction,
|
||||||
span,
|
span,
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use air_lambda_ast::format_ast;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
impl fmt::Display for Instruction<'_> {
|
impl fmt::Display for Instruction<'_> {
|
||||||
@ -24,6 +25,7 @@ impl fmt::Display for Instruction<'_> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
Call(call) => write!(f, "{}", call),
|
Call(call) => write!(f, "{}", call),
|
||||||
|
Canon(canon) => write!(f, "{}", canon),
|
||||||
Ap(ap) => write!(f, "{}", ap),
|
Ap(ap) => write!(f, "{}", ap),
|
||||||
Seq(seq) => write!(f, "{}", seq),
|
Seq(seq) => write!(f, "{}", seq),
|
||||||
Par(par) => write!(f, "{}", par),
|
Par(par) => write!(f, "{}", par),
|
||||||
@ -50,6 +52,16 @@ impl fmt::Display for Call<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Canon<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"canon {} {} {}",
|
||||||
|
self.peer_pk, self.stream, self.canon_stream
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Ap<'_> {
|
impl fmt::Display for Ap<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "ap {} {}", self.argument, self.result)
|
write!(f, "ap {} {}", self.argument, self.result)
|
||||||
@ -64,6 +76,9 @@ impl fmt::Display for Fail<'_> {
|
|||||||
ret_code,
|
ret_code,
|
||||||
error_message,
|
error_message,
|
||||||
} => write!(f, r#"fail {} "{}""#, ret_code, error_message),
|
} => write!(f, r#"fail {} "{}""#, ret_code, error_message),
|
||||||
|
Fail::CanonStream { name, lambda, .. } => {
|
||||||
|
write!(f, "fail {}.$.{}", name, format_ast(lambda))
|
||||||
|
}
|
||||||
Fail::LastError => write!(f, "fail %last_error%"),
|
Fail::LastError => write!(f, "fail %last_error%"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,6 +140,6 @@ impl fmt::Display for Next<'_> {
|
|||||||
|
|
||||||
impl fmt::Display for New<'_> {
|
impl fmt::Display for New<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "new {}", self.variable)
|
write!(f, "new {}", self.argument)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,22 @@ pub struct StreamWithLambda<'i> {
|
|||||||
pub position: usize,
|
pub position: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A canonicalized stream without lambda.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CanonStream<'i> {
|
||||||
|
pub name: &'i str,
|
||||||
|
pub position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A canonicalized stream with lambda.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CanonStreamWithLambda<'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.
|
/// A variable that could be either scalar or stream without lambda.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
pub enum Variable<'i> {
|
pub enum Variable<'i> {
|
||||||
@ -61,6 +77,8 @@ pub enum Variable<'i> {
|
|||||||
Scalar(Scalar<'i>),
|
Scalar(Scalar<'i>),
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
Stream(Stream<'i>),
|
Stream(Stream<'i>),
|
||||||
|
#[serde(borrow)]
|
||||||
|
CanonStream(CanonStream<'i>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A variable that could be either scalar or stream with possible lambda expression.
|
/// A variable that could be either scalar or stream with possible lambda expression.
|
||||||
@ -70,4 +88,6 @@ pub enum VariableWithLambda<'i> {
|
|||||||
Scalar(ScalarWithLambda<'i>),
|
Scalar(ScalarWithLambda<'i>),
|
||||||
#[serde(borrow)]
|
#[serde(borrow)]
|
||||||
Stream(StreamWithLambda<'i>),
|
Stream(StreamWithLambda<'i>),
|
||||||
|
#[serde(borrow)]
|
||||||
|
CanonStream(CanonStreamWithLambda<'i>),
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,22 @@ impl<'i> StreamWithLambda<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'i> CanonStream<'i> {
|
||||||
|
pub fn new(name: &'i str, position: usize) -> Self {
|
||||||
|
Self { name, position }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> CanonStreamWithLambda<'i> {
|
||||||
|
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
lambda,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'i> Scalar<'i> {
|
impl<'i> Scalar<'i> {
|
||||||
pub fn new(name: &'i str, position: usize) -> Self {
|
pub fn new(name: &'i str, position: usize) -> Self {
|
||||||
Self { name, position }
|
Self { name, position }
|
||||||
@ -92,6 +108,7 @@ impl<'i> Variable<'i> {
|
|||||||
match self {
|
match self {
|
||||||
Variable::Scalar(scalar) => scalar.name,
|
Variable::Scalar(scalar) => scalar.name,
|
||||||
Variable::Stream(stream) => stream.name,
|
Variable::Stream(stream) => stream.name,
|
||||||
|
Variable::CanonStream(stream) => stream.name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,10 +130,19 @@ impl<'i> VariableWithLambda<'i> {
|
|||||||
Self::Stream(StreamWithLambda::new(name, Some(lambda), position))
|
Self::Stream(StreamWithLambda::new(name, Some(lambda), position))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn canon_stream(name: &'i str, position: usize) -> Self {
|
||||||
|
Self::CanonStream(CanonStreamWithLambda::new(name, None, position))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn canon_stream_wl(name: &'i str, lambda: LambdaAST<'i>, position: usize) -> Self {
|
||||||
|
Self::CanonStream(CanonStreamWithLambda::new(name, Some(lambda), position))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &'i str {
|
pub fn name(&self) -> &'i str {
|
||||||
match self {
|
match self {
|
||||||
VariableWithLambda::Scalar(scalar) => scalar.name,
|
VariableWithLambda::Scalar(scalar) => scalar.name,
|
||||||
VariableWithLambda::Stream(stream) => stream.name,
|
VariableWithLambda::Stream(stream) => stream.name,
|
||||||
|
VariableWithLambda::CanonStream(canon_stream) => canon_stream.name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +150,7 @@ impl<'i> VariableWithLambda<'i> {
|
|||||||
match self {
|
match self {
|
||||||
VariableWithLambda::Scalar(scalar) => &scalar.lambda,
|
VariableWithLambda::Scalar(scalar) => &scalar.lambda,
|
||||||
VariableWithLambda::Stream(stream) => &stream.lambda,
|
VariableWithLambda::Stream(stream) => &stream.lambda,
|
||||||
|
VariableWithLambda::CanonStream(canon_stream) => &canon_stream.lambda,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,12 @@ impl fmt::Display for Stream<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CanonStream<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for StreamWithLambda<'_> {
|
impl fmt::Display for StreamWithLambda<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match &self.lambda {
|
match &self.lambda {
|
||||||
@ -48,6 +54,15 @@ impl fmt::Display for StreamWithLambda<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CanonStreamWithLambda<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match &self.lambda {
|
||||||
|
Some(lambda) => write!(f, "{}.${}", self.name, format_ast(lambda)),
|
||||||
|
None => write!(f, "{}", self.name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Variable<'_> {
|
impl fmt::Display for Variable<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use Variable::*;
|
use Variable::*;
|
||||||
@ -55,6 +70,7 @@ impl fmt::Display for Variable<'_> {
|
|||||||
match self {
|
match self {
|
||||||
Scalar(scalar) => write!(f, "{}", scalar),
|
Scalar(scalar) => write!(f, "{}", scalar),
|
||||||
Stream(stream) => write!(f, "{}", stream),
|
Stream(stream) => write!(f, "{}", stream),
|
||||||
|
CanonStream(canon_stream) => write!(f, "{}", canon_stream),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,6 +82,7 @@ impl fmt::Display for VariableWithLambda<'_> {
|
|||||||
match self {
|
match self {
|
||||||
Scalar(scalar) => write!(f, "{}", scalar),
|
Scalar(scalar) => write!(f, "{}", scalar),
|
||||||
Stream(stream) => write!(f, "{}", stream),
|
Stream(stream) => write!(f, "{}", stream),
|
||||||
|
CanonStream(canon_stream) => write!(f, "{}", canon_stream),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ pub AIR = Instr;
|
|||||||
Instr: Box<Instruction<'input>> = {
|
Instr: Box<Instruction<'input>> = {
|
||||||
<left: @L> "(" call <triplet:Triplet> <args:Args> <output:CallOutput?> ")" <right: @R> => {
|
<left: @L> "(" call <triplet:Triplet> <args:Args> <output:CallOutput?> ")" <right: @R> => {
|
||||||
let args = Rc::new(args);
|
let args = Rc::new(args);
|
||||||
let output = output.map(CallOutputValue::Variable).unwrap_or(CallOutputValue::None);
|
let output = output.unwrap_or(CallOutputValue::None);
|
||||||
let call = Call::new(triplet, args, output);
|
let call = Call::new(triplet, args, output);
|
||||||
let span = Span::new(left, right);
|
let span = Span::new(left, right);
|
||||||
|
|
||||||
@ -25,6 +25,16 @@ Instr: Box<Instruction<'input>> = {
|
|||||||
Box::new(Instruction::Call(call))
|
Box::new(Instruction::Call(call))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
<left: @L> "(" canon <peer_pk:CallInstrValue> <stream:StreamArgument> <canon_stream:CanonStreamArgument> ")" <right: @R> => {
|
||||||
|
let canon = Canon::new(peer_pk, stream, canon_stream);
|
||||||
|
|
||||||
|
let span = Span::new(left, right);
|
||||||
|
validator.met_canon(&canon, span);
|
||||||
|
|
||||||
|
Box::new(Instruction::Canon(canon))
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
<left: @L> "(" ap <arg:ApArgument> <result:ApResult> ")" <right: @R> => {
|
<left: @L> "(" ap <arg:ApArgument> <result:ApResult> ")" <right: @R> => {
|
||||||
let apply = Ap::new(arg, result);
|
let apply = Ap::new(arg, result);
|
||||||
|
|
||||||
@ -38,9 +48,9 @@ Instr: Box<Instruction<'input>> = {
|
|||||||
"(" par <l:Instr> <r:Instr> ")" => Box::new(Instruction::Par(Par::new(l, r))),
|
"(" par <l:Instr> <r:Instr> ")" => Box::new(Instruction::Par(Par::new(l, r))),
|
||||||
"(" null ")" => Box::new(Instruction::Null(Null)),
|
"(" null ")" => Box::new(Instruction::Null(Null)),
|
||||||
|
|
||||||
<left: @L> "(" new <variable: ScriptVariable> <instruction:Instr> ")" <right: @R> => {
|
<left: @L> "(" new <argument: NewArgument> <instruction:Instr> ")" <right: @R> => {
|
||||||
let span = Span::new(left, right);
|
let span = Span::new(left, right);
|
||||||
let new = New::new(variable, instruction, span);
|
let new = New::new(argument, instruction, span);
|
||||||
|
|
||||||
validator.met_new(&new, span);
|
validator.met_new(&new, span);
|
||||||
|
|
||||||
@ -114,12 +124,14 @@ Triplet: Triplet<'input> = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ApResult = ScriptVariable;
|
ApResult: ApResult<'input> = {
|
||||||
CallOutput = ScriptVariable;
|
<scalar:Scalar> => ApResult::scalar(scalar.0, scalar.1),
|
||||||
|
<stream:Stream> => ApResult::stream(stream.0, stream.1),
|
||||||
|
};
|
||||||
|
|
||||||
ScriptVariable: Variable<'input> = {
|
CallOutput: CallOutputValue<'input> = {
|
||||||
<scalar:Scalar> => Variable::scalar(scalar.0, scalar.1),
|
<scalar:Scalar> => CallOutputValue::scalar(scalar.0, scalar.1),
|
||||||
<stream:Stream> => Variable::stream(stream.0, stream.1),
|
<stream:Stream> => CallOutputValue::stream(stream.0, stream.1),
|
||||||
};
|
};
|
||||||
|
|
||||||
FailBody: Fail<'input> = {
|
FailBody: Fail<'input> = {
|
||||||
@ -129,6 +141,10 @@ FailBody: Fail<'input> = {
|
|||||||
ret_code,
|
ret_code,
|
||||||
error_message,
|
error_message,
|
||||||
},
|
},
|
||||||
|
<canon_stream:CanonStreamWithLambda> => Fail::CanonStream {
|
||||||
|
name: canon_stream.0,
|
||||||
|
lambda: canon_stream.1,
|
||||||
|
},
|
||||||
<left: @L> <l:LastError> <right: @R> => {
|
<left: @L> <l:LastError> <right: @R> => {
|
||||||
Fail::LastError
|
Fail::LastError
|
||||||
}
|
}
|
||||||
@ -137,6 +153,7 @@ FailBody: Fail<'input> = {
|
|||||||
FoldScalarIterable: FoldScalarIterable<'input> = {
|
FoldScalarIterable: FoldScalarIterable<'input> = {
|
||||||
<scalar:Scalar> => FoldScalarIterable::Scalar(ScalarWithLambda::new(scalar.0, None, scalar.1)),
|
<scalar:Scalar> => FoldScalarIterable::Scalar(ScalarWithLambda::new(scalar.0, None, scalar.1)),
|
||||||
<scalar:ScalarWithLambda> => FoldScalarIterable::Scalar(ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2)),
|
<scalar:ScalarWithLambda> => FoldScalarIterable::Scalar(ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2)),
|
||||||
|
<canon_stream:CanonStream> => FoldScalarIterable::CanonStream(CanonStream::new(canon_stream.0, canon_stream.1)),
|
||||||
"[" "]" => FoldScalarIterable::EmptyArray,
|
"[" "]" => FoldScalarIterable::EmptyArray,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -144,6 +161,7 @@ Function = CallInstrValue;
|
|||||||
PeerId = CallInstrValue;
|
PeerId = CallInstrValue;
|
||||||
ServiceId = CallInstrValue;
|
ServiceId = CallInstrValue;
|
||||||
|
|
||||||
|
// TODO: call triplet should receive only streams with lambdas
|
||||||
CallInstrValue: CallInstrValue<'input> = {
|
CallInstrValue: CallInstrValue<'input> = {
|
||||||
InitPeerId => CallInstrValue::InitPeerId,
|
InitPeerId => CallInstrValue::InitPeerId,
|
||||||
<l:Literal> => CallInstrValue::Literal(l),
|
<l:Literal> => CallInstrValue::Literal(l),
|
||||||
@ -151,6 +169,14 @@ CallInstrValue: CallInstrValue<'input> = {
|
|||||||
<scalar:ScalarWithLambda> => CallInstrValue::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1, scalar.2)),
|
<scalar:ScalarWithLambda> => CallInstrValue::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1, scalar.2)),
|
||||||
<stream:Stream> => CallInstrValue::Variable(VariableWithLambda::stream(stream.0, stream.1)),
|
<stream:Stream> => CallInstrValue::Variable(VariableWithLambda::stream(stream.0, stream.1)),
|
||||||
<stream:StreamWithLambda> => CallInstrValue::Variable(VariableWithLambda::stream_wl(stream.0, stream.1, stream.2)),
|
<stream:StreamWithLambda> => CallInstrValue::Variable(VariableWithLambda::stream_wl(stream.0, stream.1, stream.2)),
|
||||||
|
<canon_stream:CanonStream> => CallInstrValue::Variable(VariableWithLambda::canon_stream(canon_stream.0, canon_stream.1)),
|
||||||
|
<canon_stream:CanonStreamWithLambda> => CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(canon_stream.0, canon_stream.1, canon_stream.2)),
|
||||||
|
}
|
||||||
|
|
||||||
|
NewArgument: NewArgument<'input> = {
|
||||||
|
<scalar:Scalar> => NewArgument::Scalar(Scalar::new(scalar.0, scalar.1)),
|
||||||
|
<stream:Stream> => NewArgument::Stream(Stream::new(stream.0, stream.1)),
|
||||||
|
<canon_stream:CanonStream> => NewArgument::CanonStream(CanonStream::new(canon_stream.0, canon_stream.1)),
|
||||||
}
|
}
|
||||||
|
|
||||||
Number: Number = {
|
Number: Number = {
|
||||||
@ -174,6 +200,8 @@ Value: Value<'input> = {
|
|||||||
<scalar:ScalarWithLambda> => Value::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1, scalar.2)),
|
<scalar:ScalarWithLambda> => Value::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1, scalar.2)),
|
||||||
<stream:Stream> => Value::Variable(VariableWithLambda::stream(stream.0, stream.1)),
|
<stream:Stream> => Value::Variable(VariableWithLambda::stream(stream.0, stream.1)),
|
||||||
<stream:StreamWithLambda> => Value::Variable(VariableWithLambda::stream_wl(stream.0, stream.1, stream.2)),
|
<stream:StreamWithLambda> => Value::Variable(VariableWithLambda::stream_wl(stream.0, stream.1, stream.2)),
|
||||||
|
<canon_stream:CanonStream> => Value::Variable(VariableWithLambda::canon_stream(canon_stream.0, canon_stream.1)),
|
||||||
|
<canon_stream:CanonStreamWithLambda> => Value::Variable(VariableWithLambda::canon_stream_wl(canon_stream.0, canon_stream.1, canon_stream.2)),
|
||||||
}
|
}
|
||||||
|
|
||||||
ApArgument: ApArgument<'input> = {
|
ApArgument: ApArgument<'input> = {
|
||||||
@ -188,6 +216,16 @@ ApArgument: ApArgument<'input> = {
|
|||||||
"[" "]" => ApArgument::EmptyArray,
|
"[" "]" => ApArgument::EmptyArray,
|
||||||
<scalar:Scalar> => ApArgument::Scalar(ScalarWithLambda::new(scalar.0, None, 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)),
|
<scalar:ScalarWithLambda> => ApArgument::Scalar(ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2)),
|
||||||
|
<canon_stream:CanonStream> => ApArgument::CanonStream(CanonStreamWithLambda::new(canon_stream.0, None, canon_stream.1)),
|
||||||
|
<canon_stream:CanonStreamWithLambda> => ApArgument::CanonStream(CanonStreamWithLambda::new(canon_stream.0, Some(canon_stream.1), canon_stream.2)),
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamArgument: Stream<'input> = {
|
||||||
|
<stream:Stream> => Stream::new(stream.0, stream.1),
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonStreamArgument: CanonStream<'input> = {
|
||||||
|
<canon_stream:CanonStream> => CanonStream::new(canon_stream.0, canon_stream.1),
|
||||||
}
|
}
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
@ -204,6 +242,8 @@ extern {
|
|||||||
ScalarWithLambda => Token::ScalarWithLambda { name: <&'input str>, lambda: <LambdaAST<'input>>, position: <usize> },
|
ScalarWithLambda => Token::ScalarWithLambda { name: <&'input str>, lambda: <LambdaAST<'input>>, position: <usize> },
|
||||||
Stream => Token::Stream { name: <&'input str>, position: <usize> },
|
Stream => Token::Stream { name: <&'input str>, position: <usize> },
|
||||||
StreamWithLambda => Token::StreamWithLambda {name: <&'input str>, lambda:<LambdaAST<'input>>, position: <usize>},
|
StreamWithLambda => Token::StreamWithLambda {name: <&'input str>, lambda:<LambdaAST<'input>>, position: <usize>},
|
||||||
|
CanonStream => Token::CanonStream { name: <&'input str>, position: <usize> },
|
||||||
|
CanonStreamWithLambda => Token::CanonStreamWithLambda {name: <&'input str>, lambda:<LambdaAST<'input>>, position: <usize>},
|
||||||
|
|
||||||
Literal => Token::StringLiteral(<&'input str>),
|
Literal => Token::StringLiteral(<&'input str>),
|
||||||
I64 => Token::I64(<i64>),
|
I64 => Token::I64(<i64>),
|
||||||
@ -217,6 +257,7 @@ extern {
|
|||||||
TTL => Token::TTL,
|
TTL => Token::TTL,
|
||||||
|
|
||||||
call => Token::Call,
|
call => Token::Call,
|
||||||
|
canon => Token::Canon,
|
||||||
ap => Token::Ap,
|
ap => Token::Ap,
|
||||||
seq => Token::Seq,
|
seq => Token::Seq,
|
||||||
par => Token::Par,
|
par => Token::Par,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -176,6 +176,7 @@ fn string_to_token(input: &str, start_pos: usize) -> LexerResult<Token> {
|
|||||||
"" => Err(LexerError::empty_string(start_pos..start_pos)),
|
"" => Err(LexerError::empty_string(start_pos..start_pos)),
|
||||||
|
|
||||||
CALL_INSTR => Ok(Token::Call),
|
CALL_INSTR => Ok(Token::Call),
|
||||||
|
CANON_INSTR => Ok(Token::Canon),
|
||||||
AP_INSTR => Ok(Token::Ap),
|
AP_INSTR => Ok(Token::Ap),
|
||||||
SEQ_INSTR => Ok(Token::Seq),
|
SEQ_INSTR => Ok(Token::Seq),
|
||||||
PAR_INSTR => Ok(Token::Par),
|
PAR_INSTR => Ok(Token::Par),
|
||||||
@ -226,6 +227,7 @@ fn parse_last_error(input: &str, start_pos: usize) -> LexerResult<Token<'_>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CALL_INSTR: &str = "call";
|
const CALL_INSTR: &str = "call";
|
||||||
|
const CANON_INSTR: &str = "canon";
|
||||||
const AP_INSTR: &str = "ap";
|
const AP_INSTR: &str = "ap";
|
||||||
const SEQ_INSTR: &str = "seq";
|
const SEQ_INSTR: &str = "seq";
|
||||||
const PAR_INSTR: &str = "par";
|
const PAR_INSTR: &str = "par";
|
||||||
|
@ -22,8 +22,6 @@ use crate::LambdaAST;
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::str::CharIndices;
|
use std::str::CharIndices;
|
||||||
|
|
||||||
const STREAM_START_TAG: char = '$';
|
|
||||||
|
|
||||||
pub(super) fn try_parse_call_variable(
|
pub(super) fn try_parse_call_variable(
|
||||||
string_to_parse: &str,
|
string_to_parse: &str,
|
||||||
start_pos: usize,
|
start_pos: usize,
|
||||||
@ -31,14 +29,21 @@ pub(super) fn try_parse_call_variable(
|
|||||||
CallVariableParser::try_parse(string_to_parse, start_pos)
|
CallVariableParser::try_parse(string_to_parse, start_pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum MetTag {
|
||||||
|
None,
|
||||||
|
Stream,
|
||||||
|
CanonStream,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ParserState {
|
struct ParserState {
|
||||||
pub(self) first_dot_met_pos: Option<usize>,
|
pub(self) first_dot_met_pos: Option<usize>,
|
||||||
pub(self) non_numeric_met: bool,
|
pub(self) non_numeric_met: bool,
|
||||||
pub(self) digit_met: bool,
|
pub(self) digit_met: bool,
|
||||||
pub(self) flattening_met: bool,
|
pub(self) flattening_met: bool,
|
||||||
|
pub(self) met_tag: MetTag,
|
||||||
pub(self) is_first_char: bool,
|
pub(self) is_first_char: bool,
|
||||||
pub(self) is_first_stream_tag: bool,
|
|
||||||
pub(self) current_char: char,
|
pub(self) current_char: char,
|
||||||
pub(self) current_pos: usize,
|
pub(self) current_pos: usize,
|
||||||
}
|
}
|
||||||
@ -64,7 +69,7 @@ impl<'input> CallVariableParser<'input> {
|
|||||||
digit_met: false,
|
digit_met: false,
|
||||||
flattening_met: false,
|
flattening_met: false,
|
||||||
is_first_char: true,
|
is_first_char: true,
|
||||||
is_first_stream_tag: false,
|
met_tag: MetTag::None,
|
||||||
current_char,
|
current_char,
|
||||||
current_pos,
|
current_pos,
|
||||||
};
|
};
|
||||||
@ -180,13 +185,14 @@ impl<'input> CallVariableParser<'input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_parse_as_stream_start(&mut self) -> LexerResult<bool> {
|
fn try_parse_as_stream_start(&mut self) -> LexerResult<bool> {
|
||||||
if self.current_pos() == 0 && self.current_char() == STREAM_START_TAG {
|
let stream_tag = MetTag::from_tag(self.current_char());
|
||||||
|
if self.current_pos() == 0 && stream_tag.is_tag() {
|
||||||
if self.string_to_parse.len() == 1 {
|
if self.string_to_parse.len() == 1 {
|
||||||
let error_pos = self.pos_in_string_to_parse();
|
let error_pos = self.pos_in_string_to_parse();
|
||||||
return Err(LexerError::empty_stream_name(error_pos..error_pos));
|
return Err(LexerError::empty_stream_name(error_pos..error_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.state.is_first_stream_tag = true;
|
self.state.met_tag = stream_tag;
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,32 +277,39 @@ impl<'input> CallVariableParser<'input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_variable_token<'v>(&self, name: &'v str) -> Token<'v> {
|
fn to_variable_token<'v>(&self, name: &'v str) -> Token<'v> {
|
||||||
if self.state.is_first_stream_tag {
|
match self.state.met_tag {
|
||||||
Token::Stream {
|
MetTag::None => Token::Scalar {
|
||||||
name,
|
name,
|
||||||
position: self.start_pos,
|
position: self.start_pos,
|
||||||
}
|
},
|
||||||
} else {
|
MetTag::Stream => Token::Stream {
|
||||||
Token::Scalar {
|
|
||||||
name,
|
name,
|
||||||
position: self.start_pos,
|
position: self.start_pos,
|
||||||
}
|
},
|
||||||
|
MetTag::CanonStream => Token::CanonStream {
|
||||||
|
name,
|
||||||
|
position: self.start_pos,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_variable_token_with_lambda<'v>(&self, name: &'v str, lambda: LambdaAST<'v>) -> Token<'v> {
|
fn to_variable_token_with_lambda<'v>(&self, name: &'v str, lambda: LambdaAST<'v>) -> Token<'v> {
|
||||||
if self.state.is_first_stream_tag {
|
match self.state.met_tag {
|
||||||
Token::StreamWithLambda {
|
MetTag::None => Token::ScalarWithLambda {
|
||||||
name,
|
name,
|
||||||
lambda,
|
lambda,
|
||||||
position: self.start_pos,
|
position: self.start_pos,
|
||||||
}
|
},
|
||||||
} else {
|
MetTag::Stream => Token::StreamWithLambda {
|
||||||
Token::ScalarWithLambda {
|
|
||||||
name,
|
name,
|
||||||
lambda,
|
lambda,
|
||||||
position: self.start_pos,
|
position: self.start_pos,
|
||||||
}
|
},
|
||||||
|
MetTag::CanonStream => Token::CanonStreamWithLambda {
|
||||||
|
name,
|
||||||
|
lambda,
|
||||||
|
position: self.start_pos,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,3 +369,17 @@ impl<'input> CallVariableParser<'input> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MetTag {
|
||||||
|
fn from_tag(tag: char) -> Self {
|
||||||
|
match tag {
|
||||||
|
'$' => Self::Stream,
|
||||||
|
'#' => Self::CanonStream,
|
||||||
|
_ => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_tag(&self) -> bool {
|
||||||
|
!matches!(self, Self::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -45,6 +45,15 @@ pub enum Token<'input> {
|
|||||||
lambda: LambdaAST<'input>,
|
lambda: LambdaAST<'input>,
|
||||||
position: usize,
|
position: usize,
|
||||||
},
|
},
|
||||||
|
CanonStream {
|
||||||
|
name: &'input str,
|
||||||
|
position: usize,
|
||||||
|
},
|
||||||
|
CanonStreamWithLambda {
|
||||||
|
name: &'input str,
|
||||||
|
lambda: LambdaAST<'input>,
|
||||||
|
position: usize,
|
||||||
|
},
|
||||||
|
|
||||||
StringLiteral(&'input str),
|
StringLiteral(&'input str),
|
||||||
I64(i64),
|
I64(i64),
|
||||||
@ -58,6 +67,7 @@ pub enum Token<'input> {
|
|||||||
TTL,
|
TTL,
|
||||||
|
|
||||||
Call,
|
Call,
|
||||||
|
Canon,
|
||||||
Ap,
|
Ap,
|
||||||
Seq,
|
Seq,
|
||||||
Par,
|
Par,
|
||||||
|
@ -19,6 +19,8 @@ use super::parse;
|
|||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
|
||||||
use air_lambda_ast::{LambdaAST, ValueAccessor};
|
use air_lambda_ast::{LambdaAST, ValueAccessor};
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ap_with_literal() {
|
fn ap_with_literal() {
|
||||||
@ -29,7 +31,7 @@ fn ap_with_literal() {
|
|||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(
|
let expected = ap(
|
||||||
ApArgument::Literal("some_string"),
|
ApArgument::Literal("some_string"),
|
||||||
Variable::stream("$stream", 27),
|
ApResult::Stream(Stream::new("$stream", 27)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -44,7 +46,7 @@ fn ap_with_number() {
|
|||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(
|
let expected = ap(
|
||||||
ApArgument::Number(Number::Int(-100)),
|
ApArgument::Number(Number::Int(-100)),
|
||||||
Variable::stream("$stream", 18),
|
ApResult::Stream(Stream::new("$stream", 18)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -57,7 +59,10 @@ fn ap_with_bool() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(ApArgument::Boolean(true), Variable::stream("$stream", 18));
|
let expected = ap(
|
||||||
|
ApArgument::Boolean(true),
|
||||||
|
ApResult::Stream(Stream::new("$stream", 18)),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -75,7 +80,7 @@ fn ap_with_last_error() {
|
|||||||
field_name: "message",
|
field_name: "message",
|
||||||
}])
|
}])
|
||||||
})),
|
})),
|
||||||
Variable::stream("$stream", 37),
|
ApResult::Stream(Stream::new("$stream", 37)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -88,7 +93,10 @@ fn ap_with_empty_array() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(ApArgument::EmptyArray, Variable::stream("$stream", 16));
|
let expected = ap(
|
||||||
|
ApArgument::EmptyArray,
|
||||||
|
ApResult::Stream(Stream::new("$stream", 16)),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -100,7 +108,10 @@ fn ap_with_init_peer_id() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(ApArgument::InitPeerId, Variable::stream("$stream", 28));
|
let expected = ap(
|
||||||
|
ApArgument::InitPeerId,
|
||||||
|
ApResult::Stream(Stream::new("$stream", 28)),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -112,7 +123,10 @@ fn ap_with_timestamp() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(ApArgument::Timestamp, Variable::stream("$stream", 25));
|
let expected = ap(
|
||||||
|
ApArgument::Timestamp,
|
||||||
|
ApResult::Stream(Stream::new("$stream", 25)),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -124,7 +138,48 @@ fn ap_with_ttl() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(ApArgument::TTL, Variable::stream("$stream", 19));
|
let expected = ap(
|
||||||
|
ApArgument::TTL,
|
||||||
|
ApResult::Stream(Stream::new("$stream", 19)),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ap_with_canon_stream() {
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let scalar = "scalar";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(ap {canon_stream} {scalar})
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let actual = parse(&source_code);
|
||||||
|
let expected = ap(
|
||||||
|
ApArgument::CanonStream(CanonStreamWithLambda::new(canon_stream, None, 13)),
|
||||||
|
ApResult::Scalar(Scalar::new(scalar, 27)),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ap_with_canon_stream_with_lambda() {
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let scalar = "scalar";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(ap {canon_stream}.$.[0] {scalar})
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let actual = parse(&source_code);
|
||||||
|
let expected = ap(
|
||||||
|
ApArgument::CanonStream(CanonStreamWithLambda::new(
|
||||||
|
canon_stream,
|
||||||
|
Some(unsafe { LambdaAST::new_unchecked(vec![ValueAccessor::ArrayAccess { idx: 0 }]) }),
|
||||||
|
13,
|
||||||
|
)),
|
||||||
|
ApResult::Scalar(Scalar::new(scalar, 33)),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ use super::parse;
|
|||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::parser::ParserError;
|
use crate::parser::ParserError;
|
||||||
|
|
||||||
use air_lambda_ast::ValueAccessor;
|
use air_lambda_ast::{LambdaAST, ValueAccessor};
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -43,7 +45,7 @@ fn parse_json_path() {
|
|||||||
Value::Literal("hello"),
|
Value::Literal("hello"),
|
||||||
Value::Variable(VariableWithLambda::scalar("name", 68)),
|
Value::Variable(VariableWithLambda::scalar("name", 68)),
|
||||||
]),
|
]),
|
||||||
CallOutputValue::Variable(Variable::stream("$void", 74)),
|
CallOutputValue::Stream(Stream::new("$void", 74)),
|
||||||
);
|
);
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
@ -193,7 +195,7 @@ fn parse_lambda_complex() {
|
|||||||
CallInstrValue::Literal("service_id"),
|
CallInstrValue::Literal("service_id"),
|
||||||
CallInstrValue::Literal("function_name"),
|
CallInstrValue::Literal("function_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("void", 75)),
|
CallOutputValue::Scalar(Scalar::new("void", 75)),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
@ -212,7 +214,7 @@ fn parse_lambda_complex() {
|
|||||||
CallInstrValue::Literal("service_id"),
|
CallInstrValue::Literal("service_id"),
|
||||||
CallInstrValue::Literal("function_name"),
|
CallInstrValue::Literal("function_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("void", 162)),
|
CallOutputValue::Scalar(Scalar::new("void", 162)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
@ -245,7 +247,7 @@ fn parse_lambda_with_scalars_complex() {
|
|||||||
CallInstrValue::Literal("service_id"),
|
CallInstrValue::Literal("service_id"),
|
||||||
CallInstrValue::Literal("function_name"),
|
CallInstrValue::Literal("function_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("void", 97)),
|
CallOutputValue::Scalar(Scalar::new("void", 97)),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
@ -270,7 +272,7 @@ fn parse_lambda_with_scalars_complex() {
|
|||||||
CallInstrValue::Literal("service_id"),
|
CallInstrValue::Literal("service_id"),
|
||||||
CallInstrValue::Literal("function_name"),
|
CallInstrValue::Literal("function_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("void", 205)),
|
CallOutputValue::Scalar(Scalar::new("void", 205)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
@ -310,7 +312,7 @@ fn json_path_square_braces() {
|
|||||||
64,
|
64,
|
||||||
)),
|
)),
|
||||||
]),
|
]),
|
||||||
CallOutputValue::Variable(Variable::stream("$void", 74)),
|
CallOutputValue::Stream(Stream::new("$void", 74)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
@ -410,6 +412,82 @@ fn parse_last_error() {
|
|||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_stream_in_args() {
|
||||||
|
let service_id = "service_id";
|
||||||
|
let function_name = "function_name";
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(call %init_peer_id% ("{service_id}" "{function_name}") [{canon_stream}])
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let instruction = parse(&source_code);
|
||||||
|
let expected = call(
|
||||||
|
CallInstrValue::InitPeerId,
|
||||||
|
CallInstrValue::Literal(service_id),
|
||||||
|
CallInstrValue::Literal(function_name),
|
||||||
|
Rc::new(vec![Value::Variable(VariableWithLambda::canon_stream(
|
||||||
|
canon_stream,
|
||||||
|
66,
|
||||||
|
))]),
|
||||||
|
CallOutputValue::None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_stream_in_triplet() {
|
||||||
|
let service_id = "service_id";
|
||||||
|
let function_name = "function_name";
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(call {canon_stream} ("{service_id}" "{function_name}") [])
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let instruction = parse(&source_code);
|
||||||
|
let expected = call(
|
||||||
|
CallInstrValue::Variable(VariableWithLambda::canon_stream(canon_stream, 19)),
|
||||||
|
CallInstrValue::Literal(service_id),
|
||||||
|
CallInstrValue::Literal(function_name),
|
||||||
|
Rc::new(vec![]),
|
||||||
|
CallOutputValue::None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_stream_with_lambda_in_triplet() {
|
||||||
|
let service_id = "service_id";
|
||||||
|
let function_name = "function_name";
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let canon_stream_lambda = ".$.[0].path!";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(call {canon_stream}{canon_stream_lambda} ("{service_id}" "{function_name}") [])
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let instruction = parse(&source_code);
|
||||||
|
let expected = call(
|
||||||
|
CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(
|
||||||
|
canon_stream,
|
||||||
|
unsafe {
|
||||||
|
LambdaAST::new_unchecked(vec![
|
||||||
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
|
ValueAccessor::FieldAccessByName { field_name: "path" },
|
||||||
|
])
|
||||||
|
},
|
||||||
|
19,
|
||||||
|
)),
|
||||||
|
CallInstrValue::Literal(service_id),
|
||||||
|
CallInstrValue::Literal(function_name),
|
||||||
|
Rc::new(vec![]),
|
||||||
|
CallOutputValue::None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn seq_par_call() {
|
fn seq_par_call() {
|
||||||
let peer_id = "some_peer_id";
|
let peer_id = "some_peer_id";
|
||||||
@ -430,14 +508,14 @@ fn seq_par_call() {
|
|||||||
CallInstrValue::Literal("local_service_id"),
|
CallInstrValue::Literal("local_service_id"),
|
||||||
CallInstrValue::Literal("local_fn_name"),
|
CallInstrValue::Literal("local_fn_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("result_1", 108)),
|
CallOutputValue::Scalar(Scalar::new("result_1", 108)),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
CallInstrValue::Literal(peer_id),
|
CallInstrValue::Literal(peer_id),
|
||||||
CallInstrValue::Literal("service_id"),
|
CallInstrValue::Literal("service_id"),
|
||||||
CallInstrValue::Literal("fn_name"),
|
CallInstrValue::Literal("fn_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("g", 183)),
|
CallOutputValue::Scalar(Scalar::new("g", 183)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
@ -445,7 +523,7 @@ fn seq_par_call() {
|
|||||||
CallInstrValue::Literal("local_service_id"),
|
CallInstrValue::Literal("local_service_id"),
|
||||||
CallInstrValue::Literal("local_fn_name"),
|
CallInstrValue::Literal("local_fn_name"),
|
||||||
Rc::new(vec![]),
|
Rc::new(vec![]),
|
||||||
CallOutputValue::Variable(Variable::scalar("result_2", 273)),
|
CallOutputValue::Scalar(Scalar::new("result_2", 273)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -485,14 +563,14 @@ fn seq_with_empty_and_dash() {
|
|||||||
CallInstrValue::Literal(""),
|
CallInstrValue::Literal(""),
|
||||||
CallInstrValue::Literal(""),
|
CallInstrValue::Literal(""),
|
||||||
Rc::new(vec![Value::Literal("module-bytes")]),
|
Rc::new(vec![Value::Literal("module-bytes")]),
|
||||||
CallOutputValue::Variable(Variable::scalar("module-bytes", 119)),
|
CallOutputValue::Scalar(Scalar::new("module-bytes", 119)),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
CallInstrValue::Literal("set_variables"),
|
CallInstrValue::Literal("set_variables"),
|
||||||
CallInstrValue::Literal(""),
|
CallInstrValue::Literal(""),
|
||||||
CallInstrValue::Literal(""),
|
CallInstrValue::Literal(""),
|
||||||
Rc::new(vec![Value::Literal("module_config")]),
|
Rc::new(vec![Value::Literal("module_config")]),
|
||||||
CallOutputValue::Variable(Variable::scalar("module_config", 201)),
|
CallOutputValue::Scalar(Scalar::new("module_config", 201)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
@ -500,7 +578,7 @@ fn seq_with_empty_and_dash() {
|
|||||||
CallInstrValue::Literal(""),
|
CallInstrValue::Literal(""),
|
||||||
CallInstrValue::Literal(""),
|
CallInstrValue::Literal(""),
|
||||||
Rc::new(vec![Value::Literal("blueprint")]),
|
Rc::new(vec![Value::Literal("blueprint")]),
|
||||||
CallOutputValue::Variable(Variable::scalar("blueprint", 294)),
|
CallOutputValue::Scalar(Scalar::new("blueprint", 294)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
seq(
|
seq(
|
||||||
@ -512,7 +590,7 @@ fn seq_with_empty_and_dash() {
|
|||||||
Value::Variable(VariableWithLambda::scalar("module-bytes", 381)),
|
Value::Variable(VariableWithLambda::scalar("module-bytes", 381)),
|
||||||
Value::Variable(VariableWithLambda::scalar("module_config", 394)),
|
Value::Variable(VariableWithLambda::scalar("module_config", 394)),
|
||||||
]),
|
]),
|
||||||
CallOutputValue::Variable(Variable::scalar("module", 409)),
|
CallOutputValue::Scalar(Scalar::new("module", 409)),
|
||||||
),
|
),
|
||||||
seq(
|
seq(
|
||||||
Instruction::Call(Call {
|
Instruction::Call(Call {
|
||||||
@ -525,7 +603,7 @@ fn seq_with_empty_and_dash() {
|
|||||||
"blueprint",
|
"blueprint",
|
||||||
490,
|
490,
|
||||||
))]),
|
))]),
|
||||||
output: CallOutputValue::Variable(Variable::scalar("blueprint_id", 501)),
|
output: CallOutputValue::Scalar(Scalar::new("blueprint_id", 501)),
|
||||||
}),
|
}),
|
||||||
seq(
|
seq(
|
||||||
call(
|
call(
|
||||||
@ -536,7 +614,7 @@ fn seq_with_empty_and_dash() {
|
|||||||
"blueprint_id",
|
"blueprint_id",
|
||||||
589,
|
589,
|
||||||
))]),
|
))]),
|
||||||
CallOutputValue::Variable(Variable::scalar("service_id", 603)),
|
CallOutputValue::Scalar(Scalar::new("service_id", 603)),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
CallInstrValue::Literal("remote_peer_id"),
|
CallInstrValue::Literal("remote_peer_id"),
|
||||||
@ -546,7 +624,7 @@ fn seq_with_empty_and_dash() {
|
|||||||
"service_id",
|
"service_id",
|
||||||
671,
|
671,
|
||||||
))]),
|
))]),
|
||||||
CallOutputValue::Variable(Variable::scalar("client_result", 683)),
|
CallOutputValue::Scalar(Scalar::new("client_result", 683)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
60
crates/air-lib/air-parser/src/parser/tests/canon.rs
Normal file
60
crates/air-lib/air-parser/src/parser/tests/canon.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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 fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_with_literal_peer_id() {
|
||||||
|
let peer_id = "peer_id";
|
||||||
|
let stream = "$stream";
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(canon "{peer_id}" {stream} {canon_stream})
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let actual = parse(&source_code);
|
||||||
|
let expected = canon(
|
||||||
|
CallInstrValue::Literal(peer_id),
|
||||||
|
Stream::new(stream, 26),
|
||||||
|
CanonStream::new(canon_stream, 34),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn canon_with_variable_peer_id() {
|
||||||
|
let peer_id = "peer_id";
|
||||||
|
let stream = "$stream";
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(canon {peer_id} {stream} {canon_stream})
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let actual = parse(&source_code);
|
||||||
|
let expected = canon(
|
||||||
|
CallInstrValue::Variable(VariableWithLambda::scalar(peer_id, 16)),
|
||||||
|
Stream::new(stream, 24),
|
||||||
|
CanonStream::new(canon_stream, 32),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
@ -54,12 +54,12 @@ pub(super) fn seqnn() -> Instruction<'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn new<'i>(
|
pub(super) fn new<'i>(
|
||||||
variable: Variable<'i>,
|
argument: NewArgument<'i>,
|
||||||
instruction: Instruction<'i>,
|
instruction: Instruction<'i>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Instruction<'i> {
|
) -> Instruction<'i> {
|
||||||
Instruction::New(New {
|
Instruction::New(New {
|
||||||
variable,
|
argument,
|
||||||
instruction: Box::new(instruction),
|
instruction: Box::new(instruction),
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
@ -98,6 +98,20 @@ pub(super) fn fold_scalar_variable<'i>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn fold_scalar_canon_stream<'i>(
|
||||||
|
canon_stream: CanonStream<'i>,
|
||||||
|
iterator: Scalar<'i>,
|
||||||
|
instruction: Instruction<'i>,
|
||||||
|
span: Span,
|
||||||
|
) -> Instruction<'i> {
|
||||||
|
Instruction::FoldScalar(FoldScalar {
|
||||||
|
iterable: FoldScalarIterable::CanonStream(canon_stream),
|
||||||
|
iterator,
|
||||||
|
instruction: Rc::new(instruction),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn fold_scalar_empty_array<'i>(
|
pub(super) fn fold_scalar_empty_array<'i>(
|
||||||
iterator: Scalar<'i>,
|
iterator: Scalar<'i>,
|
||||||
instruction: Instruction<'i>,
|
instruction: Instruction<'i>,
|
||||||
@ -149,10 +163,22 @@ pub(super) fn mismatch<'i>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn ap<'i>(argument: ApArgument<'i>, result: Variable<'i>) -> Instruction<'i> {
|
pub(super) fn ap<'i>(argument: ApArgument<'i>, result: ApResult<'i>) -> Instruction<'i> {
|
||||||
Instruction::Ap(Ap { argument, result })
|
Instruction::Ap(Ap { argument, result })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn canon<'i>(
|
||||||
|
peer_pk: CallInstrValue<'i>,
|
||||||
|
stream: Stream<'i>,
|
||||||
|
canon_stream: CanonStream<'i>,
|
||||||
|
) -> Instruction<'i> {
|
||||||
|
Instruction::Canon(Canon {
|
||||||
|
peer_pk,
|
||||||
|
stream,
|
||||||
|
canon_stream,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn binary_instruction<'i, 'b>(
|
pub(super) fn binary_instruction<'i, 'b>(
|
||||||
name: &'i str,
|
name: &'i str,
|
||||||
) -> impl Fn(Instruction<'b>, Instruction<'b>) -> Instruction<'b> {
|
) -> impl Fn(Instruction<'b>, Instruction<'b>) -> Instruction<'b> {
|
||||||
|
@ -20,6 +20,8 @@ use crate::ast::*;
|
|||||||
use crate::parser::ParserError;
|
use crate::parser::ParserError;
|
||||||
|
|
||||||
use air_lambda_ast::ValueAccessor;
|
use air_lambda_ast::ValueAccessor;
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -329,6 +331,24 @@ fn fold_on_stream() {
|
|||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fold_on_canon_stream() {
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let iterator = "iterator";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(fold {canon_stream} {iterator} (null))
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let instruction = parse(&source_code);
|
||||||
|
let expected = fold_scalar_canon_stream(
|
||||||
|
CanonStream::new(canon_stream, 15),
|
||||||
|
Scalar::new(iterator, 29),
|
||||||
|
null(),
|
||||||
|
Span::new(9, 45),
|
||||||
|
);
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn comments() {
|
fn comments() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
@ -18,6 +18,10 @@ use super::dsl::*;
|
|||||||
use super::parse;
|
use super::parse;
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
|
||||||
|
use air_lambda_ast::{LambdaAST, ValueAccessor};
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_match() {
|
fn parse_match() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
@ -34,6 +38,29 @@ fn parse_match() {
|
|||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_match_with_canon_stream() {
|
||||||
|
let canon_stream = "#canon_stream";
|
||||||
|
let canon_stream_lambda = ".$.[0]";
|
||||||
|
let source_code = f!(r#"
|
||||||
|
(match {canon_stream}{canon_stream_lambda} v2
|
||||||
|
(null)
|
||||||
|
)
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let instruction = parse(&source_code);
|
||||||
|
let expected = match_(
|
||||||
|
Value::Variable(VariableWithLambda::canon_stream_wl(
|
||||||
|
canon_stream,
|
||||||
|
unsafe { LambdaAST::new_unchecked(vec![ValueAccessor::ArrayAccess { idx: 0 }]) },
|
||||||
|
16,
|
||||||
|
)),
|
||||||
|
Value::Variable(VariableWithLambda::scalar("v2", 36)),
|
||||||
|
null(),
|
||||||
|
);
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_match_with_init_peer_id() {
|
fn parse_match_with_init_peer_id() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
mod ap;
|
mod ap;
|
||||||
mod call;
|
mod call;
|
||||||
|
mod canon;
|
||||||
mod dsl;
|
mod dsl;
|
||||||
mod fail;
|
mod fail;
|
||||||
mod fold;
|
mod fold;
|
||||||
@ -35,6 +36,7 @@ fn parse(source_code: &str) -> Instruction {
|
|||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let lexer = crate::parser::AIRLexer::new(source_code);
|
let lexer = crate::parser::AIRLexer::new(source_code);
|
||||||
let mut validator = crate::parser::VariableValidator::new();
|
let mut validator = crate::parser::VariableValidator::new();
|
||||||
|
|
||||||
parser
|
parser
|
||||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||||
.expect("parsing should be successful")
|
.expect("parsing should be successful")
|
||||||
|
@ -29,7 +29,11 @@ fn parse_new_with_scalar() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let instruction = parse(source_code);
|
let instruction = parse(source_code);
|
||||||
let expected = new(Variable::scalar("scalar", 5), null(), Span::new(0, 40));
|
let expected = new(
|
||||||
|
NewArgument::Scalar(Scalar::new("scalar", 5)),
|
||||||
|
null(),
|
||||||
|
Span::new(0, 40),
|
||||||
|
);
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +45,27 @@ fn parse_new_with_stream() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let instruction = parse(source_code);
|
let instruction = parse(source_code);
|
||||||
let expected = new(Variable::stream("$stream", 5), null(), Span::new(0, 41));
|
let expected = new(
|
||||||
|
NewArgument::Stream(Stream::new("$stream", 5)),
|
||||||
|
null(),
|
||||||
|
Span::new(0, 41),
|
||||||
|
);
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_new_with_canon_stream() {
|
||||||
|
let source_code = r#"(new #canon_stream
|
||||||
|
(null)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let instruction = parse(source_code);
|
||||||
|
let expected = new(
|
||||||
|
NewArgument::CanonStream(CanonStream::new("#canon_stream", 5)),
|
||||||
|
null(),
|
||||||
|
Span::new(0, 47),
|
||||||
|
);
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ fn parse_seq() {
|
|||||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id", 41)),
|
CallInstrValue::Variable(VariableWithLambda::scalar("service_id", 41)),
|
||||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name", 52)),
|
CallInstrValue::Variable(VariableWithLambda::scalar("function_name", 52)),
|
||||||
Rc::new(vec![Value::EmptyArray, Value::EmptyArray]),
|
Rc::new(vec![Value::EmptyArray, Value::EmptyArray]),
|
||||||
CallOutputValue::Variable(Variable::scalar("output", 75)),
|
CallOutputValue::Scalar(Scalar::new("output", 75)),
|
||||||
),
|
),
|
||||||
call(
|
call(
|
||||||
CallInstrValue::Literal("peer_id"),
|
CallInstrValue::Literal("peer_id"),
|
||||||
@ -90,7 +90,7 @@ fn parse_seq_seq() {
|
|||||||
Value::Literal("hello"),
|
Value::Literal("hello"),
|
||||||
Value::Variable(VariableWithLambda::scalar("name", 236)),
|
Value::Variable(VariableWithLambda::scalar("name", 236)),
|
||||||
]),
|
]),
|
||||||
CallOutputValue::Variable(Variable::stream("$output", 242)),
|
CallOutputValue::Stream(Stream::new("$output", 242)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
|
@ -20,6 +20,7 @@ use crate::parser::lexer::Token;
|
|||||||
use crate::parser::ParserError;
|
use crate::parser::ParserError;
|
||||||
use crate::parser::Span;
|
use crate::parser::Span;
|
||||||
|
|
||||||
|
use air_lambda_ast::LambdaAST;
|
||||||
use air_lambda_ast::ValueAccessor;
|
use air_lambda_ast::ValueAccessor;
|
||||||
use lalrpop_util::ErrorRecovery;
|
use lalrpop_util::ErrorRecovery;
|
||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
@ -71,11 +72,17 @@ impl<'i> VariableValidator<'i> {
|
|||||||
self.met_args(call.args.deref(), span);
|
self.met_args(call.args.deref(), span);
|
||||||
|
|
||||||
match &call.output {
|
match &call.output {
|
||||||
CallOutputValue::Variable(variable) => self.met_variable_definition(variable, span),
|
CallOutputValue::Scalar(scalar) => self.met_variable_name_definition(scalar.name, span),
|
||||||
|
CallOutputValue::Stream(stream) => self.met_variable_name_definition(stream.name, span),
|
||||||
CallOutputValue::None => {}
|
CallOutputValue::None => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn met_canon(&mut self, canon: &Canon<'i>, span: Span) {
|
||||||
|
self.met_variable_name(canon.stream.name, span);
|
||||||
|
self.met_variable_name_definition(canon.canon_stream.name, span);
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn met_match(&mut self, match_: &Match<'i>, span: Span) {
|
pub(super) fn met_match(&mut self, match_: &Match<'i>, span: Span) {
|
||||||
self.met_matchable(&match_.left_value, span);
|
self.met_matchable(&match_.left_value, span);
|
||||||
self.met_matchable(&match_.right_value, span);
|
self.met_matchable(&match_.right_value, span);
|
||||||
@ -90,7 +97,13 @@ impl<'i> VariableValidator<'i> {
|
|||||||
use FoldScalarIterable::*;
|
use FoldScalarIterable::*;
|
||||||
|
|
||||||
match &fold.iterable {
|
match &fold.iterable {
|
||||||
Scalar(variable) => self.met_variable_name(variable.name, span),
|
Scalar(variable) => {
|
||||||
|
self.met_variable_name(variable.name, span);
|
||||||
|
self.met_maybe_lambda(&variable.lambda, span);
|
||||||
|
}
|
||||||
|
CanonStream(canon_stream) => {
|
||||||
|
self.met_variable_name(canon_stream.name, span);
|
||||||
|
}
|
||||||
EmptyArray => {}
|
EmptyArray => {}
|
||||||
};
|
};
|
||||||
self.met_iterator_definition(&fold.iterator, span);
|
self.met_iterator_definition(&fold.iterator, span);
|
||||||
@ -103,9 +116,9 @@ impl<'i> VariableValidator<'i> {
|
|||||||
|
|
||||||
pub(super) fn met_new(&mut self, new: &New<'i>, span: Span) {
|
pub(super) fn met_new(&mut self, new: &New<'i>, span: Span) {
|
||||||
self.not_iterators_candidates
|
self.not_iterators_candidates
|
||||||
.push((new.variable.name(), span));
|
.push((new.argument.name(), span));
|
||||||
// new defines a new variable
|
// new defines a new variable
|
||||||
self.met_variable_definition(&new.variable, span);
|
self.met_variable_name_definition(new.argument.name(), span);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn met_next(&mut self, next: &Next<'i>, span: Span) {
|
pub(super) fn met_next(&mut self, next: &Next<'i>, span: Span) {
|
||||||
@ -128,10 +141,15 @@ impl<'i> VariableValidator<'i> {
|
|||||||
| ApArgument::EmptyArray
|
| ApArgument::EmptyArray
|
||||||
| ApArgument::LastError(_) => {}
|
| ApArgument::LastError(_) => {}
|
||||||
ApArgument::Scalar(scalar) => {
|
ApArgument::Scalar(scalar) => {
|
||||||
self.met_variable_wl(&VariableWithLambda::Scalar(scalar.clone()), span)
|
self.met_variable_name(scalar.name, span);
|
||||||
|
self.met_maybe_lambda(&scalar.lambda, span);
|
||||||
|
}
|
||||||
|
ApArgument::CanonStream(canon_stream) => {
|
||||||
|
self.met_variable_name(canon_stream.name, span);
|
||||||
|
self.met_maybe_lambda(&canon_stream.lambda, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.met_variable_definition(&ap.result, span);
|
self.met_variable_name_definition(ap.result.name(), span);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn finalize(self) -> Vec<ErrorRecovery<usize, Token<'i>, ParserError>> {
|
pub(super) fn finalize(self) -> Vec<ErrorRecovery<usize, Token<'i>, ParserError>> {
|
||||||
@ -171,11 +189,24 @@ impl<'i> VariableValidator<'i> {
|
|||||||
|
|
||||||
fn met_variable_wl(&mut self, variable: &VariableWithLambda<'i>, span: Span) {
|
fn met_variable_wl(&mut self, variable: &VariableWithLambda<'i>, span: Span) {
|
||||||
self.met_variable_name(variable.name(), span);
|
self.met_variable_name(variable.name(), span);
|
||||||
let lambda = match variable.lambda() {
|
self.met_maybe_lambda(variable.lambda(), span);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn met_variable_name(&mut self, name: &'i str, span: Span) {
|
||||||
|
if !self.contains_variable(name, span) {
|
||||||
|
self.unresolved_variables.insert(name, span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn met_maybe_lambda(&mut self, lambda: &Option<LambdaAST<'i>>, span: Span) {
|
||||||
|
let lambda = match lambda {
|
||||||
Some(lambda) => lambda,
|
Some(lambda) => lambda,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
self.met_lambda(lambda, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn met_lambda(&mut self, lambda: &LambdaAST<'i>, span: Span) {
|
||||||
for accessor in lambda.iter() {
|
for accessor in lambda.iter() {
|
||||||
match accessor {
|
match accessor {
|
||||||
&ValueAccessor::FieldAccessByScalar { scalar_name } => {
|
&ValueAccessor::FieldAccessByScalar { scalar_name } => {
|
||||||
@ -188,12 +219,6 @@ impl<'i> VariableValidator<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn met_variable_name(&mut self, name: &'i str, span: Span) {
|
|
||||||
if !self.contains_variable(name, span) {
|
|
||||||
self.unresolved_variables.insert(name, span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_variable(&self, key: &str, key_span: Span) -> bool {
|
fn contains_variable(&self, key: &str, key_span: Span) -> bool {
|
||||||
if let Some(found_span) = self.met_variable_definitions.get(key) {
|
if let Some(found_span) = self.met_variable_definitions.get(key) {
|
||||||
if found_span < &key_span {
|
if found_span < &key_span {
|
||||||
@ -209,10 +234,6 @@ impl<'i> VariableValidator<'i> {
|
|||||||
found_spans.iter().any(|s| s < &key_span)
|
found_spans.iter().any(|s| s < &key_span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn met_variable_definition(&mut self, variable: &Variable<'i>, span: Span) {
|
|
||||||
self.met_variable_name_definition(variable.name(), span);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn met_variable_name_definition(&mut self, name: &'i str, span: Span) {
|
fn met_variable_name_definition(&mut self, name: &'i str, span: Span) {
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
## Version 0.3.0
|
||||||
|
|
||||||
|
[PR 292](https://github.com/fluencelabs/aquavm/pull/292):
|
||||||
|
- added a new state in data for a canon instruction result
|
||||||
|
|
||||||
## Version 0.2.2
|
## Version 0.2.2
|
||||||
|
|
||||||
[PR 169](https://github.com/fluencelabs/aquavm/pull/169):
|
[PR 169](https://github.com/fluencelabs/aquavm/pull/169):
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "air-interpreter-data"
|
name = "air-interpreter-data"
|
||||||
description = "Data format of the AIR interpreter"
|
description = "Data format of the AIR interpreter"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
@ -124,6 +124,14 @@ pub struct ApResult {
|
|||||||
pub res_generations: Vec<u32>,
|
pub res_generations: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains ids of element that were on a stream at the moment of an appropriate canon call.
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct CanonResult {
|
||||||
|
#[serde(rename = "ids")]
|
||||||
|
pub stream_elements_pos: Vec<TracePos>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ExecutedState {
|
pub enum ExecutedState {
|
||||||
@ -132,4 +140,5 @@ pub enum ExecutedState {
|
|||||||
Call(CallResult),
|
Call(CallResult),
|
||||||
Fold(FoldResult),
|
Fold(FoldResult),
|
||||||
Ap(ApResult),
|
Ap(ApResult),
|
||||||
|
Canon(CanonResult),
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,14 @@ impl ApResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CanonResult {
|
||||||
|
pub fn new(stream_elements_pos: Vec<TracePos>) -> Self {
|
||||||
|
Self {
|
||||||
|
stream_elements_pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ExecutedState {
|
impl std::fmt::Display for ExecutedState {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use CallResult::*;
|
use CallResult::*;
|
||||||
@ -121,6 +129,9 @@ impl std::fmt::Display for ExecutedState {
|
|||||||
Ap(ap) => {
|
Ap(ap) => {
|
||||||
write!(f, "ap: _ -> {:?}", ap.res_generations)
|
write!(f, "ap: _ -> {:?}", ap.res_generations)
|
||||||
}
|
}
|
||||||
|
Canon(canon) => {
|
||||||
|
write!(f, "canon {:?}", canon.stream_elements_pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "polyplets"
|
name = "polyplets"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
description = "Security primitives to verify origin of service calls in Fluence network"
|
description = "Security primitives to verify origin of service calls in Fluence network"
|
||||||
authors = ["Fluence Labs"]
|
authors = ["Fluence Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -79,3 +79,15 @@ impl SecurityTetraplet {
|
|||||||
self.json_path.push_str(json_path)
|
self.json_path.push_str(json_path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
impl fmt::Display for SecurityTetraplet {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"peer_pk: {}, service_id: {}, function_name: {}, json_path: {}",
|
||||||
|
self.peer_pk, self.service_id, self.function_name, self.json_path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use super::ApResult;
|
use super::ApResult;
|
||||||
use super::CallResult;
|
use super::CallResult;
|
||||||
|
use super::CanonResult;
|
||||||
use super::ExecutedState;
|
use super::ExecutedState;
|
||||||
use super::JValue;
|
use super::JValue;
|
||||||
use super::ParResult;
|
use super::ParResult;
|
||||||
@ -138,6 +139,11 @@ pub fn ap(dst: Option<u32>) -> ExecutedState {
|
|||||||
ExecutedState::Ap(ap_result)
|
ExecutedState::Ap(ap_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn canon(stream_elements_pos: Vec<TracePos>) -> ExecutedState {
|
||||||
|
let canon_result = CanonResult::new(stream_elements_pos);
|
||||||
|
ExecutedState::Canon(canon_result)
|
||||||
|
}
|
||||||
|
|
||||||
fn option_to_vec(maybe_value: Option<u32>) -> Vec<u32> {
|
fn option_to_vec(maybe_value: Option<u32>) -> Vec<u32> {
|
||||||
match maybe_value {
|
match maybe_value {
|
||||||
Some(value) => vec![value],
|
Some(value) => vec![value],
|
||||||
|
@ -18,6 +18,7 @@ air-interpreter-data = { path = "../interpreter-data" }
|
|||||||
air-log-targets = { path = "../log-targets" }
|
air-log-targets = { path = "../log-targets" }
|
||||||
air-parser = { path = "../air-parser" }
|
air-parser = { path = "../air-parser" }
|
||||||
|
|
||||||
|
bimap = "0.6.2"
|
||||||
serde_json = "1.0.68"
|
serde_json = "1.0.68"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.29"
|
||||||
|
@ -21,14 +21,15 @@ use crate::TracePos;
|
|||||||
|
|
||||||
use air_interpreter_data::InterpreterData;
|
use air_interpreter_data::InterpreterData;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use bimap::BiHashMap;
|
||||||
|
|
||||||
/// Keeps all necessary data for merging.
|
/// Keeps all necessary data for merging.
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
pub(crate) struct DataKeeper {
|
pub(crate) struct DataKeeper {
|
||||||
pub(crate) prev_ctx: MergeCtx,
|
pub(crate) prev_ctx: MergeCtx,
|
||||||
pub(crate) current_ctx: MergeCtx,
|
pub(crate) current_ctx: MergeCtx,
|
||||||
pub(crate) new_to_old_pos: HashMap<TracePos, DataPositions>,
|
pub(crate) new_to_prev_pos: BiHashMap<TracePos, TracePos>,
|
||||||
|
pub(crate) new_to_current_pos: BiHashMap<TracePos, TracePos>,
|
||||||
pub(crate) result_trace: ExecutionTrace,
|
pub(crate) result_trace: ExecutionTrace,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +41,8 @@ impl DataKeeper {
|
|||||||
Self {
|
Self {
|
||||||
prev_ctx,
|
prev_ctx,
|
||||||
current_ctx,
|
current_ctx,
|
||||||
new_to_old_pos: <_>::default(),
|
new_to_prev_pos: <_>::default(),
|
||||||
|
new_to_current_pos: <_>::default(),
|
||||||
result_trace: <_>::default(),
|
result_trace: <_>::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,9 +71,3 @@ impl DataKeeper {
|
|||||||
&mut self.current_ctx.slider
|
&mut self.current_ctx.slider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub(crate) struct DataPositions {
|
|
||||||
pub(crate) prev_pos: Option<TracePos>,
|
|
||||||
pub(crate) current_pos: Option<TracePos>,
|
|
||||||
}
|
|
||||||
|
@ -24,7 +24,6 @@ pub use merge_ctx::MergeCtx;
|
|||||||
pub use trace_slider::TraceSlider;
|
pub use trace_slider::TraceSlider;
|
||||||
|
|
||||||
pub(crate) use keeper::DataKeeper;
|
pub(crate) use keeper::DataKeeper;
|
||||||
pub(crate) use keeper::DataPositions;
|
|
||||||
|
|
||||||
pub(self) type KeeperResult<T> = std::result::Result<T, KeeperError>;
|
pub(self) type KeeperResult<T> = std::result::Result<T, KeeperError>;
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ impl TraceSlider {
|
|||||||
self.subtrace_len - self.seen_elements
|
self.subtrace_len - self.seen_elements
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn state_at_position(&self, position: TracePos) -> Option<&ExecutedState> {
|
pub(crate) fn state_at_position(&self, position: TracePos) -> Option<&ExecutedState> {
|
||||||
// it would be nice to have the `impl SliceIndex for TracePos`, but it is unstable
|
// it would be nice to have the `impl SliceIndex for TracePos`, but it is unstable
|
||||||
self.trace.get(position)
|
self.trace.get(position)
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,16 @@ impl TraceHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TraceHandler {
|
||||||
|
pub fn meet_canon_start(&mut self) -> TraceHandlerResult<MergerCanonResult> {
|
||||||
|
try_merge_next_state_as_canon(&mut self.data_keeper).map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meet_canon_end(&mut self, canon_result: CanonResult) {
|
||||||
|
self.data_keeper.result_trace.push(ExecutedState::Canon(canon_result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TraceHandler {
|
impl TraceHandler {
|
||||||
pub fn meet_par_start(&mut self) -> TraceHandlerResult<()> {
|
pub fn meet_par_start(&mut self) -> TraceHandlerResult<()> {
|
||||||
let ingredients = merger::try_merge_next_state_as_par(&mut self.data_keeper)?;
|
let ingredients = merger::try_merge_next_state_as_par(&mut self.data_keeper)?;
|
||||||
|
@ -14,6 +14,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
nonstandard_style,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_variables,
|
||||||
|
unused_unsafe,
|
||||||
|
unreachable_patterns
|
||||||
|
)]
|
||||||
|
|
||||||
mod data_keeper;
|
mod data_keeper;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod handler;
|
mod handler;
|
||||||
@ -29,6 +40,7 @@ pub use merger::MergeCtxType;
|
|||||||
pub use merger::MergeError;
|
pub use merger::MergeError;
|
||||||
pub use merger::MergerApResult;
|
pub use merger::MergerApResult;
|
||||||
pub use merger::MergerCallResult;
|
pub use merger::MergerCallResult;
|
||||||
|
pub use merger::MergerCanonResult;
|
||||||
pub use state_automata::SubgraphType;
|
pub use state_automata::SubgraphType;
|
||||||
|
|
||||||
pub type TraceHandlerResult<T> = std::result::Result<T, TraceHandlerError>;
|
pub type TraceHandlerResult<T> = std::result::Result<T, TraceHandlerError>;
|
||||||
|
@ -117,10 +117,8 @@ pub(crate) enum ValueType<'i> {
|
|||||||
|
|
||||||
impl<'i> ValueType<'i> {
|
impl<'i> ValueType<'i> {
|
||||||
pub(self) fn from_output_value(output_value: &'i CallOutputValue<'_>) -> Self {
|
pub(self) fn from_output_value(output_value: &'i CallOutputValue<'_>) -> Self {
|
||||||
use air_parser::ast::Variable;
|
|
||||||
|
|
||||||
match output_value {
|
match output_value {
|
||||||
CallOutputValue::Variable(Variable::Stream(stream)) => ValueType::Stream(stream.name),
|
CallOutputValue::Stream(stream) => ValueType::Stream(stream.name),
|
||||||
_ => ValueType::Scalar,
|
_ => ValueType::Scalar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
135
crates/air-lib/trace-handler/src/merger/canon_merger.rs
Normal file
135
crates/air-lib/trace-handler/src/merger/canon_merger.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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::*;
|
||||||
|
use crate::merger::errors::CanonResultError;
|
||||||
|
|
||||||
|
use bimap::BiHashMap;
|
||||||
|
|
||||||
|
const EXPECTED_STATE_NAME: &str = "canon";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum MergerCanonResult {
|
||||||
|
/// There is no corresponding state in a trace for this call.
|
||||||
|
Empty,
|
||||||
|
|
||||||
|
/// There was a state in at least one of the contexts. If there were two states in
|
||||||
|
/// both contexts, they were successfully merged.
|
||||||
|
/// Positions correspond to a new data trace.
|
||||||
|
CanonResult { stream_elements_pos: Vec<TracePos> },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_merge_next_state_as_canon(data_keeper: &mut DataKeeper) -> MergeResult<MergerCanonResult> {
|
||||||
|
use ExecutedState::Canon;
|
||||||
|
|
||||||
|
let prev_state = data_keeper.prev_slider_mut().next_state();
|
||||||
|
let current_state = data_keeper.current_slider_mut().next_state();
|
||||||
|
|
||||||
|
match (prev_state, current_state) {
|
||||||
|
(Some(Canon(prev_canon)), Some(Canon(current_canon))) => {
|
||||||
|
prepare_both_canon_result(&prev_canon, ¤t_canon, data_keeper)
|
||||||
|
}
|
||||||
|
(Some(Canon(prev_canon)), None) => prepare_single_canon_result(&prev_canon, &data_keeper.new_to_prev_pos),
|
||||||
|
(None, Some(Canon(current_canon))) => {
|
||||||
|
prepare_single_canon_result(¤t_canon, &data_keeper.new_to_current_pos)
|
||||||
|
}
|
||||||
|
(None, None) => Ok(MergerCanonResult::Empty),
|
||||||
|
(prev_state, current_state) => Err(MergeError::incompatible_states(
|
||||||
|
prev_state,
|
||||||
|
current_state,
|
||||||
|
EXPECTED_STATE_NAME,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_both_canon_result(
|
||||||
|
prev_canon_result: &CanonResult,
|
||||||
|
current_canon_result: &CanonResult,
|
||||||
|
data_keeper: &DataKeeper,
|
||||||
|
) -> MergeResult<MergerCanonResult> {
|
||||||
|
check_canon_results(prev_canon_result, current_canon_result, data_keeper)
|
||||||
|
.map_err(MergeError::IncorrectCanonResult)?;
|
||||||
|
prepare_single_canon_result(prev_canon_result, &data_keeper.new_to_prev_pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_single_canon_result(
|
||||||
|
canon_result: &CanonResult,
|
||||||
|
new_to_other_pos: &BiHashMap<TracePos, TracePos>,
|
||||||
|
) -> MergeResult<MergerCanonResult> {
|
||||||
|
let new_positions = canon_result
|
||||||
|
.stream_elements_pos
|
||||||
|
.iter()
|
||||||
|
.map(|pos| {
|
||||||
|
new_to_other_pos
|
||||||
|
.get_by_right(pos)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| CanonResultError::not_met_position(canon_result.clone(), *pos))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
Ok(MergerCanonResult::CanonResult {
|
||||||
|
stream_elements_pos: new_positions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_canon_results(
|
||||||
|
prev_canon_result: &CanonResult,
|
||||||
|
current_canon_result: &CanonResult,
|
||||||
|
data_keeper: &DataKeeper,
|
||||||
|
) -> Result<(), CanonResultError> {
|
||||||
|
if prev_canon_result.stream_elements_pos.len() != current_canon_result.stream_elements_pos.len() {
|
||||||
|
return Err(CanonResultError::different_lens(
|
||||||
|
prev_canon_result.clone(),
|
||||||
|
current_canon_result.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev_slider = data_keeper.prev_slider();
|
||||||
|
let current_slider = data_keeper.current_slider();
|
||||||
|
for (position, (prev_idx, current_idx)) in prev_canon_result
|
||||||
|
.stream_elements_pos
|
||||||
|
.iter()
|
||||||
|
.zip(current_canon_result.stream_elements_pos.iter())
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let prev_state = prev_slider.state_at_position(*prev_idx);
|
||||||
|
let current_state = current_slider.state_at_position(*current_idx);
|
||||||
|
|
||||||
|
match (prev_state, current_state) {
|
||||||
|
(Some(ExecutedState::Call(prev_call_result)), Some(ExecutedState::Call(current_call_result)))
|
||||||
|
if prev_call_result == current_call_result =>
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(Some(ExecutedState::Ap(prev_ap_result)), Some(ExecutedState::Ap(current_ap_result)))
|
||||||
|
if prev_ap_result == current_ap_result =>
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(CanonResultError::incompatible_state(
|
||||||
|
prev_canon_result.clone(),
|
||||||
|
current_canon_result.clone(),
|
||||||
|
prev_state.cloned(),
|
||||||
|
current_state.cloned(),
|
||||||
|
position,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -22,6 +22,7 @@ use super::FoldResult;
|
|||||||
use super::KeeperError;
|
use super::KeeperError;
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
|
use air_interpreter_data::CanonResult;
|
||||||
use air_interpreter_data::TracePos;
|
use air_interpreter_data::TracePos;
|
||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
||||||
@ -46,6 +47,9 @@ pub enum MergeError {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IncorrectCallResult(#[from] CallResultError),
|
IncorrectCallResult(#[from] CallResultError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
IncorrectCanonResult(#[from] CanonResultError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IncorrectFoldResult(#[from] FoldResultError),
|
IncorrectFoldResult(#[from] FoldResultError),
|
||||||
}
|
}
|
||||||
@ -73,6 +77,30 @@ pub enum CallResultError {
|
|||||||
DataNotMatchAIR { air_type: String, data_value: Value },
|
DataNotMatchAIR { air_type: String, data_value: Value },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(ThisError, Debug)]
|
||||||
|
pub enum CanonResultError {
|
||||||
|
#[error("canon results have different length: {prev_canon_result:?} != {current_canon_result:?}")]
|
||||||
|
LensNotEqual {
|
||||||
|
prev_canon_result: CanonResult,
|
||||||
|
current_canon_result: CanonResult,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("canon results {prev_canon_result:?} {current_canon_result:?} at position {position} points to incompatible execution states: {prev_state:?} {current_state:?}")]
|
||||||
|
IncompatibleState {
|
||||||
|
prev_canon_result: CanonResult,
|
||||||
|
current_canon_result: CanonResult,
|
||||||
|
prev_state: Option<ExecutedState>,
|
||||||
|
current_state: Option<ExecutedState>,
|
||||||
|
position: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("position {position} from canon result {canon_result:?} hasn't been met yet")]
|
||||||
|
NotMetPosition {
|
||||||
|
canon_result: CanonResult,
|
||||||
|
position: TracePos,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(ThisError, Debug)]
|
#[derive(ThisError, Debug)]
|
||||||
pub enum FoldResultError {
|
pub enum FoldResultError {
|
||||||
#[error("the first {count} subtrace descriptors lens of fold {fold_result:?} overflows")]
|
#[error("the first {count} subtrace descriptors lens of fold {fold_result:?} overflows")]
|
||||||
@ -138,6 +166,35 @@ impl CallResultError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CanonResultError {
|
||||||
|
pub(crate) fn different_lens(prev_canon_result: CanonResult, current_canon_result: CanonResult) -> Self {
|
||||||
|
Self::LensNotEqual {
|
||||||
|
prev_canon_result,
|
||||||
|
current_canon_result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn incompatible_state(
|
||||||
|
prev_canon_result: CanonResult,
|
||||||
|
current_canon_result: CanonResult,
|
||||||
|
prev_state: Option<ExecutedState>,
|
||||||
|
current_state: Option<ExecutedState>,
|
||||||
|
position: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self::IncompatibleState {
|
||||||
|
prev_canon_result,
|
||||||
|
current_canon_result,
|
||||||
|
prev_state,
|
||||||
|
current_state,
|
||||||
|
position,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn not_met_position(canon_result: CanonResult, position: TracePos) -> Self {
|
||||||
|
Self::NotMetPosition { canon_result, position }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum DataType {
|
pub enum DataType {
|
||||||
Previous,
|
Previous,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
mod ap_merger;
|
mod ap_merger;
|
||||||
mod call_merger;
|
mod call_merger;
|
||||||
|
mod canon_merger;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod fold_merger;
|
mod fold_merger;
|
||||||
mod par_merger;
|
mod par_merger;
|
||||||
@ -23,6 +24,7 @@ mod position_mapping;
|
|||||||
|
|
||||||
pub use ap_merger::MergerApResult;
|
pub use ap_merger::MergerApResult;
|
||||||
pub use call_merger::MergerCallResult;
|
pub use call_merger::MergerCallResult;
|
||||||
|
pub use canon_merger::MergerCanonResult;
|
||||||
pub use fold_merger::MergerFoldResult;
|
pub use fold_merger::MergerFoldResult;
|
||||||
pub use par_merger::MergerParResult;
|
pub use par_merger::MergerParResult;
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ pub use fold_merger::ResolvedSubTraceDescs;
|
|||||||
|
|
||||||
pub(super) use ap_merger::try_merge_next_state_as_ap;
|
pub(super) use ap_merger::try_merge_next_state_as_ap;
|
||||||
pub(super) use call_merger::try_merge_next_state_as_call;
|
pub(super) use call_merger::try_merge_next_state_as_call;
|
||||||
|
pub(super) use canon_merger::try_merge_next_state_as_canon;
|
||||||
pub(crate) use fold_merger::try_merge_next_state_as_fold;
|
pub(crate) use fold_merger::try_merge_next_state_as_fold;
|
||||||
pub(crate) use par_merger::try_merge_next_state_as_par;
|
pub(crate) use par_merger::try_merge_next_state_as_par;
|
||||||
|
|
||||||
@ -44,7 +47,6 @@ use position_mapping::PreparationScheme;
|
|||||||
|
|
||||||
type MergeResult<T> = std::result::Result<T, MergeError>;
|
type MergeResult<T> = std::result::Result<T, MergeError>;
|
||||||
|
|
||||||
use super::data_keeper::DataPositions;
|
|
||||||
use super::data_keeper::KeeperError;
|
use super::data_keeper::KeeperError;
|
||||||
use super::DataKeeper;
|
use super::DataKeeper;
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use super::DataKeeper;
|
use super::DataKeeper;
|
||||||
use super::DataPositions;
|
|
||||||
|
|
||||||
pub(super) enum PreparationScheme {
|
pub(super) enum PreparationScheme {
|
||||||
Previous,
|
Previous,
|
||||||
@ -27,19 +26,23 @@ pub(super) enum PreparationScheme {
|
|||||||
pub(super) fn prepare_positions_mapping(scheme: PreparationScheme, data_keeper: &mut DataKeeper) {
|
pub(super) fn prepare_positions_mapping(scheme: PreparationScheme, data_keeper: &mut DataKeeper) {
|
||||||
use PreparationScheme::*;
|
use PreparationScheme::*;
|
||||||
|
|
||||||
// it's safe to sub 1 from positions iff scheme was set correctly
|
let new_pos = data_keeper.result_trace_next_pos();
|
||||||
let prev_pos = match scheme {
|
|
||||||
Previous | Both => Some(data_keeper.prev_slider().position() - 1),
|
// it's safe to sub 1 from positions here iff scheme was set correctly
|
||||||
Current => None,
|
match scheme {
|
||||||
|
Previous => {
|
||||||
|
let prev_pos = data_keeper.prev_slider().position() - 1;
|
||||||
|
data_keeper.new_to_prev_pos.insert(new_pos, prev_pos);
|
||||||
|
}
|
||||||
|
Current => {
|
||||||
|
let current_pos = data_keeper.current_slider().position() - 1;
|
||||||
|
data_keeper.new_to_current_pos.insert(new_pos, current_pos);
|
||||||
|
}
|
||||||
|
Both => {
|
||||||
|
let prev_pos = data_keeper.prev_slider().position() - 1;
|
||||||
|
let current_pos = data_keeper.current_slider().position() - 1;
|
||||||
|
data_keeper.new_to_prev_pos.insert(new_pos, prev_pos);
|
||||||
|
data_keeper.new_to_current_pos.insert(new_pos, current_pos);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_pos = match scheme {
|
|
||||||
Current | Both => Some(data_keeper.current_slider().position() - 1),
|
|
||||||
Previous => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let data_positions = DataPositions { prev_pos, current_pos };
|
|
||||||
|
|
||||||
let trace_pos = data_keeper.result_trace_next_pos();
|
|
||||||
data_keeper.new_to_old_pos.insert(trace_pos, data_positions);
|
|
||||||
}
|
}
|
||||||
|
@ -66,13 +66,11 @@ impl FoldFSM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn meet_iteration_start(&mut self, value_pos: TracePos, data_keeper: &mut DataKeeper) -> FSMResult<()> {
|
pub(crate) fn meet_iteration_start(&mut self, value_pos: TracePos, data_keeper: &mut DataKeeper) -> FSMResult<()> {
|
||||||
let (prev_pos, current_pos) = match data_keeper.new_to_old_pos.get(&value_pos) {
|
let prev_pos = data_keeper.new_to_prev_pos.get_by_left(&value_pos);
|
||||||
Some(DataPositions { prev_pos, current_pos }) => (prev_pos, current_pos),
|
let current_pos = data_keeper.new_to_current_pos.get_by_left(&value_pos);
|
||||||
None => return self.prepare(None, None, value_pos, data_keeper),
|
|
||||||
};
|
|
||||||
|
|
||||||
let prev_lore = prev_pos.map(|pos| self.prev_fold.lore.remove(&pos)).flatten();
|
let prev_lore = prev_pos.and_then(|pos| self.prev_fold.lore.remove(pos));
|
||||||
let current_lore = current_pos.map(|pos| self.current_fold.lore.remove(&pos)).flatten();
|
let current_lore = current_pos.and_then(|pos| self.current_fold.lore.remove(pos));
|
||||||
|
|
||||||
self.prepare(prev_lore, current_lore, value_pos, data_keeper)
|
self.prepare(prev_lore, current_lore, value_pos, data_keeper)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ pub(super) use fold_fsm::FoldFSM;
|
|||||||
pub(super) use fsm_queue::FSMKeeper;
|
pub(super) use fsm_queue::FSMKeeper;
|
||||||
pub(super) use par_fsm::ParFSM;
|
pub(super) use par_fsm::ParFSM;
|
||||||
|
|
||||||
use super::data_keeper::DataPositions;
|
|
||||||
use super::data_keeper::KeeperError;
|
use super::data_keeper::KeeperError;
|
||||||
use super::merger::MergerParResult;
|
use super::merger::MergerParResult;
|
||||||
use super::DataKeeper;
|
use super::DataKeeper;
|
||||||
|
@ -135,6 +135,7 @@ impl<W: io::Write> Beautifier<W> {
|
|||||||
match node {
|
match node {
|
||||||
ast::Instruction::Call(call) => self.beautify_call(call, indent),
|
ast::Instruction::Call(call) => self.beautify_call(call, indent),
|
||||||
ast::Instruction::Ap(ap) => self.beautify_simple(ap, indent),
|
ast::Instruction::Ap(ap) => self.beautify_simple(ap, indent),
|
||||||
|
ast::Instruction::Canon(canon) => self.beautify_simple(canon, indent),
|
||||||
ast::Instruction::Seq(seq) => self.beautify_seq(seq, indent),
|
ast::Instruction::Seq(seq) => self.beautify_seq(seq, indent),
|
||||||
ast::Instruction::Par(par) => self.beautify_par(par, indent),
|
ast::Instruction::Par(par) => self.beautify_par(par, indent),
|
||||||
ast::Instruction::Xor(xor) => self.beautify_xor(xor, indent),
|
ast::Instruction::Xor(xor) => self.beautify_xor(xor, indent),
|
||||||
@ -157,7 +158,8 @@ impl<W: io::Write> Beautifier<W> {
|
|||||||
fn beautify_call(&mut self, call: &ast::Call, indent: usize) -> io::Result<()> {
|
fn beautify_call(&mut self, call: &ast::Call, indent: usize) -> io::Result<()> {
|
||||||
fmt_indent(&mut self.output, indent)?;
|
fmt_indent(&mut self.output, indent)?;
|
||||||
match &call.output {
|
match &call.output {
|
||||||
ast::CallOutputValue::Variable(v) => write!(&mut self.output, "{} <- ", v)?,
|
ast::CallOutputValue::Scalar(v) => write!(&mut self.output, "{} <- ", v)?,
|
||||||
|
ast::CallOutputValue::Stream(v) => write!(&mut self.output, "{} <- ", v)?,
|
||||||
ast::CallOutputValue::None => {}
|
ast::CallOutputValue::None => {}
|
||||||
}
|
}
|
||||||
writeln!(
|
writeln!(
|
||||||
|
Loading…
Reference in New Issue
Block a user