mirror of
https://github.com/fluencelabs/aquavm
synced 2024-12-04 23:20:18 +00:00
Introduce length functor (#314)
This commit is contained in:
parent
626796b299
commit
a4011ef038
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,3 +1,20 @@
|
||||
## Version 0.28.0 (2022-09-07)
|
||||
|
||||
[PR 314](https://github.com/fluencelabs/aquavm/pull/314):
|
||||
The length functor was introduced
|
||||
|
||||
[PR 307](https://github.com/fluencelabs/aquavm/pull/307):
|
||||
bug with iteration over empty canonicalized stream was fixed
|
||||
|
||||
[PR 305](https://github.com/fluencelabs/aquavm/pull/305):
|
||||
bug with absence of join behaviour for canonicalized streams in fold instructions was fixed
|
||||
|
||||
[PR 303](https://github.com/fluencelabs/aquavm/pull/303):
|
||||
unsafe code for `LambdaAST` creation was removed
|
||||
|
||||
[PR 301](https://github.com/fluencelabs/aquavm/pull/301):
|
||||
bug with adding just executed values to a restricted stream was fixed
|
||||
|
||||
## Version 0.27.0 (2022-08-23)
|
||||
|
||||
[PR 292](https://github.com/fluencelabs/aquavm/pull/292):
|
||||
|
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -13,7 +13,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "air"
|
||||
version = "0.27.0"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"air-execution-info-collector",
|
||||
"air-interpreter-data",
|
||||
@ -72,7 +72,7 @@ version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "air-interpreter"
|
||||
version = "0.27.0"
|
||||
version = "0.28.0"
|
||||
dependencies = [
|
||||
"air",
|
||||
"air-interpreter-interface",
|
||||
@ -112,6 +112,7 @@ dependencies = [
|
||||
name = "air-lambda-ast"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools 0.10.3",
|
||||
"non-empty-vec",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air-interpreter"
|
||||
version = "0.27.0"
|
||||
version = "0.28.0"
|
||||
description = "Crate-wrapper for air"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air"
|
||||
version = "0.27.0"
|
||||
version = "0.28.0"
|
||||
description = "Interpreter of AIR scripts intended to coordinate request flow in the Fluence network"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
|
@ -138,7 +138,11 @@ fn apply_canon_stream_with_lambda(
|
||||
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());
|
||||
let value = ValueAggregate::new(
|
||||
Rc::new(result.into_owned()),
|
||||
Rc::new(tetraplet),
|
||||
canon_stream.position(),
|
||||
);
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::execution_step::boxed_value::populate_tetraplet_with_lambda;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::PEEK_ALLOWED_ON_NON_EMPTY;
|
||||
use crate::JValue;
|
||||
@ -23,6 +24,7 @@ use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -130,11 +132,11 @@ fn create_scalar_lambda_iterable<'ctx>(
|
||||
scalar_name: &str,
|
||||
lambda: &LambdaAST<'_>,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
use crate::execution_step::lambda_applier::select_from_scalar;
|
||||
use crate::execution_step::lambda_applier::select_by_lambda_from_scalar;
|
||||
|
||||
match exec_ctx.scalars.get_value(scalar_name)? {
|
||||
ScalarRef::Value(variable) => {
|
||||
let jvalues = select_from_scalar(&variable.result, lambda.iter(), exec_ctx)?;
|
||||
let jvalues = select_by_lambda_from_scalar(&variable.result, lambda, exec_ctx)?;
|
||||
let tetraplet = variable.tetraplet.deref().clone();
|
||||
from_jvalue(jvalues, tetraplet, lambda)
|
||||
}
|
||||
@ -150,18 +152,17 @@ fn create_scalar_lambda_iterable<'ctx>(
|
||||
|
||||
/// Construct IterableValue from the result and given triplet.
|
||||
fn from_jvalue(
|
||||
jvalue: &JValue,
|
||||
mut tetraplet: SecurityTetraplet,
|
||||
jvalue: Cow<'_, JValue>,
|
||||
tetraplet: SecurityTetraplet,
|
||||
lambda: &LambdaAST<'_>,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
let formatted_lambda_ast = air_lambda_ast::format_ast(lambda);
|
||||
tetraplet.add_lambda(&formatted_lambda_ast);
|
||||
let tetraplet = populate_tetraplet_with_lambda(tetraplet, lambda);
|
||||
let tetraplet = Rc::new(tetraplet);
|
||||
|
||||
let iterable = match jvalue {
|
||||
let iterable = match jvalue.as_ref() {
|
||||
JValue::Array(array) => array,
|
||||
_ => {
|
||||
return Err(CatchableError::FoldIteratesOverNonArray(jvalue.clone(), formatted_lambda_ast).into());
|
||||
return Err(CatchableError::FoldIteratesOverNonArray(jvalue.into_owned(), lambda.to_string()).into());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -38,14 +38,15 @@ use std::borrow::Cow;
|
||||
/// Represent a value that could be transform to a JValue with or without tetraplets.
|
||||
pub(crate) trait JValuable {
|
||||
/// Applies lambda to the internal value, produces JValue.
|
||||
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue>;
|
||||
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>)
|
||||
-> ExecutionResult<Cow<'_, JValue>>;
|
||||
|
||||
/// Applies lambda to the internal value, produces JValue with tetraplet.
|
||||
fn apply_lambda_with_tetraplets<'i>(
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)>;
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)>;
|
||||
|
||||
/// Return internal value as borrowed if it's possible, owned otherwise.
|
||||
fn as_jvalue(&self) -> Cow<'_, JValue>;
|
||||
|
@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::select_from_stream;
|
||||
use super::select_by_lambda_from_stream;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::execution_step::boxed_value::populate_tetraplet_with_lambda;
|
||||
use crate::execution_step::boxed_value::CanonStream;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
use crate::execution_step::RcSecurityTetraplets;
|
||||
@ -24,15 +25,17 @@ 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> {
|
||||
fn apply_lambda<'i>(
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'_, JValue>> {
|
||||
let iter = self.iter().map(|v| v.result.deref());
|
||||
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||
let select_result = select_by_lambda_from_stream(iter, lambda, exec_ctx)?;
|
||||
|
||||
Ok(select_result.result)
|
||||
}
|
||||
@ -41,14 +44,18 @@ impl JValuable for &CanonStream {
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)> {
|
||||
let iter = self.iter().map(|v| v.result.deref());
|
||||
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||
let select_result = select_by_lambda_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));
|
||||
let tetraplet = match select_result.tetraplet_idx {
|
||||
Some(idx) => {
|
||||
let resolved_call = self.nth(idx).expect(crate::execution_step::TETRAPLET_IDX_CORRECT);
|
||||
resolved_call.tetraplet.as_ref().clone()
|
||||
}
|
||||
None => SecurityTetraplet::new(exec_ctx.run_parameters.current_peer_id.to_string(), "", "", ""),
|
||||
};
|
||||
let tetraplet = populate_tetraplet_with_lambda(tetraplet, lambda);
|
||||
|
||||
Ok((select_result.result, tetraplet))
|
||||
}
|
||||
|
@ -14,25 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::select_from_stream;
|
||||
use super::select_by_lambda_from_stream;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use super::ValueAggregate;
|
||||
use crate::execution_step::boxed_value::populate_tetraplet_with_lambda;
|
||||
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 std::cell::Ref<'_, Vec<ValueAggregate>> {
|
||||
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||
fn apply_lambda<'i>(
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'_, JValue>> {
|
||||
let stream_iter = self.iter().map(|r| r.result.deref());
|
||||
let select_result = select_from_stream(stream_iter, lambda, exec_ctx)?;
|
||||
let select_result = select_by_lambda_from_stream(stream_iter, lambda, exec_ctx)?;
|
||||
Ok(select_result.result)
|
||||
}
|
||||
|
||||
@ -40,13 +43,17 @@ impl JValuable for std::cell::Ref<'_, Vec<ValueAggregate>> {
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)> {
|
||||
let stream_iter = self.iter().map(|r| r.result.deref());
|
||||
let select_result = select_from_stream(stream_iter, lambda, exec_ctx)?;
|
||||
let select_result = select_by_lambda_from_stream(stream_iter, lambda, exec_ctx)?;
|
||||
|
||||
let tetraplet = &self[select_result.tetraplet_idx].tetraplet;
|
||||
let mut tetraplet = tetraplet.as_ref().clone();
|
||||
tetraplet.add_lambda(&format_ast(lambda));
|
||||
let tetraplet = match select_result.tetraplet_idx {
|
||||
Some(idx) => {
|
||||
let tetraplet = &self[idx].tetraplet;
|
||||
populate_tetraplet_with_lambda(tetraplet.as_ref().clone(), lambda)
|
||||
}
|
||||
None => SecurityTetraplet::new(exec_ctx.run_parameters.current_peer_id.to_string(), "", "", ""),
|
||||
};
|
||||
|
||||
Ok((select_result.result, tetraplet))
|
||||
}
|
||||
|
@ -27,7 +27,11 @@ use crate::SecurityTetraplet;
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl JValuable for () {
|
||||
fn apply_lambda<'i>(&self, _lambda: &LambdaAST<'_>, _exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||
fn apply_lambda<'i>(
|
||||
&self,
|
||||
_lambda: &LambdaAST<'_>,
|
||||
_exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'_, JValue>> {
|
||||
// applying lambda to an empty stream will produce a join behaviour
|
||||
Err(LambdaApplierError(EmptyStream).into())
|
||||
}
|
||||
@ -36,7 +40,7 @@ impl JValuable for () {
|
||||
&self,
|
||||
_lambda: &LambdaAST<'_>,
|
||||
_exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)> {
|
||||
// applying lambda to an empty stream will produce a join behaviour
|
||||
Err(LambdaApplierError(EmptyStream).into())
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::select_from_scalar;
|
||||
use super::select_by_lambda_from_scalar;
|
||||
use super::ExecutionResult;
|
||||
use super::IterableItem;
|
||||
use super::JValuable;
|
||||
@ -24,12 +24,16 @@ use crate::execution_step::RcSecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_lambda_ast::format_ast;
|
||||
use crate::execution_step::boxed_value::populate_tetraplet_with_lambda;
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||
fn apply_lambda<'i>(
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'_, JValue>> {
|
||||
use super::IterableItem::*;
|
||||
|
||||
let jvalue = match self {
|
||||
@ -38,7 +42,7 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
RcValue((jvalue, ..)) => jvalue.deref(),
|
||||
};
|
||||
|
||||
let selected_value = select_from_scalar(jvalue, lambda.iter(), exec_ctx)?;
|
||||
let selected_value = select_by_lambda_from_scalar(jvalue, lambda, exec_ctx)?;
|
||||
Ok(selected_value)
|
||||
}
|
||||
|
||||
@ -46,7 +50,7 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)> {
|
||||
use super::IterableItem::*;
|
||||
|
||||
let (jvalue, tetraplet) = match self {
|
||||
@ -55,9 +59,8 @@ impl<'ctx> JValuable for IterableItem<'ctx> {
|
||||
RcValue((jvalue, tetraplet, _)) => (jvalue.deref(), tetraplet),
|
||||
};
|
||||
|
||||
let selected_value = select_from_scalar(jvalue, lambda.iter(), exec_ctx)?;
|
||||
let mut tetraplet = tetraplet.as_ref().clone();
|
||||
tetraplet.add_lambda(&format_ast(lambda));
|
||||
let selected_value = select_by_lambda_from_scalar(jvalue, lambda, exec_ctx)?;
|
||||
let tetraplet = populate_tetraplet_with_lambda(tetraplet.as_ref().clone(), lambda);
|
||||
|
||||
Ok((selected_value, tetraplet))
|
||||
}
|
||||
|
@ -14,24 +14,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::select_from_scalar;
|
||||
use super::select_by_lambda_from_scalar;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use super::LambdaAST;
|
||||
use super::ValueAggregate;
|
||||
use crate::execution_step::boxed_value::populate_tetraplet_with_lambda;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
use crate::execution_step::RcSecurityTetraplets;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_lambda_ast::format_ast;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
impl JValuable for ValueAggregate {
|
||||
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||
let selected_value = select_from_scalar(&self.result, lambda.iter(), exec_ctx)?;
|
||||
fn apply_lambda<'i>(
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'_, JValue>> {
|
||||
let selected_value = select_by_lambda_from_scalar(&self.result, lambda, exec_ctx)?;
|
||||
Ok(selected_value)
|
||||
}
|
||||
|
||||
@ -39,10 +42,9 @@ impl JValuable for ValueAggregate {
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||
let selected_value = select_from_scalar(&self.result, lambda.iter(), exec_ctx)?;
|
||||
let mut tetraplet = self.tetraplet.as_ref().clone();
|
||||
tetraplet.add_lambda(&format_ast(lambda));
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)> {
|
||||
let selected_value = select_by_lambda_from_scalar(&self.result, lambda, exec_ctx)?;
|
||||
let tetraplet = populate_tetraplet_with_lambda(self.tetraplet.as_ref().clone(), lambda);
|
||||
|
||||
Ok((selected_value, tetraplet))
|
||||
}
|
||||
|
@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::select_from_stream;
|
||||
use super::select_by_lambda_from_stream;
|
||||
use super::ExecutionResult;
|
||||
use super::JValuable;
|
||||
use crate::execution_step::boxed_value::populate_tetraplet_with_lambda;
|
||||
use crate::execution_step::boxed_value::Generation;
|
||||
use crate::execution_step::boxed_value::Stream;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
@ -25,8 +26,6 @@ use crate::JValue;
|
||||
use crate::LambdaAST;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_lambda_ast::format_ast;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
@ -39,9 +38,13 @@ pub(crate) struct StreamJvaluableIngredients<'stream> {
|
||||
// TODO: this will be deleted soon, because it would be impossible to use streams without
|
||||
// canonicalization as an arg of a call
|
||||
impl JValuable for StreamJvaluableIngredients<'_> {
|
||||
fn apply_lambda<'i>(&self, lambda: &LambdaAST<'_>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<&JValue> {
|
||||
fn apply_lambda<'i>(
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'_, JValue>> {
|
||||
let iter = self.iter()?.map(|v| v.result.deref());
|
||||
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||
let select_result = select_by_lambda_from_stream(iter, lambda, exec_ctx)?;
|
||||
|
||||
Ok(select_result.result)
|
||||
}
|
||||
@ -50,14 +53,21 @@ impl JValuable for StreamJvaluableIngredients<'_> {
|
||||
&self,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(&JValue, SecurityTetraplet)> {
|
||||
) -> ExecutionResult<(Cow<'_, JValue>, SecurityTetraplet)> {
|
||||
let iter = self.iter()?.map(|v| v.result.deref());
|
||||
let select_result = select_from_stream(iter, lambda, exec_ctx)?;
|
||||
let select_result = select_by_lambda_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.iter()?.nth(select_result.tetraplet_idx).unwrap();
|
||||
let mut tetraplet = resolved_call.tetraplet.as_ref().clone();
|
||||
tetraplet.add_lambda(&format_ast(lambda));
|
||||
let tetraplet = match select_result.tetraplet_idx {
|
||||
Some(idx) => {
|
||||
let resolved_call = self
|
||||
.iter()?
|
||||
.nth(idx)
|
||||
.expect(crate::execution_step::TETRAPLET_IDX_CORRECT);
|
||||
resolved_call.tetraplet.as_ref().clone()
|
||||
}
|
||||
None => SecurityTetraplet::new(exec_ctx.run_parameters.current_peer_id.to_string(), "", "", ""),
|
||||
};
|
||||
let tetraplet = populate_tetraplet_with_lambda(tetraplet, lambda);
|
||||
|
||||
Ok((select_result.result, tetraplet))
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ mod iterable;
|
||||
mod jvaluable;
|
||||
mod scalar;
|
||||
mod stream;
|
||||
mod utils;
|
||||
mod variable;
|
||||
|
||||
pub(crate) use canon_stream::*;
|
||||
@ -29,6 +30,7 @@ pub(crate) use scalar::ValueAggregate;
|
||||
pub(crate) use stream::Generation;
|
||||
pub(crate) use stream::Stream;
|
||||
pub(crate) use stream::StreamIter;
|
||||
pub(crate) use utils::populate_tetraplet_with_lambda;
|
||||
pub(crate) use variable::Variable;
|
||||
|
||||
use super::ExecutionResult;
|
||||
|
31
air/src/execution_step/boxed_value/utils.rs
Normal file
31
air/src/execution_step/boxed_value/utils.rs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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_lambda_ast::LambdaAST;
|
||||
use polyplets::SecurityTetraplet;
|
||||
|
||||
pub(crate) fn populate_tetraplet_with_lambda(
|
||||
mut tetraplet: SecurityTetraplet,
|
||||
lambda: &LambdaAST<'_>,
|
||||
) -> SecurityTetraplet {
|
||||
match lambda {
|
||||
LambdaAST::ValuePath(_) => {
|
||||
tetraplet.add_lambda(&lambda.to_string());
|
||||
tetraplet
|
||||
}
|
||||
LambdaAST::Functor(_) => SecurityTetraplet::new("", "", "", &lambda.to_string()),
|
||||
}
|
||||
}
|
@ -91,6 +91,10 @@ pub enum CatchableError {
|
||||
/// 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),
|
||||
|
||||
/// This error type is occurred when the length functor applied to a value of non-array type.
|
||||
#[error("the length functor could applied only to an array-like value, but it's applied to '{0}'")]
|
||||
LengthFunctorAppliedToNotArray(JValue),
|
||||
}
|
||||
|
||||
impl From<LambdaError> for Rc<CatchableError> {
|
||||
|
@ -16,23 +16,52 @@
|
||||
|
||||
use super::utils::*;
|
||||
use super::LambdaError;
|
||||
use crate::execution_step::CatchableError;
|
||||
use crate::execution_step::ExecutionCtx;
|
||||
use crate::execution_step::ExecutionResult;
|
||||
use crate::lambda_to_execution_error;
|
||||
use crate::ExecutionError;
|
||||
use crate::JValue;
|
||||
use crate::LambdaAST;
|
||||
|
||||
use air_lambda_ast::Functor;
|
||||
use air_lambda_parser::ValueAccessor;
|
||||
use non_empty_vec::NonEmpty;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) struct StreamSelectResult<'value> {
|
||||
pub(crate) result: &'value JValue,
|
||||
pub(crate) tetraplet_idx: usize,
|
||||
pub(crate) result: Cow<'value, JValue>,
|
||||
pub(crate) tetraplet_idx: Option<usize>,
|
||||
}
|
||||
|
||||
pub(crate) fn select_from_stream<'value, 'i>(
|
||||
pub(crate) fn select_by_lambda_from_stream<'value, 'i>(
|
||||
stream: impl ExactSizeIterator<Item = &'value JValue> + 'value,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<StreamSelectResult<'value>> {
|
||||
match lambda {
|
||||
LambdaAST::ValuePath(value_path) => select_by_path_from_stream(stream, value_path, exec_ctx),
|
||||
LambdaAST::Functor(functor) => Ok(select_by_functor_from_stream(stream, functor)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn select_by_lambda_from_scalar<'value, 'i>(
|
||||
value: &'value JValue,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Cow<'value, JValue>> {
|
||||
match lambda {
|
||||
LambdaAST::ValuePath(value_path) => select_by_path_from_scalar(value, value_path.iter(), exec_ctx),
|
||||
LambdaAST::Functor(functor) => select_by_functor_from_scalar(value, functor).map(Cow::Owned),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_by_path_from_stream<'value, 'i>(
|
||||
stream: impl ExactSizeIterator<Item = &'value JValue> + 'value,
|
||||
lambda: &NonEmpty<ValueAccessor<'_>>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<StreamSelectResult<'value>> {
|
||||
let (prefix, body) = lambda.split_first();
|
||||
let idx = match prefix {
|
||||
@ -55,16 +84,28 @@ pub(crate) fn select_from_stream<'value, 'i>(
|
||||
.nth(idx as usize)
|
||||
.ok_or(LambdaError::StreamNotHaveEnoughValues { stream_size, idx }))?;
|
||||
|
||||
let result = select_from_scalar(value, body.iter(), exec_ctx)?;
|
||||
let select_result = StreamSelectResult::new(result, idx);
|
||||
let result = select_by_path_from_scalar(value, body.iter(), exec_ctx)?;
|
||||
let select_result = StreamSelectResult::from_cow(result, idx);
|
||||
Ok(select_result)
|
||||
}
|
||||
|
||||
pub(crate) fn select_from_scalar<'value, 'accessor, 'i>(
|
||||
fn select_by_functor_from_stream<'value, 'i>(
|
||||
stream: impl ExactSizeIterator<Item = &'value JValue> + 'value,
|
||||
functor: &Functor,
|
||||
) -> StreamSelectResult<'value> {
|
||||
match functor {
|
||||
Functor::Length => {
|
||||
let result = serde_json::json!(stream.len());
|
||||
StreamSelectResult::from_value(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_by_path_from_scalar<'value, 'accessor, 'i>(
|
||||
mut value: &'value JValue,
|
||||
lambda: impl Iterator<Item = &'accessor ValueAccessor<'accessor>>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<&'value JValue> {
|
||||
) -> ExecutionResult<Cow<'value, JValue>> {
|
||||
for accessor in lambda {
|
||||
match accessor {
|
||||
ValueAccessor::ArrayAccess { idx } => {
|
||||
@ -81,14 +122,35 @@ pub(crate) fn select_from_scalar<'value, 'accessor, 'i>(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
Ok(Cow::Borrowed(value))
|
||||
}
|
||||
|
||||
fn select_by_functor_from_scalar(value: &JValue, functor: &Functor) -> ExecutionResult<JValue> {
|
||||
match functor {
|
||||
Functor::Length => {
|
||||
let length = value
|
||||
.as_array()
|
||||
.ok_or_else(|| {
|
||||
ExecutionError::Catchable(Rc::new(CatchableError::LengthFunctorAppliedToNotArray(value.clone())))
|
||||
})?
|
||||
.len();
|
||||
Ok(serde_json::json!(length))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'value> StreamSelectResult<'value> {
|
||||
pub(self) fn new(result: &'value JValue, tetraplet_idx: u32) -> Self {
|
||||
pub(self) fn from_cow(result: Cow<'value, JValue>, tetraplet_idx: u32) -> Self {
|
||||
Self {
|
||||
result,
|
||||
tetraplet_idx: tetraplet_idx as usize,
|
||||
tetraplet_idx: Some(tetraplet_idx as usize),
|
||||
}
|
||||
}
|
||||
|
||||
pub(self) fn from_value(result: JValue) -> Self {
|
||||
Self {
|
||||
result: Cow::Owned(result),
|
||||
tetraplet_idx: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ pub use errors::LambdaError;
|
||||
|
||||
pub(crate) type LambdaResult<T> = std::result::Result<T, LambdaError>;
|
||||
|
||||
pub(crate) use applier::select_from_scalar;
|
||||
pub(crate) use applier::select_from_stream;
|
||||
pub(crate) use applier::select_by_lambda_from_scalar;
|
||||
pub(crate) use applier::select_by_lambda_from_stream;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! lambda_to_execution_error {
|
||||
|
@ -24,6 +24,8 @@ mod resolver;
|
||||
const PEEK_ALLOWED_ON_NON_EMPTY: &str = "peek always return elements inside fold,\
|
||||
this guaranteed by implementation of next and avoiding empty folds";
|
||||
|
||||
const TETRAPLET_IDX_CORRECT: &str = "selects always return a correct index inside stream";
|
||||
|
||||
pub use errors::CatchableError;
|
||||
pub use errors::ExecutionError;
|
||||
pub use errors::UncatchableError;
|
||||
|
@ -18,7 +18,7 @@ use super::RcSecurityTetraplets;
|
||||
use crate::execution_step::boxed_value::JValuable;
|
||||
use crate::execution_step::boxed_value::Variable;
|
||||
use crate::execution_step::execution_context::ExecutionCtx;
|
||||
use crate::execution_step::lambda_applier::select_from_scalar;
|
||||
use crate::execution_step::lambda_applier::select_by_lambda_from_scalar;
|
||||
use crate::execution_step::ExecutionResult;
|
||||
use crate::JValue;
|
||||
use crate::LambdaAST;
|
||||
@ -71,8 +71,8 @@ pub(crate) fn prepare_last_error<'i>(
|
||||
let LastError { error, tetraplet } = ctx.last_error();
|
||||
|
||||
let jvalue = match error_accessor {
|
||||
Some(error_accessor) => select_from_scalar(error.as_ref(), error_accessor.iter(), ctx)?,
|
||||
None => error.as_ref(),
|
||||
Some(error_accessor) => select_by_lambda_from_scalar(error.as_ref(), error_accessor, ctx)?.into_owned(),
|
||||
None => error.as_ref().clone(),
|
||||
};
|
||||
|
||||
let tetraplets = match tetraplet {
|
||||
@ -84,7 +84,7 @@ pub(crate) fn prepare_last_error<'i>(
|
||||
}
|
||||
};
|
||||
|
||||
Ok((jvalue.clone(), tetraplets))
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(ctx))]
|
||||
@ -157,5 +157,5 @@ pub(crate) fn apply_lambda<'i>(
|
||||
let (jvalue, tetraplet) = resolved.apply_lambda_with_tetraplets(lambda, exec_ctx)?;
|
||||
|
||||
// it's known that apply_lambda_with_tetraplets returns vec of one value
|
||||
Ok((jvalue.clone(), tetraplet))
|
||||
Ok((jvalue.into_owned(), tetraplet))
|
||||
}
|
||||
|
202
air/tests/test_module/features/lambda/functors.rs
Normal file
202
air/tests/test_module/features/lambda/functors.rs
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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::CatchableError;
|
||||
use air_test_framework::TestExecutor;
|
||||
use air_test_utils::prelude::*;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[test]
|
||||
fn length_functor_for_array_scalar() {
|
||||
let script = r#"
|
||||
(seq
|
||||
(call %init_peer_id% ("" "") [] variable) ; ok = [1,1,1]
|
||||
(call %init_peer_id% ("" "") [variable.length]) ; behaviour = echo
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = TestExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::scalar(json!([1, 1, 1])),
|
||||
executed_state::scalar_number(3),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_functor_for_non_array_scalar() {
|
||||
let result_jvalue = "string_jvalue";
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call %init_peer_id% ("" "") [] variable) ; ok = "{result_jvalue}"
|
||||
(call %init_peer_id% ("" "") [variable.length]) ; behaviour = echo
|
||||
)
|
||||
"#);
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = TestExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
check_error(
|
||||
&result,
|
||||
CatchableError::LengthFunctorAppliedToNotArray(json!(result_jvalue)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_functor_for_stream() {
|
||||
let script = r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap 1 $stream)
|
||||
(ap 1 $stream))
|
||||
(call %init_peer_id% ("" "") [$stream.length]) ; behaviour = echo
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = TestExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::scalar_number(2),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_functor_for_empty_stream() {
|
||||
let script = r#"
|
||||
(new $stream
|
||||
(call %init_peer_id% ("" "") [$stream.length]) ; behaviour = echo
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = TestExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_trace = vec![executed_state::scalar_number(0)];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_functor_for_canon_stream() {
|
||||
let script = r#"
|
||||
(seq
|
||||
(seq
|
||||
(ap 1 $stream)
|
||||
(ap 1 $stream))
|
||||
(seq
|
||||
(canon %init_peer_id% $stream #canon_stream)
|
||||
(call %init_peer_id% ("" "") [#canon_stream.length]) ; behaviour = echo
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = TestExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::ap(Some(0)),
|
||||
executed_state::canon(vec![0.into(), 1.into()]),
|
||||
executed_state::scalar_number(2),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length_functor_for_empty_canon_stream() {
|
||||
let script = r#"
|
||||
(new $stream
|
||||
(seq
|
||||
(canon %init_peer_id% $stream #canon_stream)
|
||||
(call %init_peer_id% ("" "") [#canon_stream.length]) ; behaviour = echo
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let init_peer_id = "init_peer_id";
|
||||
let executor = TestExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &script)
|
||||
.expect("invalid test AIR script");
|
||||
|
||||
let result = executor.execute_one(init_peer_id).unwrap();
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_trace = vec![executed_state::canon(vec![]), executed_state::scalar_number(0)];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn functor_dont_influence_tetraplet() {
|
||||
let set_variable_peer_id = "set_variable_peer_id";
|
||||
let set_variable_peer_result = json!({"field": [1,2,3]});
|
||||
let mut set_variable_vm = create_avm(
|
||||
set_variable_call_service(set_variable_peer_result.clone()),
|
||||
set_variable_peer_id,
|
||||
);
|
||||
|
||||
let tetraplet_catcher_peer_id = "tetraplet_catcher_peer_id";
|
||||
let (call_service, actual_tetraplet) = tetraplet_host_function(echo_call_service());
|
||||
let mut tetraplet_catcher_vm = create_avm(call_service, tetraplet_catcher_peer_id);
|
||||
|
||||
let script = f!(r#"
|
||||
(seq
|
||||
(call "{set_variable_peer_id}" ("" "") [] scalar)
|
||||
(seq
|
||||
(ap scalar.$.field field)
|
||||
(seq
|
||||
(ap field.length length)
|
||||
(call "{tetraplet_catcher_peer_id}" ("" "") [length])
|
||||
)
|
||||
)
|
||||
)
|
||||
"#);
|
||||
|
||||
let result = checked_call_vm!(set_variable_vm, <_>::default(), &script, "", "");
|
||||
let result = checked_call_vm!(tetraplet_catcher_vm, <_>::default(), &script, "", result.data);
|
||||
let actual_trace = trace_from_result(&result);
|
||||
|
||||
let expected_tetraplet = RefCell::new(vec![vec![SecurityTetraplet::new("", "", "", ".length")]]);
|
||||
assert_eq!(actual_tetraplet.as_ref(), &expected_tetraplet);
|
||||
|
||||
let expected_trace = vec![
|
||||
executed_state::scalar(set_variable_peer_result),
|
||||
executed_state::scalar_number(3),
|
||||
];
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
@ -15,4 +15,5 @@
|
||||
*/
|
||||
|
||||
mod flattening;
|
||||
mod functors;
|
||||
mod lambda;
|
||||
|
@ -134,7 +134,7 @@ fn fold_json_path() {
|
||||
peer_pk: set_variable_vm_peer_id,
|
||||
service_id,
|
||||
function_name,
|
||||
json_path: String::from(".args"),
|
||||
json_path: String::from(".$.args"),
|
||||
};
|
||||
|
||||
let second_arg_tetraplet = SecurityTetraplet {
|
||||
@ -180,14 +180,14 @@ fn check_tetraplet_works_correctly() {
|
||||
peer_pk: set_variable_vm_peer_id.clone(),
|
||||
service_id: service_id.clone(),
|
||||
function_name: function_name.clone(),
|
||||
json_path: String::from(".args"),
|
||||
json_path: String::from(".$.args"),
|
||||
};
|
||||
|
||||
let second_arg_tetraplet = SecurityTetraplet {
|
||||
peer_pk: set_variable_vm_peer_id.clone(),
|
||||
service_id,
|
||||
function_name,
|
||||
json_path: String::from(".args.[0]"),
|
||||
json_path: String::from(".$.args.[0]"),
|
||||
};
|
||||
|
||||
let expected_tetraplets = vec![vec![first_arg_tetraplet], vec![second_arg_tetraplet]];
|
||||
|
@ -249,7 +249,7 @@ fn ap_canon_stream_with_lambda() {
|
||||
vm_1_peer_id,
|
||||
service_name,
|
||||
function_name,
|
||||
".[1]",
|
||||
".$.[1]",
|
||||
)]]);
|
||||
assert_eq!(tetraplet_checker.as_ref(), &expected_tetraplet);
|
||||
}
|
||||
|
@ -147,12 +147,9 @@ impl From<&Number> for serde_json::Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn display_last_error(
|
||||
f: &mut fmt::Formatter,
|
||||
last_error_accessor: &Option<LambdaAST>,
|
||||
) -> fmt::Result {
|
||||
match last_error_accessor {
|
||||
Some(accessor) => write!(f, "%last_error%.${}", air_lambda_ast::format_ast(accessor)),
|
||||
fn display_last_error(f: &mut fmt::Formatter, lambda_ast: &Option<LambdaAST>) -> fmt::Result {
|
||||
match lambda_ast {
|
||||
Some(lambda_ast) => write!(f, "%last_error%{}", lambda_ast),
|
||||
None => write!(f, "%last_error%"),
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use air_lambda_ast::format_ast;
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Instruction<'_> {
|
||||
@ -77,7 +76,7 @@ impl fmt::Display for Fail<'_> {
|
||||
error_message,
|
||||
} => write!(f, r#"fail {} "{}""#, ret_code, error_message),
|
||||
Fail::CanonStream { name, lambda, .. } => {
|
||||
write!(f, "fail {}.$.{}", name, format_ast(lambda))
|
||||
write!(f, "fail {}.$.{}", name, lambda)
|
||||
}
|
||||
Fail::LastError => write!(f, "fail %last_error%"),
|
||||
}
|
||||
|
@ -15,14 +15,17 @@
|
||||
*/
|
||||
|
||||
use crate::ast::Value;
|
||||
use air_lambda_ast::LambdaAST;
|
||||
use air_lambda_ast::ValueAccessor;
|
||||
use non_empty_vec::NonEmpty;
|
||||
|
||||
#[test]
|
||||
// https://github.com/fluencelabs/aquavm/issues/263
|
||||
fn issue_263() {
|
||||
let val = Value::LastError(Some(NonEmpty::new(ValueAccessor::FieldAccessByName {
|
||||
let val = Value::LastError(Some(
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "message",
|
||||
})));
|
||||
}])
|
||||
.unwrap(),
|
||||
));
|
||||
assert_eq!(val.to_string(), "%last_error%.$.message");
|
||||
}
|
||||
|
@ -18,8 +18,6 @@ use super::*;
|
||||
use air_lambda_parser::LambdaAST;
|
||||
use air_lambda_parser::ValueAccessor;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl<'i> ScalarWithLambda<'i> {
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
|
||||
Self {
|
||||
@ -29,12 +27,12 @@ impl<'i> ScalarWithLambda<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_raw_lambda(
|
||||
pub(crate) fn from_value_path(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
accessors: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let lambda = LambdaAST::try_from(lambda).ok();
|
||||
let lambda = LambdaAST::try_from_accessors(accessors).ok();
|
||||
Self {
|
||||
name,
|
||||
lambda,
|
||||
@ -53,12 +51,12 @@ impl<'i> StreamWithLambda<'i> {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda(
|
||||
pub(crate) fn from_value_path(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
accessors: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let lambda = LambdaAST::try_from(lambda).ok();
|
||||
let lambda = LambdaAST::try_from_accessors(accessors).ok();
|
||||
Self {
|
||||
name,
|
||||
lambda,
|
||||
@ -155,12 +153,12 @@ impl<'i> VariableWithLambda<'i> {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda_scalar(
|
||||
pub(crate) fn from_raw_value_path(
|
||||
name: &'i str,
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let scalar = ScalarWithLambda::from_raw_lambda(name, lambda, position);
|
||||
let scalar = ScalarWithLambda::from_value_path(name, lambda, position);
|
||||
Self::Scalar(scalar)
|
||||
}
|
||||
|
||||
@ -170,7 +168,7 @@ impl<'i> VariableWithLambda<'i> {
|
||||
lambda: Vec<ValueAccessor<'i>>,
|
||||
position: usize,
|
||||
) -> Self {
|
||||
let stream = StreamWithLambda::from_raw_lambda(name, lambda, position);
|
||||
let stream = StreamWithLambda::from_value_path(name, lambda, position);
|
||||
Self::Stream(stream)
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use air_lambda_ast::format_ast;
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Scalar<'_> {
|
||||
@ -27,7 +26,7 @@ impl fmt::Display for Scalar<'_> {
|
||||
impl fmt::Display for ScalarWithLambda<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.lambda {
|
||||
Some(lambda) => write!(f, "{}.${}", self.name, format_ast(lambda)),
|
||||
Some(lambda) => write!(f, "{}{}", self.name, lambda),
|
||||
None => write!(f, "{}", self.name),
|
||||
}
|
||||
}
|
||||
@ -48,7 +47,7 @@ impl fmt::Display for CanonStream<'_> {
|
||||
impl fmt::Display for StreamWithLambda<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.lambda {
|
||||
Some(lambda) => write!(f, "{}.${}", self.name, format_ast(lambda)),
|
||||
Some(lambda) => write!(f, "{}{}", self.name, lambda),
|
||||
None => write!(f, "{}", self.name),
|
||||
}
|
||||
}
|
||||
@ -57,7 +56,7 @@ 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)),
|
||||
Some(lambda) => write!(f, "{}{}", self.name, lambda),
|
||||
None => write!(f, "{}", self.name),
|
||||
}
|
||||
}
|
||||
|
@ -207,7 +207,6 @@ fn parse_last_error(input: &str, start_pos: usize) -> LexerResult<Token<'_>> {
|
||||
return Ok(Token::LastError);
|
||||
}
|
||||
|
||||
let last_error_size = last_error_size + 2;
|
||||
if input.len() <= last_error_size {
|
||||
return Err(LexerError::lambda_parser_error(
|
||||
start_pos + last_error_size..start_pos + input.len(),
|
||||
|
@ -314,9 +314,8 @@ impl<'input> CallVariableParser<'input> {
|
||||
}
|
||||
|
||||
fn try_to_variable_and_lambda(&self, lambda_start_pos: usize) -> LexerResult<Token<'input>> {
|
||||
// +2 to ignore ".$" prefix
|
||||
let lambda =
|
||||
crate::parse_lambda(&self.string_to_parse[lambda_start_pos + 2..]).map_err(|e| {
|
||||
crate::parse_lambda(&self.string_to_parse[lambda_start_pos..]).map_err(|e| {
|
||||
LexerError::lambda_parser_error(
|
||||
self.start_pos + lambda_start_pos..self.start_pos + self.string_to_parse.len(),
|
||||
e.to_string(),
|
||||
|
@ -22,11 +22,10 @@ use super::Token;
|
||||
use air_lambda_parser::LambdaAST;
|
||||
use air_lambda_parser::ValueAccessor;
|
||||
|
||||
use air_lambda_ast::Functor;
|
||||
use fstrings::f;
|
||||
use fstrings::format_args_f;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
|
||||
let lexer = AIRLexer::new(input);
|
||||
lexer.collect()
|
||||
@ -194,6 +193,80 @@ fn stream() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn canon_stream() {
|
||||
const CANON_STREAM: &str = "#stream____asdasd";
|
||||
|
||||
lexer_test(
|
||||
CANON_STREAM,
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::CanonStream {
|
||||
name: CANON_STREAM,
|
||||
position: 0,
|
||||
},
|
||||
CANON_STREAM.len(),
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_with_functor() {
|
||||
let stream_name = "$stream";
|
||||
let stream_with_functor: String = f!("{stream_name}.length");
|
||||
|
||||
lexer_test(
|
||||
&stream_with_functor,
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::StreamWithLambda {
|
||||
name: stream_name,
|
||||
lambda: LambdaAST::Functor(Functor::Length),
|
||||
position: 0,
|
||||
},
|
||||
stream_with_functor.len(),
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn canon_stream_with_functor() {
|
||||
let canon_stream_name = "#canon_stream";
|
||||
let canon_stream_with_functor: String = f!("{canon_stream_name}.length");
|
||||
|
||||
lexer_test(
|
||||
&canon_stream_with_functor,
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::CanonStreamWithLambda {
|
||||
name: canon_stream_name,
|
||||
lambda: LambdaAST::Functor(Functor::Length),
|
||||
position: 0,
|
||||
},
|
||||
canon_stream_with_functor.len(),
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_with_functor() {
|
||||
let scalar_name = "scalar";
|
||||
let scalar_with_functor: String = f!("{scalar_name}.length");
|
||||
|
||||
lexer_test(
|
||||
&scalar_with_functor,
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::ScalarWithLambda {
|
||||
name: scalar_name,
|
||||
lambda: LambdaAST::Functor(Functor::Length),
|
||||
position: 0,
|
||||
},
|
||||
scalar_with_functor.len(),
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_literal() {
|
||||
const STRING_LITERAL: &str = r#""some_string""#;
|
||||
@ -308,7 +381,7 @@ fn lambda() {
|
||||
0,
|
||||
Token::ScalarWithLambda {
|
||||
name: "value",
|
||||
lambda: LambdaAST::try_from(vec![
|
||||
lambda: LambdaAST::try_from_accessors(vec![
|
||||
ValueAccessor::FieldAccessByName {
|
||||
field_name: "field",
|
||||
},
|
||||
@ -415,7 +488,7 @@ fn last_error_instruction() {
|
||||
const LAST_ERROR: &str = r#"%last_error%.$.instruction"#;
|
||||
|
||||
let token = Token::LastErrorWithLambda(
|
||||
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "instruction",
|
||||
}])
|
||||
.unwrap(),
|
||||
@ -429,7 +502,7 @@ fn last_error_message() {
|
||||
const LAST_ERROR: &str = r#"%last_error%.$.message"#;
|
||||
|
||||
let token = Token::LastErrorWithLambda(
|
||||
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "message",
|
||||
}])
|
||||
.unwrap(),
|
||||
@ -442,7 +515,7 @@ fn last_error_peer_id() {
|
||||
const LAST_ERROR: &str = r#"%last_error%.$.peer_id"#;
|
||||
|
||||
let token = Token::LastErrorWithLambda(
|
||||
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "peer_id",
|
||||
}])
|
||||
.unwrap(),
|
||||
@ -455,7 +528,7 @@ fn last_error_non_standard_field() {
|
||||
const LAST_ERROR: &str = r#"%last_error%.$.asdasd"#;
|
||||
|
||||
let token = Token::LastErrorWithLambda(
|
||||
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "asdasd",
|
||||
}])
|
||||
.unwrap(),
|
||||
|
@ -22,8 +22,6 @@ use air_lambda_ast::{LambdaAST, ValueAccessor};
|
||||
use fstrings::f;
|
||||
use fstrings::format_args_f;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn ap_with_literal() {
|
||||
let source_code = r#"
|
||||
@ -78,7 +76,7 @@ fn ap_with_last_error() {
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::LastError(Some(
|
||||
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "message",
|
||||
}])
|
||||
.unwrap(),
|
||||
@ -178,7 +176,9 @@ fn ap_with_canon_stream_with_lambda() {
|
||||
let expected = ap(
|
||||
ApArgument::CanonStream(CanonStreamWithLambda::new(
|
||||
canon_stream,
|
||||
Some(LambdaAST::try_from(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap()),
|
||||
Some(
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap(),
|
||||
),
|
||||
13,
|
||||
)),
|
||||
ApResult::Scalar(Scalar::new(scalar, 33)),
|
||||
|
@ -24,7 +24,6 @@ use fstrings::f;
|
||||
use fstrings::format_args_f;
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
@ -35,7 +34,7 @@ fn parse_json_path() {
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"peer_id",
|
||||
vec![ValueAccessor::FieldAccessByName { field_name: "a" }],
|
||||
15,
|
||||
@ -188,7 +187,7 @@ fn parse_lambda_complex() {
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"m",
|
||||
vec![ValueAccessor::ArrayAccess { idx: 1 }],
|
||||
32,
|
||||
@ -199,7 +198,7 @@ fn parse_lambda_complex() {
|
||||
CallOutputValue::Scalar(Scalar::new("void", 75)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"m",
|
||||
vec![
|
||||
ValueAccessor::FieldAccessByName { field_name: "abc" },
|
||||
@ -232,7 +231,7 @@ fn parse_lambda_with_scalars_complex() {
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"m",
|
||||
vec![
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
@ -251,7 +250,7 @@ fn parse_lambda_with_scalars_complex() {
|
||||
CallOutputValue::Scalar(Scalar::new("void", 97)),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"m",
|
||||
vec![
|
||||
ValueAccessor::FieldAccessByName { field_name: "abc" },
|
||||
@ -286,7 +285,7 @@ fn json_path_square_braces() {
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"u",
|
||||
vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "peer_id",
|
||||
@ -296,7 +295,7 @@ fn json_path_square_braces() {
|
||||
CallInstrValue::Literal("return"),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![
|
||||
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
Value::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"u",
|
||||
vec![
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
@ -307,7 +306,7 @@ fn json_path_square_braces() {
|
||||
],
|
||||
43,
|
||||
)),
|
||||
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
Value::Variable(VariableWithLambda::from_raw_value_path(
|
||||
"u",
|
||||
vec![ValueAccessor::FieldAccessByName { field_name: "name" }],
|
||||
64,
|
||||
@ -472,7 +471,7 @@ fn canon_stream_with_lambda_in_triplet() {
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(
|
||||
canon_stream,
|
||||
LambdaAST::try_from(vec![
|
||||
LambdaAST::try_from_accessors(vec![
|
||||
ValueAccessor::ArrayAccess { idx: 0 },
|
||||
ValueAccessor::FieldAccessByName { field_name: "path" },
|
||||
])
|
||||
|
@ -21,8 +21,6 @@ use crate::ast::ScalarWithLambda;
|
||||
use air_lambda_ast::LambdaAST;
|
||||
use air_lambda_ast::ValueAccessor;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn parse_fail_last_error() {
|
||||
let source_code = r#"
|
||||
@ -62,7 +60,7 @@ fn parse_fail_scalar_with_lambda() {
|
||||
let expected = fail_scalar(ScalarWithLambda::new(
|
||||
"scalar",
|
||||
Some(
|
||||
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::FieldAccessByName {
|
||||
field_name: "field_accessor",
|
||||
}])
|
||||
.unwrap(),
|
||||
|
@ -290,7 +290,7 @@ fn fold_json_path() {
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = fold_scalar_variable(
|
||||
ScalarWithLambda::from_raw_lambda(
|
||||
ScalarWithLambda::from_value_path(
|
||||
"members",
|
||||
vec![ValueAccessor::ArrayAccess { idx: 123321 }],
|
||||
33,
|
||||
@ -358,7 +358,7 @@ fn comments() {
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = fold_scalar_variable(
|
||||
ScalarWithLambda::from_raw_lambda(
|
||||
ScalarWithLambda::from_value_path(
|
||||
"members",
|
||||
vec![
|
||||
ValueAccessor::FieldAccessByName {
|
||||
|
@ -22,8 +22,6 @@ use air_lambda_ast::{LambdaAST, ValueAccessor};
|
||||
use fstrings::f;
|
||||
use fstrings::format_args_f;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn parse_match() {
|
||||
let source_code = r#"
|
||||
@ -54,7 +52,7 @@ fn parse_match_with_canon_stream() {
|
||||
let expected = match_(
|
||||
Value::Variable(VariableWithLambda::canon_stream_wl(
|
||||
canon_stream,
|
||||
LambdaAST::try_from(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap(),
|
||||
LambdaAST::try_from_accessors(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap(),
|
||||
16,
|
||||
)),
|
||||
Value::Variable(VariableWithLambda::scalar("v2", 36)),
|
||||
|
@ -207,7 +207,12 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
|
||||
fn met_lambda(&mut self, lambda: &LambdaAST<'i>, span: Span) {
|
||||
for accessor in lambda.iter() {
|
||||
let accessors = match lambda {
|
||||
LambdaAST::ValuePath(accessors) => accessors,
|
||||
LambdaAST::Functor(_) => return,
|
||||
};
|
||||
|
||||
for accessor in accessors.iter() {
|
||||
match accessor {
|
||||
&ValueAccessor::FieldAccessByScalar { scalar_name } => {
|
||||
self.met_variable_name(scalar_name, span)
|
||||
|
@ -15,6 +15,7 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
non-empty-vec = { version = "0.2.3", features = ["serde"] }
|
||||
itertools = "0.10.0"
|
||||
|
||||
serde = { version = "1.0.144", features = ["rc", "derive"] }
|
||||
serde_json = "1.0.85"
|
||||
|
@ -14,13 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod impls;
|
||||
mod traits;
|
||||
|
||||
use non_empty_vec::NonEmpty;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
pub type LambdaAST<'input> = NonEmpty<ValueAccessor<'input>>;
|
||||
// TODO: rename lambda to smth more appropriate
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum LambdaAST<'input> {
|
||||
/// Various functors that could applied to a value.
|
||||
Functor(Functor),
|
||||
/// Each value in AIR could be represented as a tree and
|
||||
/// this variant acts as a path in such trees.
|
||||
#[serde(borrow)]
|
||||
ValuePath(NonEmpty<ValueAccessor<'input>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum ValueAccessor<'input> {
|
||||
@ -37,3 +47,10 @@ pub enum ValueAccessor<'input> {
|
||||
// on the very first one. Although, this variant is guaranteed not to be present in a lambda.
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum Functor {
|
||||
/// Returns a length of a value if this value has array type (json array or canon stream)
|
||||
/// or a error if not.
|
||||
Length,
|
||||
}
|
||||
|
37
crates/air-lib/lambda/ast/src/ast/impls.rs
Normal file
37
crates/air-lib/lambda/ast/src/ast/impls.rs
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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::Functor;
|
||||
use crate::LambdaAST;
|
||||
use crate::ValueAccessor;
|
||||
|
||||
pub use non_empty_vec::EmptyError;
|
||||
use non_empty_vec::NonEmpty;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
impl<'input> LambdaAST<'input> {
|
||||
pub fn try_from_accessors(accessors: Vec<ValueAccessor<'input>>) -> Result<Self, EmptyError> {
|
||||
let value_path = NonEmpty::try_from(accessors)?;
|
||||
let lambda_ast = Self::ValuePath(value_path);
|
||||
|
||||
Ok(lambda_ast)
|
||||
}
|
||||
|
||||
pub fn from_functor(functor: Functor) -> Self {
|
||||
Self::Functor(functor)
|
||||
}
|
||||
}
|
@ -15,18 +15,40 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for LambdaAST<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use LambdaAST::*;
|
||||
|
||||
match self {
|
||||
Functor(functor) => write!(f, ".{}", functor),
|
||||
ValuePath(value_path) => write!(f, ".$.{}", value_path.iter().join(".")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ValueAccessor<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use ValueAccessor::*;
|
||||
|
||||
match self {
|
||||
ArrayAccess { idx } => write!(f, ".[{}]", idx),
|
||||
FieldAccessByName { field_name } => write!(f, ".{}", field_name),
|
||||
FieldAccessByScalar { scalar_name } => write!(f, ".[{}]", scalar_name),
|
||||
ArrayAccess { idx } => write!(f, "[{}]", idx),
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Functor {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use Functor::*;
|
||||
|
||||
match self {
|
||||
Length => write!(f, "length"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,3 @@
|
||||
mod ast;
|
||||
|
||||
pub use ast::*;
|
||||
|
||||
pub fn format_ast(lambda_ast: &LambdaAST<'_>) -> String {
|
||||
let mut formatted_ast = String::new();
|
||||
for accessor in lambda_ast.iter() {
|
||||
formatted_ast.push_str(&accessor.to_string());
|
||||
}
|
||||
|
||||
formatted_ast
|
||||
}
|
||||
|
@ -28,10 +28,10 @@
|
||||
mod parser;
|
||||
|
||||
pub use parser::parse;
|
||||
pub use parser::AccessorsLexer;
|
||||
pub use parser::LambdaParser;
|
||||
pub use parser::LambdaASTLexer;
|
||||
pub use parser::LambdaParserError;
|
||||
pub use parser::LexerError;
|
||||
|
||||
pub use air_lambda_ast::Functor;
|
||||
pub use air_lambda_ast::LambdaAST;
|
||||
pub use air_lambda_ast::ValueAccessor;
|
||||
|
@ -26,8 +26,8 @@ pub enum LambdaParserError<'input> {
|
||||
#[error(transparent)]
|
||||
LexerError(#[from] LexerError),
|
||||
|
||||
#[error("provided lambda expression doesn't contain any accessor")]
|
||||
EmptyLambda,
|
||||
#[error(transparent)]
|
||||
LambdaError(#[from] IncorrectLambdaError),
|
||||
|
||||
#[error("{0:?}")]
|
||||
ParseError(ParseError<usize, Token<'input>, LexerError>),
|
||||
@ -36,6 +36,17 @@ pub enum LambdaParserError<'input> {
|
||||
RecoveryErrors(Vec<ErrorRecovery<usize, Token<'input>, LexerError>>),
|
||||
}
|
||||
|
||||
#[derive(ThisError, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum IncorrectLambdaError {
|
||||
#[error("provided lambda expression doesn't contain any accessor")]
|
||||
EmptyLambda,
|
||||
|
||||
#[error(
|
||||
"normally, this error shouldn't occur, it's an internal error of a parser implementation"
|
||||
)]
|
||||
InternalError,
|
||||
}
|
||||
|
||||
impl<'input> From<ParseError<usize, Token<'input>, LexerError>> for LambdaParserError<'input> {
|
||||
fn from(e: ParseError<usize, Token<'input>, LexerError>) -> Self {
|
||||
Self::ParseError(e)
|
||||
|
@ -14,36 +14,54 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::lexer::AccessorsLexer;
|
||||
use super::va_lambda;
|
||||
use super::LambdaParserError;
|
||||
use super::lexer::LambdaASTLexer;
|
||||
use super::LambdaParserResult;
|
||||
use crate::parser::errors::IncorrectLambdaError;
|
||||
use crate::parser::va_lambda::RawLambdaASTParser;
|
||||
use crate::Functor;
|
||||
use crate::LambdaAST;
|
||||
use crate::ValueAccessor;
|
||||
|
||||
use va_lambda::LambdaParser;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
|
||||
// Caching parser to cache internal regexes, which are expensive to instantiate
|
||||
// See also https://github.com/lalrpop/lalrpop/issues/269
|
||||
thread_local!(static PARSER: LambdaParser = LambdaParser::new());
|
||||
thread_local!(static PARSER: RawLambdaASTParser = RawLambdaASTParser::new());
|
||||
|
||||
/// Parse AIR `source_code` to `Box<Instruction>`
|
||||
/// Parse AIR lambda ast to `LambdaAST`
|
||||
pub fn parse(lambda: &str) -> LambdaParserResult<'_, LambdaAST> {
|
||||
PARSER.with(|parser| {
|
||||
let mut errors = Vec::new();
|
||||
let lexer = AccessorsLexer::new(lambda);
|
||||
let lexer = LambdaASTLexer::new(lambda);
|
||||
let result = parser.parse(lambda, &mut errors, lexer);
|
||||
|
||||
match result {
|
||||
Ok(accessors) if errors.is_empty() => try_to_lambda(accessors),
|
||||
Ok(lambda_ast) if errors.is_empty() => lambda_ast.try_into().map_err(Into::into),
|
||||
Ok(_) => Err(errors.into()),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
|
||||
LambdaAST::try_from(accessors).or(Err(LambdaParserError::EmptyLambda))
|
||||
impl<'input> TryFrom<RawLambdaAST<'input>> for LambdaAST<'input> {
|
||||
type Error = IncorrectLambdaError;
|
||||
|
||||
fn try_from(raw_lambda_ast: RawLambdaAST<'input>) -> Result<Self, Self::Error> {
|
||||
match raw_lambda_ast {
|
||||
RawLambdaAST::ValuePath(accessors) => {
|
||||
LambdaAST::try_from_accessors(accessors).or(Err(IncorrectLambdaError::EmptyLambda))
|
||||
}
|
||||
RawLambdaAST::Functor(functor) => Ok(LambdaAST::from_functor(functor)),
|
||||
RawLambdaAST::Error => Err(IncorrectLambdaError::InternalError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub(crate) enum RawLambdaAST<'input> {
|
||||
Functor(Functor),
|
||||
ValuePath(Vec<ValueAccessor<'input>>),
|
||||
// needed to allow parser catch all errors from a lambda expression without stopping on the very first one.
|
||||
Error,
|
||||
}
|
||||
|
@ -16,21 +16,24 @@
|
||||
|
||||
use super::errors::LexerError;
|
||||
use super::token::Token;
|
||||
|
||||
use crate::parser::lexer::is_air_alphanumeric;
|
||||
|
||||
use std::iter::Peekable;
|
||||
use std::str::CharIndices;
|
||||
|
||||
const ARRAY_IDX_BASE: u32 = 10;
|
||||
const LENGTH_FUNCTOR: &str = ".length";
|
||||
const VALUE_PATH_STARTER: &str = ".$";
|
||||
|
||||
pub type Spanned<Token, Loc, Error> = Result<(Loc, Token, Loc), Error>;
|
||||
|
||||
pub struct AccessorsLexer<'input> {
|
||||
pub struct LambdaASTLexer<'input> {
|
||||
input: &'input str,
|
||||
chars: Peekable<CharIndices<'input>>,
|
||||
is_first_token: bool,
|
||||
}
|
||||
|
||||
impl<'input> Iterator for AccessorsLexer<'input> {
|
||||
impl<'input> Iterator for LambdaASTLexer<'input> {
|
||||
type Item = Spanned<Token<'input>, usize, LexerError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@ -38,20 +41,30 @@ impl<'input> Iterator for AccessorsLexer<'input> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input> AccessorsLexer<'input> {
|
||||
impl<'input> LambdaASTLexer<'input> {
|
||||
pub fn new(input: &'input str) -> Self {
|
||||
Self {
|
||||
input,
|
||||
chars: input.char_indices().peekable(),
|
||||
is_first_token: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_token(&mut self) -> Option<Spanned<Token<'input>, usize, LexerError>> {
|
||||
if self.input.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.is_first_token {
|
||||
self.is_first_token = false;
|
||||
return Some(self.try_parse_first_token());
|
||||
}
|
||||
|
||||
self.chars.next().map(|(start_pos, ch)| match ch {
|
||||
'[' => Ok((start_pos, Token::OpenSquareBracket, start_pos + 1)),
|
||||
']' => Ok((start_pos, Token::CloseSquareBracket, start_pos + 1)),
|
||||
|
||||
'.' => Ok((start_pos, Token::Selector, start_pos + 1)),
|
||||
'.' => Ok((start_pos, Token::ValuePathSelector, start_pos + 1)),
|
||||
|
||||
d if d.is_digit(ARRAY_IDX_BASE) => self.tokenize_arrays_idx(start_pos),
|
||||
s if is_air_alphanumeric(s) => self.tokenize_field_name(start_pos),
|
||||
@ -109,4 +122,24 @@ impl<'input> AccessorsLexer<'input> {
|
||||
|
||||
&self.input[start_pos..end_pos + 1]
|
||||
}
|
||||
|
||||
fn try_parse_first_token(&mut self) -> Spanned<Token<'input>, usize, LexerError> {
|
||||
let (token, token_size) = if self.input == LENGTH_FUNCTOR {
|
||||
(Token::LengthFunctor, LENGTH_FUNCTOR.len())
|
||||
} else if self.input.starts_with(VALUE_PATH_STARTER) {
|
||||
(Token::ValuePathStarter, VALUE_PATH_STARTER.len())
|
||||
} else {
|
||||
return Err(LexerError::UnexpectedSymbol(0, self.input.len()));
|
||||
};
|
||||
|
||||
self.advance_by(token_size);
|
||||
Ok((0, token, token_size))
|
||||
}
|
||||
|
||||
fn advance_by(&mut self, advance_size: usize) {
|
||||
// advance_by is unstable
|
||||
for _ in 0..advance_size {
|
||||
self.chars.next();
|
||||
}
|
||||
}
|
||||
}
|
@ -14,16 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod accessors_lexer;
|
||||
mod errors;
|
||||
mod lambda_ast_lexer;
|
||||
mod token;
|
||||
mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use accessors_lexer::AccessorsLexer;
|
||||
pub use errors::LexerError;
|
||||
pub use lambda_ast_lexer::LambdaASTLexer;
|
||||
pub use token::Token;
|
||||
|
||||
pub(self) use utils::is_air_alphanumeric;
|
||||
|
@ -14,26 +14,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::accessors_lexer::Spanned;
|
||||
use super::AccessorsLexer;
|
||||
use super::lambda_ast_lexer::Spanned;
|
||||
use super::LambdaASTLexer;
|
||||
use super::LexerError;
|
||||
use super::Token;
|
||||
|
||||
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
|
||||
let lexer = AccessorsLexer::new(input);
|
||||
let lexer = LambdaASTLexer::new(input);
|
||||
lexer.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn array_access() {
|
||||
let array_access: &str = ".[0]";
|
||||
let array_access: &str = ".$.[0]";
|
||||
|
||||
let actual = run_lexer(array_access);
|
||||
let expected = vec![
|
||||
Spanned::Ok((0, Token::Selector, 1)),
|
||||
Spanned::Ok((1, Token::OpenSquareBracket, 2)),
|
||||
Spanned::Ok((2, Token::NumberAccessor(0), 3)),
|
||||
Spanned::Ok((3, Token::CloseSquareBracket, 4)),
|
||||
Spanned::Ok((0, Token::ValuePathStarter, 2)),
|
||||
Spanned::Ok((2, Token::ValuePathSelector, 3)),
|
||||
Spanned::Ok((3, Token::OpenSquareBracket, 4)),
|
||||
Spanned::Ok((4, Token::NumberAccessor(0), 5)),
|
||||
Spanned::Ok((5, Token::CloseSquareBracket, 6)),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -41,12 +42,13 @@ fn array_access() {
|
||||
#[test]
|
||||
fn field_access() {
|
||||
let field_name = "some_field_name";
|
||||
let field_access = format!(".{}", field_name);
|
||||
let field_access = format!(".$.{}", field_name);
|
||||
|
||||
let actual = run_lexer(&field_access);
|
||||
let expected = vec![
|
||||
Spanned::Ok((0, Token::Selector, 1)),
|
||||
Spanned::Ok((1, Token::StringAccessor(field_name), 1 + field_name.len())),
|
||||
Spanned::Ok((0, Token::ValuePathStarter, 2)),
|
||||
Spanned::Ok((2, Token::ValuePathSelector, 3)),
|
||||
Spanned::Ok((3, Token::StringAccessor(field_name), 3 + field_name.len())),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
@ -19,8 +19,12 @@ use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Token<'input> {
|
||||
LengthFunctor,
|
||||
|
||||
//.$
|
||||
ValuePathStarter,
|
||||
// .
|
||||
Selector,
|
||||
ValuePathSelector,
|
||||
|
||||
OpenSquareBracket,
|
||||
CloseSquareBracket,
|
||||
|
@ -31,9 +31,8 @@ pub type LambdaParserResult<'input, T> = std::result::Result<T, LambdaParserErro
|
||||
|
||||
pub use errors::LambdaParserError;
|
||||
pub use lambda_parser::parse;
|
||||
pub use lexer::AccessorsLexer;
|
||||
pub use lexer::LambdaASTLexer;
|
||||
pub use lexer::LexerError;
|
||||
pub use va_lambda::LambdaParser;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
|
@ -14,27 +14,45 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::parser::LambdaParser;
|
||||
use crate::parser::lambda_parser::RawLambdaAST;
|
||||
use crate::parser::va_lambda::RawLambdaASTParser;
|
||||
use crate::ValueAccessor;
|
||||
use air_lambda_ast::Functor;
|
||||
|
||||
thread_local!(static TEST_PARSER: LambdaParser = LambdaParser::new());
|
||||
thread_local!(static TEST_PARSER: RawLambdaASTParser = RawLambdaASTParser::new());
|
||||
|
||||
fn parse(source_code: &str) -> Vec<ValueAccessor<'_>> {
|
||||
fn parse(source_code: &str) -> RawLambdaAST<'_> {
|
||||
TEST_PARSER.with(|parser| {
|
||||
let mut errors = Vec::new();
|
||||
let lexer = crate::parser::AccessorsLexer::new(source_code);
|
||||
let lexer = crate::parser::LambdaASTLexer::new(source_code);
|
||||
parser
|
||||
.parse(source_code, &mut errors, lexer)
|
||||
.expect("parsing should be successful")
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_to_accessors(source_code: &str) -> Vec<ValueAccessor<'_>> {
|
||||
let lambda_ast = parse(source_code);
|
||||
match lambda_ast {
|
||||
RawLambdaAST::ValuePath(accessors) => accessors,
|
||||
_ => panic!("it should be a value path"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_to_functor(source_code: &str) -> Functor {
|
||||
let lambda_ast = parse(source_code);
|
||||
match lambda_ast {
|
||||
RawLambdaAST::Functor(functor) => functor,
|
||||
_ => panic!("it should be a functor"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_access() {
|
||||
let field_name = "some_field_name";
|
||||
let lambda = format!(".{}", field_name);
|
||||
let lambda = format!(".$.{}", field_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![ValueAccessor::FieldAccessByName { field_name }];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -42,9 +60,9 @@ fn field_access() {
|
||||
#[test]
|
||||
fn field_access_with_flattening() {
|
||||
let field_name = "some_field_name";
|
||||
let lambda = format!(".{}!", field_name);
|
||||
let lambda = format!(".$.{}!", field_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![ValueAccessor::FieldAccessByName { field_name }];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -52,9 +70,9 @@ fn field_access_with_flattening() {
|
||||
#[test]
|
||||
fn array_access() {
|
||||
let idx = 0;
|
||||
let lambda = format!(".[{}]", idx);
|
||||
let lambda = format!(".$.[{}]", idx);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![ValueAccessor::ArrayAccess { idx }];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -62,9 +80,9 @@ fn array_access() {
|
||||
#[test]
|
||||
fn array_access_with_flattening() {
|
||||
let idx = 0;
|
||||
let lambda = format!(".[{}]!", idx);
|
||||
let lambda = format!(".$.[{}]!", idx);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![ValueAccessor::ArrayAccess { idx }];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -72,9 +90,9 @@ fn array_access_with_flattening() {
|
||||
#[test]
|
||||
fn scalar_access() {
|
||||
let scalar_name = "some_field_name";
|
||||
let lambda = format!(".[{}]", scalar_name);
|
||||
let lambda = format!(".$.[{}]", scalar_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![ValueAccessor::FieldAccessByScalar { scalar_name }];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -82,9 +100,9 @@ fn scalar_access() {
|
||||
#[test]
|
||||
fn scalar_access_with_flattening() {
|
||||
let scalar_name = "some_scalar_name";
|
||||
let lambda = format!(".[{}]!", scalar_name);
|
||||
let lambda = format!(".$.[{}]!", scalar_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![ValueAccessor::FieldAccessByScalar { scalar_name }];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
@ -93,9 +111,9 @@ fn scalar_access_with_flattening() {
|
||||
fn field_array_access() {
|
||||
let field_name = "some_field_name";
|
||||
let idx = 1;
|
||||
let lambda = format!(".{}.[{}]", field_name, idx);
|
||||
let lambda = format!(".$.{}.[{}]", field_name, idx);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::FieldAccessByName { field_name },
|
||||
ValueAccessor::ArrayAccess { idx },
|
||||
@ -107,9 +125,9 @@ fn field_array_access() {
|
||||
fn field_scalar_access() {
|
||||
let field_name = "some_field_name";
|
||||
let scalar_name = "some_scalar_name";
|
||||
let lambda = format!(".{}.[{}]", field_name, scalar_name);
|
||||
let lambda = format!(".$.{}.[{}]", field_name, scalar_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::FieldAccessByName { field_name },
|
||||
ValueAccessor::FieldAccessByScalar { scalar_name },
|
||||
@ -121,9 +139,9 @@ fn field_scalar_access() {
|
||||
fn scalar_array_access() {
|
||||
let scalar_name = "some_scalar_name";
|
||||
let idx = 1;
|
||||
let lambda = format!(".[{}].[{}]", scalar_name, idx);
|
||||
let lambda = format!(".$.[{}].[{}]", scalar_name, idx);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::FieldAccessByScalar { scalar_name },
|
||||
ValueAccessor::ArrayAccess { idx },
|
||||
@ -135,9 +153,9 @@ fn scalar_array_access() {
|
||||
fn field_array_access_without_dot() {
|
||||
let field_name = "some_field_name";
|
||||
let idx = 1;
|
||||
let lambda = format!(".{}[{}]", field_name, idx);
|
||||
let lambda = format!(".$.{}[{}]", field_name, idx);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::FieldAccessByName { field_name },
|
||||
ValueAccessor::ArrayAccess { idx },
|
||||
@ -149,9 +167,9 @@ fn field_array_access_without_dot() {
|
||||
fn array_field_access() {
|
||||
let field_name = "some_field_name";
|
||||
let idx = 1;
|
||||
let lambda = format!(".[{}].{}", idx, field_name);
|
||||
let lambda = format!(".$.[{}].{}", idx, field_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::ArrayAccess { idx },
|
||||
ValueAccessor::FieldAccessByName { field_name },
|
||||
@ -163,9 +181,9 @@ fn array_field_access() {
|
||||
fn array_scalar_access() {
|
||||
let scalar_name = "some_scalar_name";
|
||||
let idx = 1;
|
||||
let lambda = format!(".[{}].[{}]", idx, scalar_name);
|
||||
let lambda = format!(".$.[{}].[{}]", idx, scalar_name);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::ArrayAccess { idx },
|
||||
ValueAccessor::FieldAccessByScalar { scalar_name },
|
||||
@ -179,9 +197,12 @@ fn many_array_field_access() {
|
||||
let field_name_2 = "some_field_name_2";
|
||||
let idx_1 = 1;
|
||||
let idx_2 = u32::MAX;
|
||||
let lambda = format!(".[{}].{}.[{}].{}", idx_1, field_name_1, idx_2, field_name_2);
|
||||
let lambda = format!(
|
||||
".$.[{}].{}.[{}].{}",
|
||||
idx_1, field_name_1, idx_2, field_name_2
|
||||
);
|
||||
|
||||
let actual = parse(&lambda);
|
||||
let actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::ArrayAccess { idx: idx_1 },
|
||||
ValueAccessor::FieldAccessByName {
|
||||
@ -204,11 +225,11 @@ fn many_array_field_scalar_access() {
|
||||
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 actual = parse_to_accessors(&lambda);
|
||||
let expected = vec![
|
||||
ValueAccessor::ArrayAccess { idx: idx_1 },
|
||||
ValueAccessor::FieldAccessByScalar {
|
||||
@ -227,3 +248,25 @@ fn many_array_field_scalar_access() {
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_length_functor() {
|
||||
let lambda = ".length";
|
||||
|
||||
let actual = parse_to_functor(&lambda);
|
||||
let expected = Functor::Length;
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_length_functor_with_following_accessors() {
|
||||
let lambda = ".length.[0]";
|
||||
|
||||
let actual = TEST_PARSER.with(|parser| {
|
||||
let mut errors = Vec::new();
|
||||
let lexer = crate::parser::LambdaASTLexer::new(lambda);
|
||||
parser.parse(lambda, &mut errors, lexer)
|
||||
});
|
||||
|
||||
assert!(matches!(actual, Err(lalrpop_util::ParseError::User { .. })))
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::ValueAccessor;
|
||||
use crate::parser::lambda_parser::RawLambdaAST;
|
||||
use crate::Functor;
|
||||
use crate::parser::lexer::LexerError;
|
||||
use crate::parser::lexer::Token;
|
||||
|
||||
@ -7,7 +9,13 @@ use lalrpop_util::ErrorRecovery;
|
||||
// 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>>);
|
||||
|
||||
pub Lambda: Vec<ValueAccessor<'input>> = <ValueAccessor*> => <>;
|
||||
pub(crate) RawLambdaAST: RawLambdaAST<'input> = {
|
||||
<value_path_starter: ".$"> <accessors: ValueAccessor*> => RawLambdaAST::ValuePath(accessors),
|
||||
|
||||
length_functor => RawLambdaAST::Functor(Functor::Length),
|
||||
|
||||
! => { errors.push(<>); RawLambdaAST::Error },
|
||||
}
|
||||
|
||||
ValueAccessor: ValueAccessor<'input> = {
|
||||
<maybe_dot_selector:"."?> "[" <idx: number_accessor> "]" <maybe_flatten_sign:"!"?> => {
|
||||
@ -30,7 +38,8 @@ extern {
|
||||
type Error = LexerError;
|
||||
|
||||
enum Token<'input> {
|
||||
"." => Token::Selector,
|
||||
".$" => Token::ValuePathStarter,
|
||||
"." => Token::ValuePathSelector,
|
||||
|
||||
"[" => Token::OpenSquareBracket,
|
||||
"]" => Token::CloseSquareBracket,
|
||||
@ -39,5 +48,7 @@ extern {
|
||||
string_accessor => Token::StringAccessor(<&'input str>),
|
||||
|
||||
"!" => Token::FlatteningSign,
|
||||
|
||||
length_functor => Token::LengthFunctor,
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -164,7 +164,7 @@ mod tests {
|
||||
&avm_outcome[..],
|
||||
);
|
||||
|
||||
let anomaly: AnomalyData =
|
||||
let anomaly: AnomalyData<'_> =
|
||||
serde_json::from_str(&json_data).expect("deserialize JSON anomaly data");
|
||||
|
||||
assert_eq!(
|
||||
|
@ -18,6 +18,7 @@ use super::{Call, Sexp};
|
||||
use crate::{asserts::ServiceDefinition, ephemeral::PeerId};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Transformer {
|
||||
@ -57,7 +58,9 @@ impl Transformer {
|
||||
self.results.insert(call_id, service.clone());
|
||||
|
||||
match &mut call.triplet.1 {
|
||||
Sexp::String(ref mut value) => value.push_str(&format!("..{}", call_id)),
|
||||
Sexp::String(ref mut value) => {
|
||||
write!(value, "..{}", call_id).unwrap();
|
||||
}
|
||||
_ => panic!("Incorrect script: non-string service string not supported"),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user