mirror of
https://github.com/fluencelabs/aquavm
synced 2025-05-28 15:11:22 +00:00
Support scalars in lambda (#192)
This commit is contained in:
parent
9f91d3161f
commit
1d8182d497
@ -1,3 +1,11 @@
|
|||||||
|
## Version 0.18.0 (2021-12-14)
|
||||||
|
|
||||||
|
[PR 192](https://github.com/fluencelabs/aquavm/pull/172):
|
||||||
|
Added a possibility to use scalars in lambdas.
|
||||||
|
|
||||||
|
[PR 190](https://github.com/fluencelabs/aquavm/pull/190), [PR 186](https://github.com/fluencelabs/aquavm/pull/186), [PR 185](https://github.com/fluencelabs/aquavm/pull/185), [PR 182](https://github.com/fluencelabs/aquavm/pull/182), [PR 181](https://github.com/fluencelabs/aquavm/pull/181):
|
||||||
|
Bug fixing.
|
||||||
|
|
||||||
## Version 0.17.0 (2021-11-24)
|
## Version 0.17.0 (2021-11-24)
|
||||||
|
|
||||||
[PR 172](https://github.com/fluencelabs/aquavm/pull/172):
|
[PR 172](https://github.com/fluencelabs/aquavm/pull/172):
|
||||||
|
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -13,7 +13,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "air"
|
name = "air"
|
||||||
version = "0.17.0"
|
version = "0.18.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"air-execution-info-collector",
|
"air-execution-info-collector",
|
||||||
"air-interpreter-data",
|
"air-interpreter-data",
|
||||||
@ -50,7 +50,7 @@ version = "0.1.0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "air-interpreter"
|
name = "air-interpreter"
|
||||||
version = "0.17.0"
|
version = "0.18.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"air",
|
"air",
|
||||||
"air-log-targets",
|
"air-log-targets",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "air-interpreter"
|
name = "air-interpreter"
|
||||||
version = "0.17.0"
|
version = "0.18.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.17.0"
|
version = "0.18.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"
|
||||||
|
33
air/src/execution_step/air/fail.rs
Normal file
33
air/src/execution_step/air/fail.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use super::ExecutionCtx;
|
||||||
|
use super::ExecutionResult;
|
||||||
|
use super::ExecutionError;
|
||||||
|
use super::TraceHandler;
|
||||||
|
use crate::log_instruction;
|
||||||
|
|
||||||
|
use air_parser::ast::Null;
|
||||||
|
|
||||||
|
impl<'i> super::ExecutableInstruction<'i> for Fail {
|
||||||
|
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||||
|
log_instruction!(null, exec_ctx, trace_ctx);
|
||||||
|
|
||||||
|
Err(ExecutionError::FailMet {
|
||||||
|
message: self.message.to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -127,12 +127,12 @@ fn create_scalar_lambda_iterable<'ctx>(
|
|||||||
|
|
||||||
match exec_ctx.scalars.get(scalar_name)? {
|
match exec_ctx.scalars.get(scalar_name)? {
|
||||||
ScalarRef::Value(variable) => {
|
ScalarRef::Value(variable) => {
|
||||||
let jvalues = select(&variable.result, lambda.iter())?;
|
let jvalues = select(&variable.result, lambda.iter(), exec_ctx)?;
|
||||||
from_jvalue(jvalues, variable.tetraplet.clone(), lambda)
|
from_jvalue(jvalues, variable.tetraplet.clone(), lambda)
|
||||||
}
|
}
|
||||||
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 jvalue = iterable_value.apply_lambda(lambda)?;
|
let jvalue = iterable_value.apply_lambda(lambda, exec_ctx)?;
|
||||||
let tetraplet = as_tetraplet(&iterable_value);
|
let tetraplet = as_tetraplet(&iterable_value);
|
||||||
|
|
||||||
from_jvalue(jvalue, tetraplet, lambda)
|
from_jvalue(jvalue, tetraplet, lambda)
|
||||||
|
@ -25,6 +25,7 @@ use super::ExecutionError;
|
|||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::ValueAggregate;
|
use super::ValueAggregate;
|
||||||
use crate::execution_step::lambda_applier::*;
|
use crate::execution_step::lambda_applier::*;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
@ -37,10 +38,14 @@ use std::borrow::Cow;
|
|||||||
/// Represent a value that could be transform to a JValue with or without tetraplets.
|
/// Represent a value that could be transform to a JValue with or without tetraplets.
|
||||||
pub(crate) trait JValuable {
|
pub(crate) trait JValuable {
|
||||||
/// Applies lambda to the internal value, produces JValue.
|
/// Applies lambda to the internal value, produces JValue.
|
||||||
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<&JValue>;
|
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue>;
|
||||||
|
|
||||||
/// Applies lambda to the internal value, produces JValue with tetraplet.
|
/// Applies lambda to the internal value, produces JValue with tetraplet.
|
||||||
fn apply_lambda_with_tetraplets(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<(&JValue, RSecurityTetraplet)>;
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
|
&self,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)>;
|
||||||
|
|
||||||
/// Return internal value as borrowed if it's possible, owned otherwise.
|
/// Return internal value as borrowed if it's possible, owned otherwise.
|
||||||
fn as_jvalue(&self) -> Cow<'_, JValue>;
|
fn as_jvalue(&self) -> Cow<'_, JValue>;
|
||||||
|
@ -18,6 +18,7 @@ use super::select_from_stream;
|
|||||||
use super::ExecutionResult;
|
use super::ExecutionResult;
|
||||||
use super::JValuable;
|
use super::JValuable;
|
||||||
use super::ValueAggregate;
|
use super::ValueAggregate;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
@ -29,15 +30,19 @@ use std::borrow::Cow;
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
impl JValuable for std::cell::Ref<'_, Vec<ValueAggregate>> {
|
impl JValuable for std::cell::Ref<'_, Vec<ValueAggregate>> {
|
||||||
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<&JValue> {
|
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
let stream_iter = self.iter().map(|r| r.result.deref());
|
let stream_iter = self.iter().map(|r| r.result.deref());
|
||||||
let select_result = select_from_stream(stream_iter, lambda)?;
|
let select_result = select_from_stream(stream_iter, lambda, exec_ctx)?;
|
||||||
Ok(select_result.result)
|
Ok(select_result.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_lambda_with_tetraplets(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
|
&self,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||||
let stream_iter = self.iter().map(|r| r.result.deref());
|
let stream_iter = self.iter().map(|r| r.result.deref());
|
||||||
let select_result = select_from_stream(stream_iter, lambda)?;
|
let select_result = select_from_stream(stream_iter, lambda, exec_ctx)?;
|
||||||
|
|
||||||
let tetraplet = self[select_result.tetraplet_idx].tetraplet.clone();
|
let tetraplet = self[select_result.tetraplet_idx].tetraplet.clone();
|
||||||
tetraplet.borrow_mut().add_lambda(&format_ast(lambda));
|
tetraplet.borrow_mut().add_lambda(&format_ast(lambda));
|
||||||
|
@ -19,6 +19,7 @@ use super::ExecutionResult;
|
|||||||
use super::JValuable;
|
use super::JValuable;
|
||||||
use super::LambdaAST;
|
use super::LambdaAST;
|
||||||
use crate::exec_err;
|
use crate::exec_err;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
@ -26,12 +27,16 @@ use crate::JValue;
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
impl JValuable for () {
|
impl JValuable for () {
|
||||||
fn apply_lambda(&self, _lambda: &LambdaAST<'_>) -> ExecutionResult<&JValue> {
|
fn apply_lambda<'i>(&self, _lambda: &LambdaAST<'_>, _exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
// applying lambda to an empty stream will produce a join behaviour
|
// applying lambda to an empty stream will produce a join behaviour
|
||||||
exec_err!(ExecutionError::EmptyStreamLambdaError)
|
exec_err!(ExecutionError::EmptyStreamLambdaError)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_lambda_with_tetraplets(&self, _lambda: &LambdaAST<'_>) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
|
&self,
|
||||||
|
_lambda: &LambdaAST<'_>,
|
||||||
|
_exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||||
// applying lambda to an empty stream will produce a join behaviour
|
// applying lambda to an empty stream will produce a join behaviour
|
||||||
exec_err!(ExecutionError::EmptyStreamLambdaError)
|
exec_err!(ExecutionError::EmptyStreamLambdaError)
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ use super::ExecutionResult;
|
|||||||
use super::IterableItem;
|
use super::IterableItem;
|
||||||
use super::JValuable;
|
use super::JValuable;
|
||||||
use super::LambdaAST;
|
use super::LambdaAST;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
@ -27,7 +28,7 @@ use std::borrow::Cow;
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
impl<'ctx> JValuable for IterableItem<'ctx> {
|
impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||||
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<&JValue> {
|
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
use super::IterableItem::*;
|
use super::IterableItem::*;
|
||||||
|
|
||||||
let jvalue = match self {
|
let jvalue = match self {
|
||||||
@ -36,11 +37,15 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
|||||||
RcValue((jvalue, ..)) => jvalue.deref(),
|
RcValue((jvalue, ..)) => jvalue.deref(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let selected_value = select(jvalue, lambda.iter())?;
|
let selected_value = select(jvalue, lambda.iter(), exec_ctx)?;
|
||||||
Ok(selected_value)
|
Ok(selected_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_lambda_with_tetraplets(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
|
&self,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||||
use super::IterableItem::*;
|
use super::IterableItem::*;
|
||||||
|
|
||||||
let (jvalue, tetraplet) = match self {
|
let (jvalue, tetraplet) = match self {
|
||||||
@ -49,7 +54,7 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
|||||||
RcValue((jvalue, tetraplet, _)) => (jvalue.deref(), tetraplet),
|
RcValue((jvalue, tetraplet, _)) => (jvalue.deref(), tetraplet),
|
||||||
};
|
};
|
||||||
|
|
||||||
let selected_value = select(jvalue, lambda.iter())?;
|
let selected_value = select(jvalue, lambda.iter(), exec_ctx)?;
|
||||||
Ok((selected_value, tetraplet.clone()))
|
Ok((selected_value, tetraplet.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ use super::ExecutionResult;
|
|||||||
use super::JValuable;
|
use super::JValuable;
|
||||||
use super::LambdaAST;
|
use super::LambdaAST;
|
||||||
use super::ValueAggregate;
|
use super::ValueAggregate;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
@ -29,13 +30,17 @@ use std::borrow::Cow;
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
impl JValuable for ValueAggregate {
|
impl JValuable for ValueAggregate {
|
||||||
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<&JValue> {
|
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
let selected_value = select(&self.result, lambda.iter())?;
|
let selected_value = select(&self.result, lambda.iter(), exec_ctx)?;
|
||||||
Ok(selected_value)
|
Ok(selected_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_lambda_with_tetraplets(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
let selected_value = select(&self.result, lambda.iter())?;
|
&self,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||||
|
let selected_value = select(&self.result, lambda.iter(), exec_ctx)?;
|
||||||
let tetraplet = self.tetraplet.clone();
|
let tetraplet = self.tetraplet.clone();
|
||||||
tetraplet.borrow_mut().add_lambda(&format_ast(lambda));
|
tetraplet.borrow_mut().add_lambda(&format_ast(lambda));
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ use super::JValuable;
|
|||||||
use crate::exec_err;
|
use crate::exec_err;
|
||||||
use crate::execution_step::boxed_value::Generation;
|
use crate::execution_step::boxed_value::Generation;
|
||||||
use crate::execution_step::boxed_value::Stream;
|
use crate::execution_step::boxed_value::Stream;
|
||||||
|
use crate::execution_step::ExecutionCtx;
|
||||||
use crate::execution_step::RSecurityTetraplet;
|
use crate::execution_step::RSecurityTetraplet;
|
||||||
use crate::execution_step::SecurityTetraplets;
|
use crate::execution_step::SecurityTetraplets;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
@ -39,16 +40,20 @@ pub(crate) struct StreamJvaluableIngredients<'stream> {
|
|||||||
// TODO: this will be deleted soon, because it would be impossible to use streams without
|
// TODO: this will be deleted soon, because it would be impossible to use streams without
|
||||||
// canonicalization as an arg of a call
|
// canonicalization as an arg of a call
|
||||||
impl JValuable for StreamJvaluableIngredients<'_> {
|
impl JValuable for StreamJvaluableIngredients<'_> {
|
||||||
fn apply_lambda(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<&JValue> {
|
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||||
let iter = self.iter()?.map(|v| v.result.deref());
|
let iter = self.iter()?.map(|v| v.result.deref());
|
||||||
let select_result = select_from_stream(iter, lambda)?;
|
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||||
|
|
||||||
Ok(select_result.result)
|
Ok(select_result.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_lambda_with_tetraplets(&self, lambda: &LambdaAST<'_>) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
fn apply_lambda_with_tetraplets<'i>(
|
||||||
|
&self,
|
||||||
|
lambda: &LambdaAST<'_>,
|
||||||
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<(&JValue, RSecurityTetraplet)> {
|
||||||
let iter = self.iter()?.map(|v| v.result.deref());
|
let iter = self.iter()?.map(|v| v.result.deref());
|
||||||
let select_result = select_from_stream(iter, lambda)?;
|
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
|
// unwrap is safe here because each value has a tetraplet and a lambda always returns a valid index
|
||||||
let resolved_call = self.iter()?.nth(select_result.tetraplet_idx).unwrap();
|
let resolved_call = self.iter()?.nth(select_result.tetraplet_idx).unwrap();
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
use super::utils::*;
|
use super::utils::*;
|
||||||
use super::LambdaError;
|
use super::LambdaError;
|
||||||
use super::LambdaResult;
|
use crate::execution_step::ExecutionCtx;
|
||||||
|
use crate::execution_step::ExecutionResult;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
use crate::LambdaAST;
|
use crate::LambdaAST;
|
||||||
|
|
||||||
@ -27,19 +28,21 @@ pub(crate) struct StreamSelectResult<'value> {
|
|||||||
pub(crate) tetraplet_idx: usize,
|
pub(crate) tetraplet_idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn select_from_stream<'value>(
|
pub(crate) fn select_from_stream<'value, 'i>(
|
||||||
stream: impl ExactSizeIterator<Item = &'value JValue> + 'value,
|
stream: impl ExactSizeIterator<Item = &'value JValue> + 'value,
|
||||||
lambda: &LambdaAST<'_>,
|
lambda: &LambdaAST<'_>,
|
||||||
) -> LambdaResult<StreamSelectResult<'value>> {
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
|
) -> ExecutionResult<StreamSelectResult<'value>> {
|
||||||
use ValueAccessor::*;
|
use ValueAccessor::*;
|
||||||
|
|
||||||
let (prefix, body) = lambda.split_first();
|
let (prefix, body) = lambda.split_first();
|
||||||
let idx = match prefix {
|
let idx = match prefix {
|
||||||
ArrayAccess { idx } => *idx,
|
ArrayAccess { idx } => *idx,
|
||||||
FieldAccess { field_name } => {
|
FieldAccessByName { field_name } => {
|
||||||
return Err(LambdaError::FieldAccessorAppliedToStream {
|
return Err(LambdaError::FieldAccessorAppliedToStream {
|
||||||
field_name: field_name.to_string(),
|
field_name: field_name.to_string(),
|
||||||
})
|
}
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
_ => unreachable!("should not execute if parsing succeeded. QED."),
|
_ => unreachable!("should not execute if parsing succeeded. QED."),
|
||||||
};
|
};
|
||||||
@ -50,23 +53,28 @@ pub(crate) fn select_from_stream<'value>(
|
|||||||
.nth(idx as usize)
|
.nth(idx as usize)
|
||||||
.ok_or(LambdaError::StreamNotHaveEnoughValues { stream_size, idx })?;
|
.ok_or(LambdaError::StreamNotHaveEnoughValues { stream_size, idx })?;
|
||||||
|
|
||||||
let result = select(value, body.iter())?;
|
let result = select(value, body.iter(), exec_ctx)?;
|
||||||
let select_result = StreamSelectResult::new(result, idx);
|
let select_result = StreamSelectResult::new(result, idx);
|
||||||
Ok(select_result)
|
Ok(select_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn select<'value, 'algebra>(
|
pub(crate) fn select<'value, 'accessor, 'i>(
|
||||||
mut value: &'value JValue,
|
mut value: &'value JValue,
|
||||||
lambda: impl Iterator<Item = &'algebra ValueAccessor<'algebra>>,
|
lambda: impl Iterator<Item = &'accessor ValueAccessor<'accessor>>,
|
||||||
) -> LambdaResult<&'value JValue> {
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
for value_algebra in lambda {
|
) -> ExecutionResult<&'value JValue> {
|
||||||
match value_algebra {
|
for accessor in lambda {
|
||||||
|
match accessor {
|
||||||
ValueAccessor::ArrayAccess { idx } => {
|
ValueAccessor::ArrayAccess { idx } => {
|
||||||
value = try_jvalue_with_idx(value, *idx)?;
|
value = try_jvalue_with_idx(value, *idx)?;
|
||||||
}
|
}
|
||||||
ValueAccessor::FieldAccess { field_name } => {
|
ValueAccessor::FieldAccessByName { field_name } => {
|
||||||
value = try_jvalue_with_field_name(value, *field_name)?;
|
value = try_jvalue_with_field_name(value, *field_name)?;
|
||||||
}
|
}
|
||||||
|
ValueAccessor::FieldAccessByScalar { scalar_name } => {
|
||||||
|
let scalar = exec_ctx.scalars.get(scalar_name)?;
|
||||||
|
value = select_by_scalar(value, scalar)?;
|
||||||
|
}
|
||||||
ValueAccessor::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
ValueAccessor::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,24 @@ pub(crate) enum LambdaError {
|
|||||||
#[error("lambda is applied to a stream that have only '{stream_size}' elements, but '{idx}' requested")]
|
#[error("lambda is applied to a stream that have only '{stream_size}' elements, but '{idx}' requested")]
|
||||||
StreamNotHaveEnoughValues { stream_size: usize, idx: u32 },
|
StreamNotHaveEnoughValues { stream_size: usize, idx: u32 },
|
||||||
|
|
||||||
#[error("field algebra (with field name = '{field_name}') can't be applied to a stream")]
|
#[error("field accessor (with field name = '{field_name}') can't be applied to a stream")]
|
||||||
FieldAccessorAppliedToStream { field_name: String },
|
FieldAccessorAppliedToStream { field_name: String },
|
||||||
|
|
||||||
#[error("value '{value}' is not an array-type to match array algebra with idx = '{idx}'")]
|
#[error("value '{value}' is not an array-type to match array accessor with idx = '{idx}'")]
|
||||||
ArrayAccessorNotMatchValue { value: JValue, idx: u32 },
|
ArrayAccessorNotMatchValue { value: JValue, idx: u32 },
|
||||||
|
|
||||||
#[error("value '{value}' does not contain element for idx = '{idx}'")]
|
#[error("value '{value}' does not contain element for idx = '{idx}'")]
|
||||||
ValueNotContainSuchArrayIdx { value: JValue, idx: u32 },
|
ValueNotContainSuchArrayIdx { value: JValue, idx: u32 },
|
||||||
|
|
||||||
#[error("value '{value}' is not an map-type to match field algebra with field_name = '{field_name}'")]
|
#[error("value '{value}' is not an map-type to match field accessor with field_name = '{field_name}'")]
|
||||||
FieldAccessorNotMatchValue { value: JValue, field_name: String },
|
FieldAccessorNotMatchValue { value: JValue, field_name: String },
|
||||||
|
|
||||||
#[error("value '{value}' does not contain element with field name = '{field_name}'")]
|
#[error("value '{value}' does not contain element with field name = '{field_name}'")]
|
||||||
JValueNotContainSuchField { value: JValue, field_name: String },
|
JValueNotContainSuchField { value: JValue, field_name: String },
|
||||||
|
|
||||||
|
#[error("index accessor `{accessor} can't be converted to u32`")]
|
||||||
|
IndexAccessNotU32 { accessor: serde_json::Number },
|
||||||
|
|
||||||
|
#[error("scalar accessor `{scalar_accessor}` should has number or string type")]
|
||||||
|
ScalarAccessorHasInvalidType { scalar_accessor: JValue },
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
use super::LambdaError;
|
use super::LambdaError;
|
||||||
use super::LambdaResult;
|
use super::LambdaResult;
|
||||||
|
use crate::execution_step::ScalarRef;
|
||||||
use crate::JValue;
|
use crate::JValue;
|
||||||
|
|
||||||
pub(super) fn try_jvalue_with_idx(jvalue: &JValue, idx: u32) -> LambdaResult<&JValue> {
|
pub(super) fn try_jvalue_with_idx(jvalue: &JValue, idx: u32) -> LambdaResult<&JValue> {
|
||||||
@ -52,3 +53,43 @@ pub(super) fn try_jvalue_with_field_name<'value>(
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn select_by_scalar<'value, 'i>(
|
||||||
|
value: &'value JValue,
|
||||||
|
scalar_ref: ScalarRef<'i>,
|
||||||
|
) -> LambdaResult<&'value JValue> {
|
||||||
|
use ScalarRef::*;
|
||||||
|
|
||||||
|
match scalar_ref {
|
||||||
|
Value(lambda_value) => select_by_jvalue(value, &lambda_value.result),
|
||||||
|
IterableValue(fold_state) => {
|
||||||
|
// it's safe because iterable always point to valid value
|
||||||
|
let accessor = fold_state.iterable.peek().unwrap().into_resolved_result();
|
||||||
|
select_by_jvalue(value, &accessor.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_by_jvalue<'value>(value: &'value JValue, accessor: &JValue) -> LambdaResult<&'value JValue> {
|
||||||
|
match accessor {
|
||||||
|
JValue::String(string_accessor) => try_jvalue_with_field_name(value, string_accessor),
|
||||||
|
JValue::Number(number_accessor) => {
|
||||||
|
let idx = try_number_to_u32(number_accessor)?;
|
||||||
|
try_jvalue_with_idx(value, idx)
|
||||||
|
}
|
||||||
|
scalar_accessor => Err(LambdaError::ScalarAccessorHasInvalidType {
|
||||||
|
scalar_accessor: scalar_accessor.clone(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_number_to_u32(accessor: &serde_json::Number) -> LambdaResult<u32> {
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
accessor
|
||||||
|
.as_u64()
|
||||||
|
.and_then(|v| u32::try_from(v).ok())
|
||||||
|
.ok_or(LambdaError::IndexAccessNotU32 {
|
||||||
|
accessor: accessor.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -128,7 +128,7 @@ pub(crate) fn apply_lambda<'i>(
|
|||||||
exec_ctx: &ExecutionCtx<'i>,
|
exec_ctx: &ExecutionCtx<'i>,
|
||||||
) -> ExecutionResult<(JValue, RSecurityTetraplet)> {
|
) -> ExecutionResult<(JValue, RSecurityTetraplet)> {
|
||||||
let resolved = resolve_variable(variable, exec_ctx)?;
|
let resolved = resolve_variable(variable, exec_ctx)?;
|
||||||
let (jvalue, tetraplet) = resolved.apply_lambda_with_tetraplets(lambda)?;
|
let (jvalue, tetraplet) = resolved.apply_lambda_with_tetraplets(lambda, exec_ctx)?;
|
||||||
|
|
||||||
// it's known that apply_lambda_with_tetraplets returns vec of one value
|
// it's known that apply_lambda_with_tetraplets returns vec of one value
|
||||||
Ok((jvalue.clone(), tetraplet))
|
Ok((jvalue.clone(), tetraplet))
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Fluence Labs Limited
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use air_test_utils::prelude::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn json_path_not_allowed_for_non_objects_and_arrays() {
|
|
||||||
let set_variable_peer_id = "set_variable";
|
|
||||||
let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id);
|
|
||||||
|
|
||||||
let local_peer_id = "local_peer_id";
|
|
||||||
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
|
||||||
|
|
||||||
let script = format!(
|
|
||||||
r#"
|
|
||||||
(seq
|
|
||||||
(call "{0}" ("" "") ["some_string"] string_variable)
|
|
||||||
(call "{1}" ("" "") [string_variable.$.some_json_path])
|
|
||||||
)
|
|
||||||
"#,
|
|
||||||
set_variable_peer_id, local_peer_id
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
|
||||||
let result = call_vm!(local_vm, "asd", script, "", result.data);
|
|
||||||
|
|
||||||
assert_eq!(result.ret_code, 1003);
|
|
||||||
}
|
|
140
air/tests/test_module/integration/lambda.rs
Normal file
140
air/tests/test_module/integration/lambda.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Fluence Labs Limited
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use air_test_utils::prelude::*;
|
||||||
|
|
||||||
|
use fstrings::f;
|
||||||
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_not_allowed_for_non_objects_and_arrays() {
|
||||||
|
let set_variable_peer_id = "set_variable";
|
||||||
|
let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = format!(
|
||||||
|
r#"
|
||||||
|
(seq
|
||||||
|
(call "{0}" ("" "") ["some_string"] string_variable)
|
||||||
|
(call "{1}" ("" "") [string_variable.$.some_lambda])
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
set_variable_peer_id, local_peer_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||||
|
let result = call_vm!(local_vm, "asd", script, "", result.data);
|
||||||
|
|
||||||
|
assert_eq!(result.ret_code, 1003);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_with_string_scalar() {
|
||||||
|
let set_variable_peer_id = "set_variable";
|
||||||
|
let variables = maplit::hashmap! {
|
||||||
|
"string_accessor".to_string() => json!("some_field_name"),
|
||||||
|
"value".to_string() => json!({"other_name_1": 0, "some_field_name": 1, "other_name_2": 0})
|
||||||
|
};
|
||||||
|
let mut set_variable_vm = create_avm(
|
||||||
|
set_variables_call_service(variables, VariableOptionSource::FunctionName),
|
||||||
|
set_variable_peer_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{set_variable_peer_id}" ("" "string_accessor") [] string_accessor)
|
||||||
|
(call "{set_variable_peer_id}" ("" "value") [] value)
|
||||||
|
)
|
||||||
|
(call "{local_peer_id}" ("" "") [value.$.[string_accessor]])
|
||||||
|
)
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||||
|
let result = checked_call_vm!(local_vm, "asd", script, "", result.data);
|
||||||
|
let trace = trace_from_result(&result);
|
||||||
|
|
||||||
|
assert_eq!(&trace[2], &executed_state::scalar_number(1u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_with_number_scalar() {
|
||||||
|
let set_variable_peer_id = "set_variable";
|
||||||
|
let variables = maplit::hashmap! {
|
||||||
|
"string_accessor".to_string() => json!(1u32),
|
||||||
|
"value".to_string() => json!([0, 1, 2])
|
||||||
|
};
|
||||||
|
let mut set_variable_vm = create_avm(
|
||||||
|
set_variables_call_service(variables, VariableOptionSource::FunctionName),
|
||||||
|
set_variable_peer_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(seq
|
||||||
|
(call "{set_variable_peer_id}" ("" "string_accessor") [] string_accessor)
|
||||||
|
(call "{set_variable_peer_id}" ("" "value") [] value)
|
||||||
|
)
|
||||||
|
(call "{local_peer_id}" ("" "") [value.$.[string_accessor]])
|
||||||
|
)
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||||
|
let result = checked_call_vm!(local_vm, "asd", script, "", result.data);
|
||||||
|
let trace = trace_from_result(&result);
|
||||||
|
|
||||||
|
assert_eq!(&trace[2], &executed_state::scalar_number(1u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_with_scalar_join() {
|
||||||
|
let set_variable_peer_id = "set_variable";
|
||||||
|
let variables = maplit::hashmap! {
|
||||||
|
"string_accessor".to_string() => json!("some_field_name"),
|
||||||
|
"value".to_string() => json!({"other_name_1": 0, "some_field_name": 1, "other_name_2": 0})
|
||||||
|
};
|
||||||
|
let mut set_variable_vm = create_avm(
|
||||||
|
set_variables_call_service(variables, VariableOptionSource::FunctionName),
|
||||||
|
set_variable_peer_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let local_peer_id = "local_peer_id";
|
||||||
|
let mut local_vm = create_avm(echo_call_service(), local_peer_id);
|
||||||
|
|
||||||
|
let script = f!(r#"
|
||||||
|
(seq
|
||||||
|
(par
|
||||||
|
(call "non_exist_peer_id" ("" "string_accessor") [] string_accessor)
|
||||||
|
(call "{set_variable_peer_id}" ("" "value") [] value)
|
||||||
|
)
|
||||||
|
(call "{local_peer_id}" ("" "") [value.$.[string_accessor]])
|
||||||
|
)
|
||||||
|
"#);
|
||||||
|
|
||||||
|
let result = checked_call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||||
|
let result = checked_call_vm!(local_vm, "asd", script, "", result.data);
|
||||||
|
let trace = trace_from_result(&result);
|
||||||
|
|
||||||
|
assert_eq!(&trace[3], &executed_state::request_sent_by("set_variable"));
|
||||||
|
}
|
@ -23,7 +23,7 @@ mod empty_array;
|
|||||||
mod flattening;
|
mod flattening;
|
||||||
mod join;
|
mod join;
|
||||||
mod join_behaviour;
|
mod join_behaviour;
|
||||||
mod json_path;
|
mod lambda;
|
||||||
mod last_error;
|
mod last_error;
|
||||||
mod network_explore;
|
mod network_explore;
|
||||||
mod security_tetraplets;
|
mod security_tetraplets;
|
||||||
|
@ -285,7 +285,7 @@ fn lambda() {
|
|||||||
name: "value",
|
name: "value",
|
||||||
lambda: unsafe {
|
lambda: unsafe {
|
||||||
LambdaAST::new_unchecked(vec![
|
LambdaAST::new_unchecked(vec![
|
||||||
ValueAccessor::FieldAccess {
|
ValueAccessor::FieldAccessByName {
|
||||||
field_name: "field",
|
field_name: "field",
|
||||||
},
|
},
|
||||||
ValueAccessor::ArrayAccess { idx: 1 },
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
|
@ -34,7 +34,7 @@ fn parse_json_path() {
|
|||||||
let expected = call(
|
let expected = call(
|
||||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
"peer_id",
|
"peer_id",
|
||||||
vec![ValueAccessor::FieldAccess { field_name: "a" }],
|
vec![ValueAccessor::FieldAccessByName { field_name: "a" }],
|
||||||
15,
|
15,
|
||||||
)),
|
)),
|
||||||
CallInstrValue::Literal("service_id"),
|
CallInstrValue::Literal("service_id"),
|
||||||
@ -194,7 +194,7 @@ fn parse_call_with_invalid_triplet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_json_path_complex() {
|
fn parse_lambda_complex() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
(seq
|
(seq
|
||||||
(call m.$.[1]! ("service_id" "function_name") [] void)
|
(call m.$.[1]! ("service_id" "function_name") [] void)
|
||||||
@ -218,12 +218,12 @@ fn parse_json_path_complex() {
|
|||||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
"m",
|
"m",
|
||||||
vec![
|
vec![
|
||||||
ValueAccessor::FieldAccess { field_name: "abc" },
|
ValueAccessor::FieldAccessByName { field_name: "abc" },
|
||||||
ValueAccessor::ArrayAccess { idx: 0 },
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
ValueAccessor::FieldAccessByName { field_name: "cde" },
|
||||||
ValueAccessor::ArrayAccess { idx: 1 },
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
ValueAccessor::ArrayAccess { idx: 0 },
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
ValueAccessor::FieldAccessByName { field_name: "cde" },
|
||||||
ValueAccessor::ArrayAccess { idx: 1 },
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
],
|
],
|
||||||
99,
|
99,
|
||||||
@ -237,6 +237,64 @@ fn parse_json_path_complex() {
|
|||||||
assert_eq!(instruction, expected);
|
assert_eq!(instruction, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_lambda_with_scalars_complex() {
|
||||||
|
let source_code = r#"
|
||||||
|
(seq
|
||||||
|
(call m.$.[1].[scalar_1].[scalar_2]! ("service_id" "function_name") [] void)
|
||||||
|
(call m.$.abc[0].[scalar_2].cde[1][0][scalar_3].cde[1]! ("service_id" "function_name") [] void)
|
||||||
|
)
|
||||||
|
"#;
|
||||||
|
let instruction = parse(source_code);
|
||||||
|
let expected = seq(
|
||||||
|
call(
|
||||||
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
|
"m",
|
||||||
|
vec![
|
||||||
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
|
ValueAccessor::FieldAccessByScalar {
|
||||||
|
scalar_name: "scalar_1",
|
||||||
|
},
|
||||||
|
ValueAccessor::FieldAccessByScalar {
|
||||||
|
scalar_name: "scalar_2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
32,
|
||||||
|
)),
|
||||||
|
CallInstrValue::Literal("service_id"),
|
||||||
|
CallInstrValue::Literal("function_name"),
|
||||||
|
Rc::new(vec![]),
|
||||||
|
CallOutputValue::Variable(Variable::scalar("void", 97)),
|
||||||
|
),
|
||||||
|
call(
|
||||||
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
|
"m",
|
||||||
|
vec![
|
||||||
|
ValueAccessor::FieldAccessByName { field_name: "abc" },
|
||||||
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
|
ValueAccessor::FieldAccessByScalar {
|
||||||
|
scalar_name: "scalar_2",
|
||||||
|
},
|
||||||
|
ValueAccessor::FieldAccessByName { field_name: "cde" },
|
||||||
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
|
ValueAccessor::FieldAccessByScalar {
|
||||||
|
scalar_name: "scalar_3",
|
||||||
|
},
|
||||||
|
ValueAccessor::FieldAccessByName { field_name: "cde" },
|
||||||
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
|
],
|
||||||
|
121,
|
||||||
|
)),
|
||||||
|
CallInstrValue::Literal("service_id"),
|
||||||
|
CallInstrValue::Literal("function_name"),
|
||||||
|
Rc::new(vec![]),
|
||||||
|
CallOutputValue::Variable(Variable::scalar("void", 205)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
assert_eq!(instruction, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn json_path_square_braces() {
|
fn json_path_square_braces() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
@ -246,7 +304,7 @@ fn json_path_square_braces() {
|
|||||||
let expected = call(
|
let expected = call(
|
||||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
"u",
|
"u",
|
||||||
vec![ValueAccessor::FieldAccess {
|
vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "peer_id",
|
field_name: "peer_id",
|
||||||
}],
|
}],
|
||||||
15,
|
15,
|
||||||
@ -258,16 +316,16 @@ fn json_path_square_braces() {
|
|||||||
"u",
|
"u",
|
||||||
vec![
|
vec![
|
||||||
ValueAccessor::ArrayAccess { idx: 1 },
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
ValueAccessor::FieldAccessByName { field_name: "cde" },
|
||||||
ValueAccessor::ArrayAccess { idx: 0 },
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
ValueAccessor::ArrayAccess { idx: 0 },
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
ValueAccessor::FieldAccess { field_name: "abc" },
|
ValueAccessor::FieldAccessByName { field_name: "abc" },
|
||||||
],
|
],
|
||||||
43,
|
43,
|
||||||
)),
|
)),
|
||||||
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||||
"u",
|
"u",
|
||||||
vec![ValueAccessor::FieldAccess { field_name: "name" }],
|
vec![ValueAccessor::FieldAccessByName { field_name: "name" }],
|
||||||
64,
|
64,
|
||||||
)),
|
)),
|
||||||
]),
|
]),
|
||||||
|
@ -126,7 +126,7 @@ fn comments() {
|
|||||||
ScalarWithLambda::from_raw_lambda(
|
ScalarWithLambda::from_raw_lambda(
|
||||||
"members",
|
"members",
|
||||||
vec![
|
vec![
|
||||||
ValueAccessor::FieldAccess {
|
ValueAccessor::FieldAccessByName {
|
||||||
field_name: "field",
|
field_name: "field",
|
||||||
},
|
},
|
||||||
ValueAccessor::ArrayAccess { idx: 1 },
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
|
@ -28,7 +28,10 @@ pub enum ValueAccessor<'input> {
|
|||||||
ArrayAccess { idx: u32 },
|
ArrayAccess { idx: u32 },
|
||||||
|
|
||||||
// .field
|
// .field
|
||||||
FieldAccess { field_name: &'input str },
|
FieldAccessByName { field_name: &'input str },
|
||||||
|
|
||||||
|
// (.)?[field]
|
||||||
|
FieldAccessByScalar { scalar_name: &'input str },
|
||||||
|
|
||||||
// needed to allow parser catch all errors from a lambda expression without stopping
|
// needed to allow parser catch all errors from a lambda expression without stopping
|
||||||
// on the very first one. Although, this variant is guaranteed not to be present in a lambda.
|
// on the very first one. Although, this variant is guaranteed not to be present in a lambda.
|
||||||
|
@ -24,7 +24,8 @@ impl fmt::Display for ValueAccessor<'_> {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
ArrayAccess { idx } => write!(f, ".[{}]", idx),
|
ArrayAccess { idx } => write!(f, ".[{}]", idx),
|
||||||
FieldAccess { field_name } => write!(f, ".{}", field_name),
|
FieldAccessByName { field_name } => write!(f, ".{}", field_name),
|
||||||
|
FieldAccessByScalar { scalar_name } => write!(f, ".[{}]", scalar_name),
|
||||||
Error => write!(f, "a parser error occurred while parsing lambda expression"),
|
Error => write!(f, "a parser error occurred while parsing lambda expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ pub use ast::*;
|
|||||||
|
|
||||||
pub fn format_ast(lambda_ast: &LambdaAST<'_>) -> String {
|
pub fn format_ast(lambda_ast: &LambdaAST<'_>) -> String {
|
||||||
let mut formatted_ast = String::new();
|
let mut formatted_ast = String::new();
|
||||||
for algebra in lambda_ast.iter() {
|
for accessor in lambda_ast.iter() {
|
||||||
formatted_ast.push_str(&algebra.to_string());
|
formatted_ast.push_str(&accessor.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted_ast
|
formatted_ast
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
pub use parser::parse;
|
pub use parser::parse;
|
||||||
pub use parser::AlgebraLexer;
|
pub use parser::AccessorsLexer;
|
||||||
pub use parser::LambdaParser;
|
pub use parser::LambdaParser;
|
||||||
pub use parser::LambdaParserError;
|
pub use parser::LambdaParserError;
|
||||||
pub use parser::LexerError;
|
pub use parser::LexerError;
|
||||||
|
@ -26,7 +26,7 @@ pub enum LambdaParserError<'input> {
|
|||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
LexerError(#[from] LexerError),
|
LexerError(#[from] LexerError),
|
||||||
|
|
||||||
#[error("provided lambda expression doesn't contain any algebras")]
|
#[error("provided lambda expression doesn't contain any accessor")]
|
||||||
EmptyLambda,
|
EmptyLambda,
|
||||||
|
|
||||||
#[error("{0:?}")]
|
#[error("{0:?}")]
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::lexer::AlgebraLexer;
|
use super::lexer::AccessorsLexer;
|
||||||
use super::va_lambda;
|
use super::va_lambda;
|
||||||
use super::LambdaParserError;
|
use super::LambdaParserError;
|
||||||
use super::LambdaParserResult;
|
use super::LambdaParserResult;
|
||||||
@ -31,22 +31,22 @@ thread_local!(static PARSER: LambdaParser = LambdaParser::new());
|
|||||||
pub fn parse(lambda: &str) -> LambdaParserResult<'_, LambdaAST> {
|
pub fn parse(lambda: &str) -> LambdaParserResult<'_, LambdaAST> {
|
||||||
PARSER.with(|parser| {
|
PARSER.with(|parser| {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let lexer = AlgebraLexer::new(lambda);
|
let lexer = AccessorsLexer::new(lambda);
|
||||||
let result = parser.parse(lambda, &mut errors, lexer);
|
let result = parser.parse(lambda, &mut errors, lexer);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(algebras) if errors.is_empty() => try_to_lambda(algebras),
|
Ok(accessors) if errors.is_empty() => try_to_lambda(accessors),
|
||||||
Ok(_) => Err(errors.into()),
|
Ok(_) => Err(errors.into()),
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_to_lambda(algebras: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
|
fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
|
||||||
if algebras.is_empty() {
|
if accessors.is_empty() {
|
||||||
return Err(LambdaParserError::EmptyLambda);
|
return Err(LambdaParserError::EmptyLambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ast = unsafe { LambdaAST::new_unchecked(algebras) };
|
let ast = unsafe { LambdaAST::new_unchecked(accessors) };
|
||||||
Ok(ast)
|
Ok(ast)
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,12 @@ const ARRAY_IDX_BASE: u32 = 10;
|
|||||||
|
|
||||||
pub type Spanned<Token, Loc, Error> = Result<(Loc, Token, Loc), Error>;
|
pub type Spanned<Token, Loc, Error> = Result<(Loc, Token, Loc), Error>;
|
||||||
|
|
||||||
pub struct AlgebraLexer<'input> {
|
pub struct AccessorsLexer<'input> {
|
||||||
input: &'input str,
|
input: &'input str,
|
||||||
chars: Peekable<CharIndices<'input>>,
|
chars: Peekable<CharIndices<'input>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> Iterator for AlgebraLexer<'input> {
|
impl<'input> Iterator for AccessorsLexer<'input> {
|
||||||
type Item = Spanned<Token<'input>, usize, LexerError>;
|
type Item = Spanned<Token<'input>, usize, LexerError>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
@ -38,7 +38,7 @@ impl<'input> Iterator for AlgebraLexer<'input> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'input> AlgebraLexer<'input> {
|
impl<'input> AccessorsLexer<'input> {
|
||||||
pub fn new(input: &'input str) -> Self {
|
pub fn new(input: &'input str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input,
|
input,
|
||||||
@ -71,7 +71,11 @@ impl<'input> AlgebraLexer<'input> {
|
|||||||
.parse::<u32>()
|
.parse::<u32>()
|
||||||
.map_err(|e| LexerError::ParseIntError(start_pos, start_pos + array_idx.len(), e))
|
.map_err(|e| LexerError::ParseIntError(start_pos, start_pos + array_idx.len(), e))
|
||||||
{
|
{
|
||||||
Ok(idx) => Ok((start_pos, Token::ArrayIdx(idx), start_pos + array_idx.len())),
|
Ok(idx) => Ok((
|
||||||
|
start_pos,
|
||||||
|
Token::NumberAccessor(idx),
|
||||||
|
start_pos + array_idx.len(),
|
||||||
|
)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +88,7 @@ impl<'input> AlgebraLexer<'input> {
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
start_pos,
|
start_pos,
|
||||||
Token::FieldName(field_name),
|
Token::StringAccessor(field_name),
|
||||||
start_pos + field_name.len(),
|
start_pos + field_name.len(),
|
||||||
))
|
))
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ use std::num::ParseIntError;
|
|||||||
|
|
||||||
#[derive(ThisError, Debug, Clone, PartialEq, Eq)]
|
#[derive(ThisError, Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum LexerError {
|
pub enum LexerError {
|
||||||
#[error("unexpected symbol for value algebra")]
|
#[error("unexpected symbol for value accessor")]
|
||||||
UnexpectedSymbol(usize, usize),
|
UnexpectedSymbol(usize, usize),
|
||||||
|
|
||||||
#[error("{2}")]
|
#[error("{2}")]
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod algebra_lexer;
|
mod accessors_lexer;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod token;
|
mod token;
|
||||||
mod utils;
|
mod utils;
|
||||||
@ -22,7 +22,7 @@ mod utils;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub use algebra_lexer::AlgebraLexer;
|
pub use accessors_lexer::AccessorsLexer;
|
||||||
pub use errors::LexerError;
|
pub use errors::LexerError;
|
||||||
pub use token::Token;
|
pub use token::Token;
|
||||||
|
|
||||||
|
@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::algebra_lexer::Spanned;
|
use super::accessors_lexer::Spanned;
|
||||||
use super::AlgebraLexer;
|
use super::AccessorsLexer;
|
||||||
use super::LexerError;
|
use super::LexerError;
|
||||||
use super::Token;
|
use super::Token;
|
||||||
|
|
||||||
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
|
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
|
||||||
let lexer = AlgebraLexer::new(input);
|
let lexer = AccessorsLexer::new(input);
|
||||||
lexer.collect()
|
lexer.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ fn array_access() {
|
|||||||
let expected = vec![
|
let expected = vec![
|
||||||
Spanned::Ok((0, Token::Selector, 1)),
|
Spanned::Ok((0, Token::Selector, 1)),
|
||||||
Spanned::Ok((1, Token::OpenSquareBracket, 2)),
|
Spanned::Ok((1, Token::OpenSquareBracket, 2)),
|
||||||
Spanned::Ok((2, Token::ArrayIdx(0), 3)),
|
Spanned::Ok((2, Token::NumberAccessor(0), 3)),
|
||||||
Spanned::Ok((3, Token::CloseSquareBracket, 4)),
|
Spanned::Ok((3, Token::CloseSquareBracket, 4)),
|
||||||
];
|
];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -46,7 +46,7 @@ fn field_access() {
|
|||||||
let actual = run_lexer(&field_access);
|
let actual = run_lexer(&field_access);
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
Spanned::Ok((0, Token::Selector, 1)),
|
Spanned::Ok((0, Token::Selector, 1)),
|
||||||
Spanned::Ok((1, Token::FieldName(field_name), 1 + field_name.len())),
|
Spanned::Ok((1, Token::StringAccessor(field_name), 1 + field_name.len())),
|
||||||
];
|
];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@ pub enum Token<'input> {
|
|||||||
OpenSquareBracket,
|
OpenSquareBracket,
|
||||||
CloseSquareBracket,
|
CloseSquareBracket,
|
||||||
|
|
||||||
ArrayIdx(u32),
|
NumberAccessor(u32),
|
||||||
FieldName(&'input str),
|
StringAccessor(&'input str),
|
||||||
|
|
||||||
// !
|
// !
|
||||||
FlatteningSign,
|
FlatteningSign,
|
||||||
|
@ -31,7 +31,7 @@ pub type LambdaParserResult<'input, T> = std::result::Result<T, LambdaParserErro
|
|||||||
|
|
||||||
pub use errors::LambdaParserError;
|
pub use errors::LambdaParserError;
|
||||||
pub use lambda_parser::parse;
|
pub use lambda_parser::parse;
|
||||||
pub use lexer::AlgebraLexer;
|
pub use lexer::AccessorsLexer;
|
||||||
pub use lexer::LexerError;
|
pub use lexer::LexerError;
|
||||||
pub use va_lambda::LambdaParser;
|
pub use va_lambda::LambdaParser;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ thread_local!(static TEST_PARSER: LambdaParser = LambdaParser::new());
|
|||||||
fn parse(source_code: &str) -> Vec<ValueAccessor<'_>> {
|
fn parse(source_code: &str) -> Vec<ValueAccessor<'_>> {
|
||||||
TEST_PARSER.with(|parser| {
|
TEST_PARSER.with(|parser| {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let lexer = crate::parser::AlgebraLexer::new(source_code);
|
let lexer = crate::parser::AccessorsLexer::new(source_code);
|
||||||
parser
|
parser
|
||||||
.parse(source_code, &mut errors, lexer)
|
.parse(source_code, &mut errors, lexer)
|
||||||
.expect("parsing should be successful")
|
.expect("parsing should be successful")
|
||||||
@ -35,7 +35,7 @@ fn field_access() {
|
|||||||
let lambda = format!(".{}", field_name);
|
let lambda = format!(".{}", field_name);
|
||||||
|
|
||||||
let actual = parse(&lambda);
|
let actual = parse(&lambda);
|
||||||
let expected = vec![ValueAccessor::FieldAccess { field_name }];
|
let expected = vec![ValueAccessor::FieldAccessByName { field_name }];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ fn field_access_with_flattening() {
|
|||||||
let lambda = format!(".{}!", field_name);
|
let lambda = format!(".{}!", field_name);
|
||||||
|
|
||||||
let actual = parse(&lambda);
|
let actual = parse(&lambda);
|
||||||
let expected = vec![ValueAccessor::FieldAccess { field_name }];
|
let expected = vec![ValueAccessor::FieldAccessByName { field_name }];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +69,26 @@ fn array_access_with_flattening() {
|
|||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_access() {
|
||||||
|
let scalar_name = "some_field_name";
|
||||||
|
let lambda = format!(".[{}]", scalar_name);
|
||||||
|
|
||||||
|
let actual = parse(&lambda);
|
||||||
|
let expected = vec![ValueAccessor::FieldAccessByScalar { scalar_name }];
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_access_with_flattening() {
|
||||||
|
let scalar_name = "some_scalar_name";
|
||||||
|
let lambda = format!(".[{}]!", scalar_name);
|
||||||
|
|
||||||
|
let actual = parse(&lambda);
|
||||||
|
let expected = vec![ValueAccessor::FieldAccessByScalar { scalar_name }];
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn field_array_access() {
|
fn field_array_access() {
|
||||||
let field_name = "some_field_name";
|
let field_name = "some_field_name";
|
||||||
@ -77,7 +97,35 @@ fn field_array_access() {
|
|||||||
|
|
||||||
let actual = parse(&lambda);
|
let actual = parse(&lambda);
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
ValueAccessor::FieldAccess { field_name },
|
ValueAccessor::FieldAccessByName { field_name },
|
||||||
|
ValueAccessor::ArrayAccess { idx },
|
||||||
|
];
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn field_scalar_access() {
|
||||||
|
let field_name = "some_field_name";
|
||||||
|
let scalar_name = "some_scalar_name";
|
||||||
|
let lambda = format!(".{}.[{}]", field_name, scalar_name);
|
||||||
|
|
||||||
|
let actual = parse(&lambda);
|
||||||
|
let expected = vec![
|
||||||
|
ValueAccessor::FieldAccessByName { field_name },
|
||||||
|
ValueAccessor::FieldAccessByScalar { scalar_name },
|
||||||
|
];
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scalar_array_access() {
|
||||||
|
let scalar_name = "some_scalar_name";
|
||||||
|
let idx = 1;
|
||||||
|
let lambda = format!(".[{}].[{}]", scalar_name, idx);
|
||||||
|
|
||||||
|
let actual = parse(&lambda);
|
||||||
|
let expected = vec![
|
||||||
|
ValueAccessor::FieldAccessByScalar { scalar_name },
|
||||||
ValueAccessor::ArrayAccess { idx },
|
ValueAccessor::ArrayAccess { idx },
|
||||||
];
|
];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -91,7 +139,7 @@ fn field_array_access_without_dot() {
|
|||||||
|
|
||||||
let actual = parse(&lambda);
|
let actual = parse(&lambda);
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
ValueAccessor::FieldAccess { field_name },
|
ValueAccessor::FieldAccessByName { field_name },
|
||||||
ValueAccessor::ArrayAccess { idx },
|
ValueAccessor::ArrayAccess { idx },
|
||||||
];
|
];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
@ -106,7 +154,21 @@ fn array_field_access() {
|
|||||||
let actual = parse(&lambda);
|
let actual = parse(&lambda);
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
ValueAccessor::ArrayAccess { idx },
|
ValueAccessor::ArrayAccess { idx },
|
||||||
ValueAccessor::FieldAccess { field_name },
|
ValueAccessor::FieldAccessByName { field_name },
|
||||||
|
];
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_scalar_access() {
|
||||||
|
let scalar_name = "some_scalar_name";
|
||||||
|
let idx = 1;
|
||||||
|
let lambda = format!(".[{}].[{}]", idx, scalar_name);
|
||||||
|
|
||||||
|
let actual = parse(&lambda);
|
||||||
|
let expected = vec![
|
||||||
|
ValueAccessor::ArrayAccess { idx },
|
||||||
|
ValueAccessor::FieldAccessByScalar { scalar_name },
|
||||||
];
|
];
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
@ -122,11 +184,44 @@ fn many_array_field_access() {
|
|||||||
let actual = parse(&lambda);
|
let actual = parse(&lambda);
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
ValueAccessor::ArrayAccess { idx: idx_1 },
|
ValueAccessor::ArrayAccess { idx: idx_1 },
|
||||||
ValueAccessor::FieldAccess {
|
ValueAccessor::FieldAccessByName {
|
||||||
field_name: field_name_1,
|
field_name: field_name_1,
|
||||||
},
|
},
|
||||||
ValueAccessor::ArrayAccess { idx: idx_2 },
|
ValueAccessor::ArrayAccess { idx: idx_2 },
|
||||||
ValueAccessor::FieldAccess {
|
ValueAccessor::FieldAccessByName {
|
||||||
|
field_name: field_name_2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn many_array_field_scalar_access() {
|
||||||
|
let field_name_1 = "some_field_name_1";
|
||||||
|
let field_name_2 = "some_field_name_2";
|
||||||
|
let idx_1 = 1;
|
||||||
|
let idx_2 = u32::MAX;
|
||||||
|
let scalar_name_1 = "some_scalar_name_1";
|
||||||
|
let scalar_name_2 = "some_scalar_name_2";
|
||||||
|
let lambda = format!(
|
||||||
|
".[{}].[{}].{}.[{}].[{}].{}",
|
||||||
|
idx_1, scalar_name_1, field_name_1, idx_2, scalar_name_2, field_name_2
|
||||||
|
);
|
||||||
|
|
||||||
|
let actual = parse(&lambda);
|
||||||
|
let expected = vec![
|
||||||
|
ValueAccessor::ArrayAccess { idx: idx_1 },
|
||||||
|
ValueAccessor::FieldAccessByScalar {
|
||||||
|
scalar_name: scalar_name_1,
|
||||||
|
},
|
||||||
|
ValueAccessor::FieldAccessByName {
|
||||||
|
field_name: field_name_1,
|
||||||
|
},
|
||||||
|
ValueAccessor::ArrayAccess { idx: idx_2 },
|
||||||
|
ValueAccessor::FieldAccessByScalar {
|
||||||
|
scalar_name: scalar_name_2,
|
||||||
|
},
|
||||||
|
ValueAccessor::FieldAccessByName {
|
||||||
field_name: field_name_2,
|
field_name: field_name_2,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::ValueAlgebra;
|
use crate::ValueAccessor;
|
||||||
use crate::parser::lexer::LexerError;
|
use crate::parser::lexer::LexerError;
|
||||||
use crate::parser::lexer::Token;
|
use crate::parser::lexer::Token;
|
||||||
|
|
||||||
@ -7,18 +7,22 @@ use lalrpop_util::ErrorRecovery;
|
|||||||
// the only thing why input matters here is just introducing lifetime for Token
|
// the only thing why input matters here is just introducing lifetime for Token
|
||||||
grammar<'err, 'input>(input: &'input str, errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, LexerError>>);
|
grammar<'err, 'input>(input: &'input str, errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, LexerError>>);
|
||||||
|
|
||||||
pub Lambda: Vec<ValueAlgebra<'input>> = <ValueAlgebra*> => <>;
|
pub Lambda: Vec<ValueAccessor<'input>> = <ValueAccessor*> => <>;
|
||||||
|
|
||||||
ValueAlgebra: ValueAlgebra<'input> = {
|
ValueAccessor: ValueAccessor<'input> = {
|
||||||
<maybe_dot_selector:"."?> "[" <idx: array_idx> "]" <maybe_flatten_sign:"!"?> => {
|
<maybe_dot_selector:"."?> "[" <idx: number_accessor> "]" <maybe_flatten_sign:"!"?> => {
|
||||||
ValueAlgebra::ArrayAccess { idx }
|
ValueAccessor::ArrayAccess { idx }
|
||||||
},
|
},
|
||||||
|
|
||||||
"." <field_name: field_name> <maybe_flatten_sign:"!"?> => {
|
<maybe_dot_selector:"."?> "[" <scalar_name: string_accessor> "]" <maybe_flatten_sign:"!"?> => {
|
||||||
ValueAlgebra::FieldAccess { field_name }
|
ValueAccessor::FieldAccessByScalar { scalar_name }
|
||||||
},
|
},
|
||||||
|
|
||||||
! => { errors.push(<>); ValueAlgebra::Error },
|
"." <field_name: string_accessor> <maybe_flatten_sign:"!"?> => {
|
||||||
|
ValueAccessor::FieldAccessByName { field_name }
|
||||||
|
},
|
||||||
|
|
||||||
|
! => { errors.push(<>); ValueAccessor::Error },
|
||||||
}
|
}
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
@ -31,8 +35,8 @@ extern {
|
|||||||
"[" => Token::OpenSquareBracket,
|
"[" => Token::OpenSquareBracket,
|
||||||
"]" => Token::CloseSquareBracket,
|
"]" => Token::CloseSquareBracket,
|
||||||
|
|
||||||
array_idx => Token::ArrayIdx(<u32>),
|
number_accessor => Token::NumberAccessor(<u32>),
|
||||||
field_name => Token::FieldName(<&'input str>),
|
string_accessor => Token::StringAccessor(<&'input str>),
|
||||||
|
|
||||||
"!" => Token::FlatteningSign,
|
"!" => Token::FlatteningSign,
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user