mirror of
https://github.com/fluencelabs/aquavm
synced 2025-06-25 12:41:33 +00:00
Refactor AIR parser (#168)
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -114,6 +114,7 @@ version = "0.1.0"
|
||||
name = "air-parser"
|
||||
version = "0.7.1"
|
||||
dependencies = [
|
||||
"air-lambda-ast",
|
||||
"air-lambda-parser",
|
||||
"codespan",
|
||||
"codespan-reporting",
|
||||
|
@ -30,10 +30,8 @@ use crate::SecurityTetraplet;
|
||||
use apply_to_arguments::*;
|
||||
use utils::*;
|
||||
|
||||
use air_parser::ast::ApArgument;
|
||||
use air_parser::ast::AstVariable;
|
||||
use air_parser::ast::VariableWithLambda;
|
||||
use air_parser::ast::{Ap, LastErrorPath};
|
||||
use air_parser::ast;
|
||||
use air_parser::ast::Ap;
|
||||
use air_trace_handler::MergerApResult;
|
||||
|
||||
use std::cell::RefCell;
|
||||
@ -45,7 +43,7 @@ impl<'i> super::ExecutableInstruction<'i> for Ap<'i> {
|
||||
|
||||
let merger_ap_result = if should_touch_trace {
|
||||
let merger_ap_result = trace_to_exec_err!(trace_ctx.meet_ap_start())?;
|
||||
try_match_result_to_instr(&merger_ap_result, self)?;
|
||||
try_match_trace_to_instr(&merger_ap_result, self)?;
|
||||
merger_ap_result
|
||||
} else {
|
||||
MergerApResult::Empty
|
||||
@ -66,27 +64,24 @@ impl<'i> super::ExecutableInstruction<'i> for Ap<'i> {
|
||||
}
|
||||
|
||||
fn save_result<'ctx>(
|
||||
ap_result_type: &AstVariable<'ctx>,
|
||||
ap_result_type: &ast::Variable<'ctx>,
|
||||
merger_ap_result: &MergerApResult,
|
||||
result: ValueAggregate,
|
||||
exec_ctx: &mut ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<()> {
|
||||
use ast::Variable::*;
|
||||
|
||||
match ap_result_type {
|
||||
AstVariable::Scalar(name) => exec_ctx.scalars.set_value(*name, result).map(|_| ()),
|
||||
AstVariable::Stream(name) => {
|
||||
Scalar(scalar) => exec_ctx.scalars.set_value(scalar.name, result).map(|_| ()),
|
||||
Stream(stream) => {
|
||||
let generation = ap_result_to_generation(merger_ap_result);
|
||||
set_stream_result(result, generation, name.to_string(), exec_ctx).map(|_| ())
|
||||
set_stream_result(result, generation, stream.name.to_string(), exec_ctx).map(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is intended to check whether a Ap instruction should produce
|
||||
/// a new state in data.
|
||||
fn should_touch_trace(ap: &Ap<'_>) -> bool {
|
||||
match (&ap.argument, &ap.result) {
|
||||
(_, AstVariable::Stream(_)) => true,
|
||||
(ApArgument::VariableWithLambda(vl), _) => match &vl.variable {
|
||||
AstVariable::Scalar(_) => false,
|
||||
AstVariable::Stream(_) => true,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
matches!(ap.result, ast::Variable::Stream(_))
|
||||
}
|
||||
|
@ -15,27 +15,66 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use air_lambda_parser::LambdaAST;
|
||||
use air_parser::ast;
|
||||
|
||||
pub(super) fn apply_to_arg(
|
||||
argument: &ApArgument<'_>,
|
||||
argument: &ast::ApArgument<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
should_touch_trace: bool,
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
use ast::ApArgument::*;
|
||||
|
||||
let result = match argument {
|
||||
ApArgument::ScalarVariable(scalar_name) => apply_scalar(scalar_name, exec_ctx, trace_ctx, should_touch_trace)?,
|
||||
ApArgument::VariableWithLambda(vl) => apply_json_argument(vl, exec_ctx, trace_ctx)?,
|
||||
ApArgument::LastError(error_path) => apply_last_error(error_path, exec_ctx, trace_ctx)?,
|
||||
ApArgument::Literal(value) => apply_const(value.to_string(), exec_ctx, trace_ctx),
|
||||
ApArgument::Number(value) => apply_const(value, exec_ctx, trace_ctx),
|
||||
ApArgument::Boolean(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||
ApArgument::EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx),
|
||||
InitPeerId => apply_const(exec_ctx.init_peer_id.clone(), exec_ctx, trace_ctx),
|
||||
LastError(error_path) => apply_last_error(error_path, exec_ctx, trace_ctx)?,
|
||||
Literal(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||
Number(value) => apply_const(value, exec_ctx, trace_ctx),
|
||||
Boolean(value) => apply_const(*value, exec_ctx, trace_ctx),
|
||||
EmptyArray => apply_const(serde_json::json!([]), exec_ctx, trace_ctx),
|
||||
Scalar(scalar) => apply_scalar(scalar, exec_ctx, trace_ctx, should_touch_trace)?,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ValueAggregate {
|
||||
let value = Rc::new(value.into());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone());
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
|
||||
ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos())
|
||||
}
|
||||
|
||||
fn apply_last_error(
|
||||
error_path: &ast::LastErrorPath,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?;
|
||||
let value = Rc::new(value);
|
||||
// removing is safe because prepare_last_error always returns a vec with one element.
|
||||
let tetraplet = tetraplets.remove(0);
|
||||
|
||||
let result = ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos());
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_scalar(
|
||||
scalar: &ast::ScalarWithLambda<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
should_touch_trace: bool,
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
// TODO: refactor this code after boxed value
|
||||
match &scalar.lambda {
|
||||
Some(lambda) => apply_scalar_wl_impl(scalar.name, lambda, exec_ctx, trace_ctx),
|
||||
None => apply_scalar_impl(scalar.name, exec_ctx, trace_ctx, should_touch_trace),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_scalar_impl(
|
||||
scalar_name: &str,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
@ -63,34 +102,14 @@ fn apply_scalar(
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_const(value: impl Into<JValue>, exec_ctx: &ExecutionCtx<'_>, trace_ctx: &TraceHandler) -> ValueAggregate {
|
||||
let value = Rc::new(value.into());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(exec_ctx.init_peer_id.clone());
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
|
||||
ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos())
|
||||
}
|
||||
|
||||
fn apply_last_error(
|
||||
error_path: &LastErrorPath,
|
||||
fn apply_scalar_wl_impl(
|
||||
scalar_name: &str,
|
||||
lambda: &LambdaAST<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
let (value, mut tetraplets) = crate::execution_step::utils::prepare_last_error(error_path, exec_ctx)?;
|
||||
let value = Rc::new(value);
|
||||
let tetraplet = tetraplets.remove(0);
|
||||
|
||||
let result = ValueAggregate::new(value, tetraplet, trace_ctx.trace_pos());
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_json_argument(
|
||||
vl: &VariableWithLambda<'_>,
|
||||
exec_ctx: &ExecutionCtx<'_>,
|
||||
trace_ctx: &TraceHandler,
|
||||
) -> ExecutionResult<ValueAggregate> {
|
||||
let variable = Variable::from_ast(&vl.variable);
|
||||
let (jvalue, mut tetraplets) = apply_lambda(variable, &vl.lambda, exec_ctx)?;
|
||||
let variable = Variable::scalar(scalar_name);
|
||||
let (jvalue, mut tetraplets) = apply_lambda(variable, lambda, exec_ctx)?;
|
||||
|
||||
let tetraplet = tetraplets
|
||||
.pop()
|
||||
|
@ -19,8 +19,8 @@ use super::ExecutionResult;
|
||||
use crate::execution_step::Generation;
|
||||
|
||||
use air_interpreter_data::ApResult;
|
||||
use air_parser::ast;
|
||||
use air_parser::ast::Ap;
|
||||
use air_parser::ast::AstVariable;
|
||||
use air_trace_handler::MergerApResult;
|
||||
|
||||
pub(super) fn ap_result_to_generation(ap_result: &MergerApResult) -> Generation {
|
||||
@ -30,7 +30,7 @@ pub(super) fn ap_result_to_generation(ap_result: &MergerApResult) -> Generation
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn try_match_result_to_instr(merger_ap_result: &MergerApResult, instr: &Ap<'_>) -> ExecutionResult<()> {
|
||||
pub(super) fn try_match_trace_to_instr(merger_ap_result: &MergerApResult, instr: &Ap<'_>) -> ExecutionResult<()> {
|
||||
let res_generation = match merger_ap_result {
|
||||
MergerApResult::ApResult { res_generation } => *res_generation,
|
||||
MergerApResult::Empty => return Ok(()),
|
||||
@ -40,15 +40,16 @@ pub(super) fn try_match_result_to_instr(merger_ap_result: &MergerApResult, instr
|
||||
}
|
||||
|
||||
fn match_position_variable(
|
||||
variable: &AstVariable<'_>,
|
||||
variable: &ast::Variable<'_>,
|
||||
generation: Option<u32>,
|
||||
ap_result: &MergerApResult,
|
||||
) -> ExecutionResult<()> {
|
||||
use crate::execution_step::ExecutionError::ApResultNotCorrespondToInstr;
|
||||
use ast::Variable::*;
|
||||
|
||||
match (variable, generation) {
|
||||
(AstVariable::Stream(_), Some(_)) => Ok(()),
|
||||
(AstVariable::Scalar(_), None) => Ok(()),
|
||||
(Stream(_), Some(_)) => Ok(()),
|
||||
(Scalar(_), None) => Ok(()),
|
||||
_ => return crate::exec_err!(ApResultNotCorrespondToInstr(ap_result.clone())),
|
||||
}
|
||||
}
|
||||
@ -71,13 +72,15 @@ fn option_to_vec(value: Option<u32>) -> Vec<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
fn variable_to_generations(variable: &AstVariable<'_>, exec_ctx: &ExecutionCtx<'_>) -> Vec<u32> {
|
||||
fn variable_to_generations(variable: &ast::Variable<'_>, exec_ctx: &ExecutionCtx<'_>) -> Vec<u32> {
|
||||
use ast::Variable::*;
|
||||
|
||||
match variable {
|
||||
AstVariable::Scalar(_) => vec![],
|
||||
AstVariable::Stream(name) => {
|
||||
Scalar(_) => vec![],
|
||||
Stream(stream) => {
|
||||
// unwrap here is safe because this function will be called only
|
||||
// when this stream's been created
|
||||
let stream = exec_ctx.streams.get(*name).unwrap();
|
||||
let stream = exec_ctx.streams.get(stream.name).unwrap();
|
||||
let generation = match stream.borrow().generations_count() {
|
||||
0 => 0,
|
||||
n => n - 1,
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
use super::*;
|
||||
use crate::execution_step::execution_context::*;
|
||||
use crate::execution_step::AstVariable;
|
||||
use crate::execution_step::Generation;
|
||||
use crate::execution_step::Stream;
|
||||
use crate::execution_step::ValueAggregate;
|
||||
@ -24,6 +23,7 @@ use crate::execution_step::ValueAggregate;
|
||||
use air_interpreter_data::CallResult;
|
||||
use air_interpreter_data::Value;
|
||||
use air_parser::ast::CallOutputValue;
|
||||
use air_parser::ast::Variable;
|
||||
use air_trace_handler::TraceHandler;
|
||||
|
||||
use std::cell::RefCell;
|
||||
@ -38,18 +38,18 @@ pub(crate) fn set_local_result<'i>(
|
||||
) -> ExecutionResult<CallResult> {
|
||||
let result_value = executed_result.result.clone();
|
||||
match output {
|
||||
CallOutputValue::Variable(AstVariable::Scalar(name)) => {
|
||||
exec_ctx.scalars.set_value(*name, executed_result)?;
|
||||
CallOutputValue::Variable(Variable::Scalar(scalar)) => {
|
||||
exec_ctx.scalars.set_value(scalar.name, executed_result)?;
|
||||
Ok(CallResult::executed_scalar(result_value))
|
||||
}
|
||||
CallOutputValue::Variable(AstVariable::Stream(name)) => {
|
||||
CallOutputValue::Variable(Variable::Stream(stream)) => {
|
||||
// TODO: refactor this generation handling
|
||||
let generation = match exec_ctx.streams.get(*name) {
|
||||
let generation = match exec_ctx.streams.get(stream.name) {
|
||||
Some(stream) => Generation::Nth(stream.borrow().generations_count() as u32 - 1),
|
||||
None => Generation::Last,
|
||||
};
|
||||
|
||||
let generation = set_stream_result(executed_result, generation, name.to_string(), exec_ctx)?;
|
||||
let generation = set_stream_result(executed_result, generation, stream.name.to_string(), exec_ctx)?;
|
||||
Ok(CallResult::executed_stream(result_value, generation))
|
||||
}
|
||||
CallOutputValue::None => Ok(CallResult::executed_scalar(result_value)),
|
||||
@ -64,14 +64,14 @@ pub(crate) fn set_result_from_value<'i>(
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<()> {
|
||||
match (output, value) {
|
||||
(CallOutputValue::Variable(AstVariable::Scalar(name)), Value::Scalar(value)) => {
|
||||
(CallOutputValue::Variable(Variable::Scalar(scalar)), Value::Scalar(value)) => {
|
||||
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
||||
exec_ctx.scalars.set_value(*name, result)?;
|
||||
exec_ctx.scalars.set_value(scalar.name, result)?;
|
||||
}
|
||||
(CallOutputValue::Variable(AstVariable::Stream(name)), Value::Stream { value, generation }) => {
|
||||
(CallOutputValue::Variable(Variable::Stream(stream)), Value::Stream { value, generation }) => {
|
||||
let result = ValueAggregate::new(value, tetraplet, trace_pos);
|
||||
let generation = Generation::Nth(generation);
|
||||
let _ = set_stream_result(result, generation, name.to_string(), exec_ctx)?;
|
||||
let _ = set_stream_result(result, generation, stream.name.to_string(), exec_ctx)?;
|
||||
}
|
||||
// it isn't needed to check there that output and value matches because
|
||||
// it's been already checked in trace handler
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
use super::call_result_setter::*;
|
||||
use super::prev_result_handler::*;
|
||||
use super::triplet::Triplet;
|
||||
use super::triplet::resolve;
|
||||
use super::*;
|
||||
use crate::execution_step::RSecurityTetraplet;
|
||||
use crate::execution_step::SecurityTetraplets;
|
||||
@ -28,7 +28,7 @@ use crate::SecurityTetraplet;
|
||||
|
||||
use air_interpreter_data::CallResult;
|
||||
use air_interpreter_interface::CallRequestParams;
|
||||
use air_parser::ast::{AstVariable, CallInstrArgValue, CallOutputValue};
|
||||
use air_parser::ast;
|
||||
use air_trace_handler::MergerCallResult;
|
||||
use air_trace_handler::TraceHandler;
|
||||
|
||||
@ -39,8 +39,8 @@ use std::rc::Rc;
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(super) struct ResolvedCall<'i> {
|
||||
tetraplet: RSecurityTetraplet,
|
||||
function_arg_paths: Rc<Vec<CallInstrArgValue<'i>>>,
|
||||
output: CallOutputValue<'i>,
|
||||
function_arg_paths: Rc<Vec<ast::Value<'i>>>,
|
||||
output: ast::CallOutputValue<'i>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -52,8 +52,7 @@ struct ResolvedArguments {
|
||||
impl<'i> ResolvedCall<'i> {
|
||||
/// Build `ResolvedCall` from `Call` by transforming `PeerPart` & `FunctionPart` into `ResolvedTriplet`.
|
||||
pub(super) fn new(raw_call: &Call<'i>, exec_ctx: &ExecutionCtx<'i>) -> ExecutionResult<Self> {
|
||||
let triplet = Triplet::try_from(&raw_call.peer_part, &raw_call.function_part)?;
|
||||
let triplet = triplet.resolve(exec_ctx)?;
|
||||
let triplet = resolve(&raw_call.triplet, exec_ctx)?;
|
||||
let tetraplet = SecurityTetraplet::from_triplet(triplet);
|
||||
let tetraplet = Rc::new(RefCell::new(tetraplet));
|
||||
|
||||
@ -167,11 +166,11 @@ impl<'i> ResolvedCall<'i> {
|
||||
|
||||
/// Check output type name for being already in execution context.
|
||||
// TODO: this check should be moved on a parsing stage
|
||||
fn check_output_name(output: &CallOutputValue<'_>, exec_ctx: &ExecutionCtx<'_>) -> ExecutionResult<()> {
|
||||
fn check_output_name(output: &ast::CallOutputValue<'_>, exec_ctx: &ExecutionCtx<'_>) -> ExecutionResult<()> {
|
||||
use crate::execution_step::boxed_value::ScalarRef;
|
||||
|
||||
let scalar_name = match output {
|
||||
CallOutputValue::Variable(AstVariable::Scalar(name)) => *name,
|
||||
ast::CallOutputValue::Variable(ast::Variable::Scalar(scalar)) => scalar.name,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
|
@ -20,77 +20,40 @@ use super::ExecutionResult;
|
||||
use crate::exec_err;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast::{AstVariable, CallInstrValue, FunctionPart, PeerPart};
|
||||
use air_parser::ast::CallInstrValue;
|
||||
use air_parser::ast::Triplet;
|
||||
use polyplets::ResolvedTriplet;
|
||||
|
||||
/// Triplet represents a location of the executable code in the network.
|
||||
/// It is build from `PeerPart` and `FunctionPart` of a `Call` instruction.
|
||||
pub(super) struct Triplet<'a, 'i> {
|
||||
pub(super) peer_pk: &'a CallInstrValue<'i>,
|
||||
pub(super) service_id: &'a CallInstrValue<'i>,
|
||||
pub(super) function_name: &'a CallInstrValue<'i>,
|
||||
}
|
||||
/// Resolve variables, literals, etc in the `Triplet`, and build a `ResolvedTriplet`.
|
||||
pub(crate) fn resolve<'i>(triplet: &Triplet<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedTriplet> {
|
||||
let Triplet {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
} = triplet;
|
||||
|
||||
impl<'a, 'i> Triplet<'a, 'i> {
|
||||
/// Build a `Triplet` from `Call`'s `PeerPart` and `FunctionPart`
|
||||
pub fn try_from(peer: &'a PeerPart<'i>, f: &'a FunctionPart<'i>) -> ExecutionResult<Self> {
|
||||
use air_parser::ast::FunctionPart::*;
|
||||
use air_parser::ast::PeerPart::*;
|
||||
let peer_pk = resolve_to_string(peer_pk, ctx)?;
|
||||
let service_id = resolve_to_string(service_id, ctx)?;
|
||||
let function_name = resolve_to_string(function_name, ctx)?;
|
||||
|
||||
let (peer_pk, service_id, function_name) = match (peer, f) {
|
||||
(PeerPkWithServiceId(peer_pk, _peer_service_id), ServiceIdWithFuncName(service_id, func_name)) => {
|
||||
Ok((peer_pk, service_id, func_name))
|
||||
}
|
||||
(PeerPkWithServiceId(peer_pk, peer_service_id), FuncName(func_name)) => {
|
||||
Ok((peer_pk, peer_service_id, func_name))
|
||||
}
|
||||
(PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => Ok((peer_pk, service_id, func_name)),
|
||||
(PeerPk(_), FuncName(_)) => exec_err!(ExecutionError::IncorrectCallTriplet),
|
||||
}?;
|
||||
|
||||
Ok(Self {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolve variables, literals, etc in the `Triplet`, and build a `ResolvedTriplet`.
|
||||
pub fn resolve(self, ctx: &ExecutionCtx<'i>) -> ExecutionResult<ResolvedTriplet> {
|
||||
let Triplet {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
} = self;
|
||||
let peer_pk = resolve_to_string(peer_pk, ctx)?;
|
||||
let service_id = resolve_to_string(service_id, ctx)?;
|
||||
let function_name = resolve_to_string(function_name, ctx)?;
|
||||
|
||||
Ok(ResolvedTriplet {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
})
|
||||
}
|
||||
Ok(ResolvedTriplet {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
||||
// TODO: return Rc<String> to avoid excess cloning
|
||||
fn resolve_to_string<'i>(value: &CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
||||
use crate::execution_step::utils::resolve_ast_variable;
|
||||
use crate::execution_step::utils::resolve_ast_variable_wl;
|
||||
|
||||
let resolved = match value {
|
||||
CallInstrValue::InitPeerId => ctx.init_peer_id.clone(),
|
||||
CallInstrValue::Literal(value) => value.to_string(),
|
||||
CallInstrValue::Variable(variable) => {
|
||||
let resolved = resolve_ast_variable(variable, ctx)?;
|
||||
let jvalue = resolved.into_jvalue();
|
||||
jvalue_to_string(jvalue)?
|
||||
}
|
||||
CallInstrValue::VariableWithLambda(vl) => {
|
||||
let resolved = resolve_ast_variable(&vl.variable, ctx)?;
|
||||
let resolved = resolved.apply_lambda(&vl.lambda)?;
|
||||
vec_to_string(resolved, &vl.variable)?
|
||||
let (resolved, _) = resolve_ast_variable_wl(variable, ctx)?;
|
||||
jvalue_to_string(resolved)?
|
||||
}
|
||||
};
|
||||
|
||||
@ -105,15 +68,3 @@ fn jvalue_to_string(jvalue: JValue) -> ExecutionResult<String> {
|
||||
_ => exec_err!(IncompatibleJValueType(jvalue, "string")),
|
||||
}
|
||||
}
|
||||
|
||||
fn vec_to_string(values: Vec<&JValue>, variable: &AstVariable<'_>) -> ExecutionResult<String> {
|
||||
if values.is_empty() {
|
||||
let variable_name = match variable {
|
||||
AstVariable::Stream(name) => name,
|
||||
AstVariable::Scalar(name) => name,
|
||||
};
|
||||
return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string()));
|
||||
}
|
||||
|
||||
jvalue_to_string(values[0].clone())
|
||||
}
|
||||
|
@ -16,57 +16,50 @@
|
||||
|
||||
use crate::execution_step::air::ExecutionResult;
|
||||
use crate::execution_step::execution_context::ExecutionCtx;
|
||||
use crate::execution_step::utils::resolve_ast_variable;
|
||||
use crate::execution_step::utils::prepare_last_error;
|
||||
use crate::execution_step::utils::resolve_ast_variable_wl;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast;
|
||||
use air_parser::ast::MatchableValue;
|
||||
|
||||
pub(crate) fn are_matchable_eq<'ctx>(
|
||||
left: &MatchableValue<'_>,
|
||||
right: &MatchableValue<'_>,
|
||||
left: &ast::Value<'_>,
|
||||
right: &ast::Value<'_>,
|
||||
exec_ctx: &'ctx ExecutionCtx<'_>,
|
||||
) -> ExecutionResult<bool> {
|
||||
use MatchableValue::*;
|
||||
use ast::Value::*;
|
||||
|
||||
match (left, right) {
|
||||
(InitPeerId, InitPeerId) => Ok(true),
|
||||
(InitPeerId, matchable) => compare_matchable(
|
||||
matchable,
|
||||
exec_ctx,
|
||||
make_string_comparator(exec_ctx.init_peer_id.as_str()),
|
||||
),
|
||||
(matchable, InitPeerId) => compare_matchable(
|
||||
(InitPeerId, matchable) | (matchable, InitPeerId) => compare_matchable(
|
||||
matchable,
|
||||
exec_ctx,
|
||||
make_string_comparator(exec_ctx.init_peer_id.as_str()),
|
||||
),
|
||||
|
||||
(LastError(path), matchable) | (matchable, LastError(path)) => {
|
||||
let (value, _) = prepare_last_error(path, exec_ctx)?;
|
||||
compare_matchable(matchable, exec_ctx, make_object_comparator(value))
|
||||
}
|
||||
|
||||
(Literal(left_name), Literal(right_name)) => Ok(left_name == right_name),
|
||||
(Literal(value), matchable) => compare_matchable(matchable, exec_ctx, make_string_comparator(value)),
|
||||
(matchable, Literal(value)) => compare_matchable(matchable, exec_ctx, make_string_comparator(value)),
|
||||
(Literal(value), matchable) | (matchable, Literal(value)) => {
|
||||
compare_matchable(matchable, exec_ctx, make_string_comparator(value))
|
||||
}
|
||||
|
||||
(Boolean(value), matchable) => compare_matchable(matchable, exec_ctx, make_bool_comparator(value)),
|
||||
(matchable, Boolean(value)) => compare_matchable(matchable, exec_ctx, make_bool_comparator(value)),
|
||||
(Boolean(left_boolean), Boolean(right_boolean)) => Ok(left_boolean == right_boolean),
|
||||
(Boolean(value), matchable) | (matchable, Boolean(value)) => {
|
||||
compare_matchable(matchable, exec_ctx, make_object_comparator((*value).into()))
|
||||
}
|
||||
|
||||
(Number(value), matchable) => compare_matchable(matchable, exec_ctx, make_number_comparator(value)),
|
||||
(matchable, Number(value)) => compare_matchable(matchable, exec_ctx, make_number_comparator(value)),
|
||||
(Number(left_number), Number(right_number)) => Ok(left_number == right_number),
|
||||
(Number(value), matchable) | (matchable, Number(value)) => {
|
||||
compare_matchable(matchable, exec_ctx, make_object_comparator(value.into()))
|
||||
}
|
||||
|
||||
(Variable(left_variable), Variable(right_variable)) => {
|
||||
let left_jvaluable = resolve_ast_variable(left_variable, exec_ctx)?;
|
||||
let left_value = left_jvaluable.as_jvalue();
|
||||
|
||||
let right_jvaluable = resolve_ast_variable(right_variable, exec_ctx)?;
|
||||
let right_value = right_jvaluable.as_jvalue();
|
||||
|
||||
Ok(left_value == right_value)
|
||||
}
|
||||
(VariableWithLambda(lhs), VariableWithLambda(rhs)) => {
|
||||
let left_jvaluable = resolve_ast_variable(&lhs.variable, exec_ctx)?;
|
||||
let left_value = left_jvaluable.apply_lambda(&lhs.lambda)?;
|
||||
|
||||
let right_jvaluable = resolve_ast_variable(&rhs.variable, exec_ctx)?;
|
||||
let right_value = right_jvaluable.apply_lambda(&rhs.lambda)?;
|
||||
let (left_value, _) = resolve_ast_variable_wl(left_variable, exec_ctx)?;
|
||||
let (right_value, _) = resolve_ast_variable_wl(right_variable, exec_ctx)?;
|
||||
|
||||
Ok(left_value == right_value)
|
||||
}
|
||||
@ -78,11 +71,11 @@ use std::borrow::Cow;
|
||||
type Comparator<'a> = Box<dyn Fn(Cow<'_, JValue>) -> bool + 'a>;
|
||||
|
||||
fn compare_matchable<'ctx>(
|
||||
matchable: &MatchableValue<'_>,
|
||||
matchable: &ast::Value<'_>,
|
||||
exec_ctx: &'ctx ExecutionCtx<'_>,
|
||||
comparator: Comparator<'ctx>,
|
||||
) -> ExecutionResult<bool> {
|
||||
use MatchableValue::*;
|
||||
use ast::Value::*;
|
||||
|
||||
match matchable {
|
||||
InitPeerId => {
|
||||
@ -90,6 +83,10 @@ fn compare_matchable<'ctx>(
|
||||
let jvalue = init_peer_id.into();
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
LastError(error_path) => {
|
||||
let (jvalue, _) = prepare_last_error(error_path, exec_ctx)?;
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
Literal(str) => {
|
||||
let jvalue = str.to_string().into();
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
@ -107,22 +104,8 @@ fn compare_matchable<'ctx>(
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
Variable(variable) => {
|
||||
let jvaluable = resolve_ast_variable(variable, exec_ctx)?;
|
||||
let jvalue = jvaluable.as_jvalue();
|
||||
Ok(comparator(jvalue))
|
||||
}
|
||||
VariableWithLambda(vl) => {
|
||||
let jvaluable = resolve_ast_variable(&vl.variable, exec_ctx)?;
|
||||
let jvalues = jvaluable.apply_lambda(&vl.lambda)?;
|
||||
|
||||
// TODO: it's known that apply_lambda always returns array with one value that is
|
||||
// intended to support multi-return in the future, this check is needed just in
|
||||
// case and should be refactored after introducing boxed values
|
||||
if jvalues.len() != 1 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(comparator(Cow::Borrowed(jvalues[0])))
|
||||
let (jvalue, _) = resolve_ast_variable_wl(variable, exec_ctx)?;
|
||||
Ok(comparator(Cow::Owned(jvalue)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,21 +121,8 @@ fn make_string_comparator(comparable_string: &str) -> Comparator<'_> {
|
||||
})
|
||||
}
|
||||
|
||||
fn make_bool_comparator(comparable_bool: &bool) -> Comparator<'_> {
|
||||
fn make_object_comparator(comparable_value: JValue) -> Comparator<'static> {
|
||||
use std::ops::Deref;
|
||||
|
||||
let comparable_bool = *comparable_bool;
|
||||
Box::new(move |jvalue: Cow<'_, JValue>| -> bool {
|
||||
match jvalue.deref() {
|
||||
JValue::Bool(jvalue) => jvalue == &comparable_bool,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn make_number_comparator(comparable_number: &ast::Number) -> Comparator<'_> {
|
||||
use std::ops::Deref;
|
||||
|
||||
let comparable_jvalue: JValue = comparable_number.into();
|
||||
Box::new(move |jvalue: Cow<'_, JValue>| -> bool { jvalue.deref() == &comparable_jvalue })
|
||||
Box::new(move |jvalue: Cow<'_, JValue>| -> bool { jvalue.deref() == &comparable_value })
|
||||
}
|
||||
|
@ -40,14 +40,12 @@ pub(crate) enum FoldIterableStream {
|
||||
|
||||
/// Constructs iterable value for given scalar iterable.
|
||||
pub(crate) fn construct_scalar_iterable_value<'ctx>(
|
||||
ast_iterable: &ast::IterableScalarValue<'ctx>,
|
||||
iterable: &ast::ScalarWithLambda<'ctx>,
|
||||
exec_ctx: &ExecutionCtx<'ctx>,
|
||||
) -> ExecutionResult<FoldIterableScalar> {
|
||||
match ast_iterable {
|
||||
ast::IterableScalarValue::ScalarVariable(scalar_name) => create_scalar_iterable(exec_ctx, scalar_name),
|
||||
ast::IterableScalarValue::VariableWithLambda { scalar_name, lambda } => {
|
||||
create_scalar_lambda_iterable(exec_ctx, scalar_name, lambda)
|
||||
}
|
||||
match &iterable.lambda {
|
||||
None => create_scalar_iterable(exec_ctx, iterable.name),
|
||||
Some(lambda) => create_scalar_lambda_iterable(exec_ctx, iterable.name, lambda),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> {
|
||||
FoldIterableScalar::Scalar(iterable) => fold(
|
||||
iterable,
|
||||
IterableType::Scalar,
|
||||
self.iterator,
|
||||
self.iterator.name,
|
||||
self.instruction.clone(),
|
||||
exec_ctx,
|
||||
trace_ctx,
|
||||
|
@ -29,7 +29,7 @@ impl<'i> ExecutableInstruction<'i> for FoldStream<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(fold, exec_ctx, trace_ctx);
|
||||
|
||||
let iterables = match construct_stream_iterable_value(self.stream_name, exec_ctx)? {
|
||||
let iterables = match construct_stream_iterable_value(self.iterable.name, exec_ctx)? {
|
||||
FoldIterableStream::Empty => return Ok(()),
|
||||
FoldIterableStream::Stream(iterables) => iterables,
|
||||
};
|
||||
@ -51,7 +51,7 @@ impl<'i> ExecutableInstruction<'i> for FoldStream<'i> {
|
||||
fold(
|
||||
iterable,
|
||||
IterableType::Stream(fold_id),
|
||||
self.iterator,
|
||||
self.iterator.name,
|
||||
self.instruction.clone(),
|
||||
exec_ctx,
|
||||
trace_ctx,
|
||||
|
@ -28,7 +28,7 @@ impl<'i> super::ExecutableInstruction<'i> for Next<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
|
||||
log_instruction!(next, exec_ctx, trace_ctx);
|
||||
|
||||
let iterator_name = self.0;
|
||||
let iterator_name = &self.iterator.name;
|
||||
let fold_state = exec_ctx.scalars.get_iterable_mut(iterator_name)?;
|
||||
maybe_meet_iteration_end(fold_state, trace_ctx)?;
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
use super::Generation;
|
||||
use air_parser::ast::AstVariable;
|
||||
use air_parser::ast;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum Variable<'i> {
|
||||
@ -24,21 +24,20 @@ pub(crate) enum Variable<'i> {
|
||||
}
|
||||
|
||||
impl<'i> Variable<'i> {
|
||||
pub(crate) fn from_ast(ast_variable: &AstVariable<'i>) -> Self {
|
||||
match ast_variable {
|
||||
AstVariable::Scalar(name) => Variable::Scalar(name),
|
||||
AstVariable::Stream(name) => Variable::Stream {
|
||||
name,
|
||||
generation: Generation::Last,
|
||||
},
|
||||
}
|
||||
pub(crate) fn scalar(name: &'i str) -> Self {
|
||||
Self::Scalar(name)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_ast_with_generation(ast_variable: &AstVariable<'i>, generation: Generation) -> Self {
|
||||
pub(crate) fn from_ast_with_generation(ast_variable: &ast::Variable<'i>, generation: Generation) -> Self {
|
||||
use ast::Variable::*;
|
||||
|
||||
match ast_variable {
|
||||
AstVariable::Scalar(name) => Variable::Scalar(name),
|
||||
AstVariable::Stream(name) => Variable::Stream { name, generation },
|
||||
Scalar(scalar) => Variable::Scalar(scalar.name),
|
||||
Stream(stream) => Variable::Stream {
|
||||
name: stream.name,
|
||||
generation,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,3 +46,31 @@ impl<'i> Variable<'i> {
|
||||
Self::Stream { name, generation }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> From<&ast::Variable<'i>> for Variable<'i> {
|
||||
fn from(ast_variable: &ast::Variable<'i>) -> Self {
|
||||
use ast::Variable::*;
|
||||
|
||||
match ast_variable {
|
||||
Scalar(scalar) => Self::Scalar(scalar.name),
|
||||
Stream(stream) => Self::Stream {
|
||||
name: stream.name,
|
||||
generation: Generation::Last,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> From<&ast::VariableWithLambda<'i>> for Variable<'i> {
|
||||
fn from(ast_variable: &ast::VariableWithLambda<'i>) -> Self {
|
||||
use ast::VariableWithLambda::*;
|
||||
|
||||
match ast_variable {
|
||||
Scalar(scalar) => Self::Scalar(scalar.name),
|
||||
Stream(stream) => Self::Stream {
|
||||
name: stream.name,
|
||||
generation: Generation::Last,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,6 @@ use std::rc::Rc;
|
||||
#[derive(ThisError, EnumDiscriminants, Debug)]
|
||||
#[strum_discriminants(derive(EnumIter))]
|
||||
pub(crate) enum ExecutionError {
|
||||
/// Semantic errors in a call instructions.
|
||||
#[error("call should have service id specified by peer part or function part")]
|
||||
IncorrectCallTriplet,
|
||||
|
||||
/// An error is occurred while calling local service via call_service.
|
||||
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
|
||||
LocalServiceError(i32, Rc<String>),
|
||||
|
@ -41,8 +41,6 @@ type ExecutionResult<T> = std::result::Result<T, Rc<ExecutionError>>;
|
||||
type RSecurityTetraplet = Rc<RefCell<crate::SecurityTetraplet>>;
|
||||
type SecurityTetraplets = Vec<RSecurityTetraplet>;
|
||||
|
||||
use air_parser::ast::AstVariable;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exec_err {
|
||||
($err:expr) => {
|
||||
|
@ -24,8 +24,7 @@ use crate::JValue;
|
||||
use crate::LambdaAST;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast::AstVariable;
|
||||
use air_parser::ast::CallInstrArgValue;
|
||||
use air_parser::ast;
|
||||
use air_parser::ast::LastErrorPath;
|
||||
|
||||
use serde_json::json;
|
||||
@ -34,24 +33,19 @@ use std::rc::Rc;
|
||||
|
||||
/// Resolve value to called function arguments.
|
||||
pub(crate) fn resolve_to_args<'i>(
|
||||
value: &CallInstrArgValue<'i>,
|
||||
value: &ast::Value<'i>,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, SecurityTetraplets)> {
|
||||
use ast::Value::*;
|
||||
|
||||
match value {
|
||||
CallInstrArgValue::InitPeerId => prepare_const(ctx.init_peer_id.clone(), ctx),
|
||||
CallInstrArgValue::LastError(path) => prepare_last_error(path, ctx),
|
||||
CallInstrArgValue::Literal(value) => prepare_const(value.to_string(), ctx),
|
||||
CallInstrArgValue::Boolean(value) => prepare_const(*value, ctx),
|
||||
CallInstrArgValue::Number(value) => prepare_const(value, ctx),
|
||||
CallInstrArgValue::EmptyArray => prepare_const(json!([]), ctx),
|
||||
CallInstrArgValue::Variable(variable) => {
|
||||
let variable = Variable::from_ast(variable);
|
||||
prepare_variable(variable, ctx)
|
||||
}
|
||||
CallInstrArgValue::VariableWithLambda(var_with_lambda) => {
|
||||
let variable = Variable::from_ast(&var_with_lambda.variable);
|
||||
apply_lambda(variable, &var_with_lambda.lambda, ctx)
|
||||
}
|
||||
InitPeerId => prepare_const(ctx.init_peer_id.clone(), ctx),
|
||||
LastError(path) => prepare_last_error(path, ctx),
|
||||
Literal(value) => prepare_const(value.to_string(), ctx),
|
||||
Boolean(value) => prepare_const(*value, ctx),
|
||||
Number(value) => prepare_const(value, ctx),
|
||||
EmptyArray => prepare_const(json!([]), ctx),
|
||||
Variable(variable) => resolve_ast_variable_wl(variable, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,17 +80,6 @@ pub(crate) fn prepare_last_error(
|
||||
Ok((jvalue, vec![tetraplets]))
|
||||
}
|
||||
|
||||
fn prepare_variable<'i>(
|
||||
variable: Variable<'_>,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, SecurityTetraplets)> {
|
||||
let resolved = resolve_variable(variable, ctx)?;
|
||||
let tetraplets = resolved.as_tetraplets();
|
||||
let jvalue = resolved.into_jvalue();
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_variable<'ctx, 'i>(
|
||||
variable: Variable<'_>,
|
||||
ctx: &'ctx ExecutionCtx<'i>,
|
||||
@ -119,20 +102,27 @@ pub(crate) fn resolve_variable<'ctx, 'i>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_ast_variable<'ctx, 'i>(
|
||||
variable: &AstVariable<'_>,
|
||||
ctx: &'ctx ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<Box<dyn JValuable + 'ctx>> {
|
||||
let variable = Variable::from_ast(variable);
|
||||
resolve_variable(variable, ctx)
|
||||
pub(crate) fn resolve_ast_variable_wl<'ctx, 'i>(
|
||||
ast_variable: &ast::VariableWithLambda<'_>,
|
||||
exec_ctx: &'ctx ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, SecurityTetraplets)> {
|
||||
let variable: Variable<'_> = ast_variable.into();
|
||||
match ast_variable.lambda() {
|
||||
Some(lambda) => apply_lambda(variable, lambda, exec_ctx),
|
||||
None => {
|
||||
let value = resolve_variable(variable, exec_ctx)?;
|
||||
let tetraplets = value.as_tetraplets();
|
||||
Ok((value.into_jvalue(), tetraplets))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_lambda<'i>(
|
||||
variable: Variable<'_>,
|
||||
lambda: &LambdaAST<'i>,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
exec_ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, SecurityTetraplets)> {
|
||||
let resolved = resolve_variable(variable, ctx)?;
|
||||
let resolved = resolve_variable(variable, exec_ctx)?;
|
||||
let (jvalue, tetraplets) = resolved.apply_lambda_with_tetraplets(lambda)?;
|
||||
|
||||
// it's known that apply_lambda_with_tetraplets returns vec of one value
|
||||
|
@ -109,7 +109,7 @@ fn duplicate_variables() {
|
||||
|
||||
let result = call_vm!(vm, "asd", script, "", "");
|
||||
|
||||
assert_eq!(result.ret_code, 1003);
|
||||
assert_eq!(result.ret_code, 1002);
|
||||
assert!(result.next_peer_pks.is_empty());
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ fn inner_fold_with_same_iterator() {
|
||||
|
||||
let result = call_vm!(vm, "", script, "", "");
|
||||
|
||||
assert_eq!(result.ret_code, 1008);
|
||||
assert_eq!(result.ret_code, 1007);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -192,11 +192,11 @@ fn match_without_xor() {
|
||||
let result = call_vm!(set_variable_vm, "", &script, "", "");
|
||||
let result = call_vm!(vm, "", &script, "", result.data);
|
||||
|
||||
assert_eq!(result.ret_code, 1011);
|
||||
assert_eq!(result.ret_code, 1010);
|
||||
|
||||
let result = call_vm!(vm, "", script, "", result.data);
|
||||
|
||||
assert_eq!(result.ret_code, 1011);
|
||||
assert_eq!(result.ret_code, 1010);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -143,11 +143,11 @@ fn mismatch_without_xor() {
|
||||
let result = call_vm!(set_variable_vm, "asd", &script, "", "");
|
||||
let result = call_vm!(vm, "asd", &script, "", result.data);
|
||||
|
||||
assert_eq!(result.ret_code, 1012);
|
||||
assert_eq!(result.ret_code, 1011);
|
||||
|
||||
let result = call_vm!(vm, "asd", script, "", result.data);
|
||||
|
||||
assert_eq!(result.ret_code, 1012);
|
||||
assert_eq!(result.ret_code, 1011);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -169,7 +169,7 @@ fn dont_wait_on_json_path_on_scalars() {
|
||||
let init_peer_id = "asd";
|
||||
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
||||
let array_result = call_vm!(array_consumer, init_peer_id, &script, "", result.data.clone());
|
||||
assert_eq!(array_result.ret_code, 1004);
|
||||
assert_eq!(array_result.ret_code, 1003);
|
||||
assert_eq!(
|
||||
array_result.error_message,
|
||||
r#"value '[1,2,3,4,5]' does not contain element for idx = '5'"#
|
||||
@ -188,7 +188,7 @@ fn dont_wait_on_json_path_on_scalars() {
|
||||
let init_peer_id = "asd";
|
||||
let result = call_vm!(set_variable_vm, init_peer_id, &script, "", "");
|
||||
let object_result = call_vm!(object_consumer, init_peer_id, script, "", result.data);
|
||||
assert_eq!(object_result.ret_code, 1004);
|
||||
assert_eq!(object_result.ret_code, 1003);
|
||||
assert_eq!(
|
||||
object_result.error_message,
|
||||
r#"value '{"err_msg":"","is_authenticated":1,"ret_code":0}' does not contain element with field name = 'non_exist_path'"#
|
||||
|
@ -37,5 +37,5 @@ fn json_path_not_allowed_for_non_objects_and_arrays() {
|
||||
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, 1004);
|
||||
assert_eq!(result.ret_code, 1003);
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ fn par_early_exit() {
|
||||
];
|
||||
let setter_3_malicious_data = raw_data_from_trace(setter_3_malicious_trace);
|
||||
let init_result_3 = call_vm!(init, "", &script, init_result_2.data.clone(), setter_3_malicious_data);
|
||||
assert_eq!(init_result_3.ret_code, 1013);
|
||||
assert_eq!(init_result_3.ret_code, 1012);
|
||||
|
||||
let actual_trace = trace_from_result(&init_result_3);
|
||||
let expected_trace = trace_from_result(&init_result_2);
|
||||
|
@ -13,6 +13,7 @@ categories = ["wasm"]
|
||||
lalrpop = "0.19.6"
|
||||
|
||||
[dependencies]
|
||||
air-lambda-ast = { path = "../lambda/ast" }
|
||||
air-lambda-parser = { path = "../lambda/parser" }
|
||||
|
||||
lalrpop-util = "0.19.6"
|
||||
|
88
crates/air-lib/air-parser/src/ast/instruction_arguments.rs
Normal file
88
crates/air-lib/air-parser/src/ast/instruction_arguments.rs
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod traits;
|
||||
|
||||
use super::Variable;
|
||||
use super::VariableWithLambda;
|
||||
use crate::ast::ScalarWithLambda;
|
||||
use crate::parser::lexer::LastErrorPath;
|
||||
use crate::parser::lexer::Number;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum CallInstrValue<'i> {
|
||||
InitPeerId,
|
||||
Literal(&'i str),
|
||||
Variable(VariableWithLambda<'i>),
|
||||
}
|
||||
|
||||
/// The peer part of a call instruction triplet
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum PeerPart<'i> {
|
||||
PeerPk(CallInstrValue<'i>),
|
||||
PeerPkWithServiceId(CallInstrValue<'i>, CallInstrValue<'i>),
|
||||
}
|
||||
|
||||
/// The function part of a call instruction triplet
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum FunctionPart<'i> {
|
||||
FuncName(CallInstrValue<'i>),
|
||||
ServiceIdWithFuncName(CallInstrValue<'i>, CallInstrValue<'i>),
|
||||
}
|
||||
|
||||
/// Triplet represents a location of the executable code in the network.
|
||||
/// It is build from `PeerPart` and `FunctionPart` of a `Call` instruction.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Triplet<'i> {
|
||||
#[serde(borrow)]
|
||||
pub peer_pk: CallInstrValue<'i>,
|
||||
#[serde(borrow)]
|
||||
pub service_id: CallInstrValue<'i>,
|
||||
#[serde(borrow)]
|
||||
pub function_name: CallInstrValue<'i>,
|
||||
}
|
||||
|
||||
/// Represents all values that is possible to set in AIR scripts.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum Value<'i> {
|
||||
InitPeerId,
|
||||
LastError(LastErrorPath),
|
||||
Literal(&'i str),
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
EmptyArray, // only empty arrays are allowed now
|
||||
Variable(VariableWithLambda<'i>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq, Clone)]
|
||||
pub enum CallOutputValue<'i> {
|
||||
Variable(Variable<'i>),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum ApArgument<'i> {
|
||||
InitPeerId,
|
||||
LastError(LastErrorPath),
|
||||
Literal(&'i str),
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
EmptyArray,
|
||||
Scalar(ScalarWithLambda<'i>),
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Value<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use Value::*;
|
||||
|
||||
match self {
|
||||
InitPeerId => write!(f, "%init_peer_id%"),
|
||||
LastError(error_path) => write!(f, "%last_error%{}", error_path),
|
||||
Literal(literal) => write!(f, r#""{}""#, literal),
|
||||
Number(number) => write!(f, "{}", number),
|
||||
Boolean(bool) => write!(f, "{}", bool),
|
||||
EmptyArray => write!(f, "[]"),
|
||||
Variable(variable) => write!(f, "{}", variable),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CallInstrValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use CallInstrValue::*;
|
||||
|
||||
match self {
|
||||
InitPeerId => write!(f, "%init_peer_id%"),
|
||||
Literal(literal) => write!(f, r#""{}""#, literal),
|
||||
Variable(variable) => write!(f, "{}", variable),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CallOutputValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use CallOutputValue::*;
|
||||
|
||||
match self {
|
||||
Variable(variable) => write!(f, "{}", variable),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ApArgument<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use ApArgument::*;
|
||||
|
||||
match self {
|
||||
InitPeerId => write!(f, "%init_peer_id%"),
|
||||
LastError(last_error) => write!(f, "{}", last_error),
|
||||
Literal(str) => write!(f, r#""{}""#, str),
|
||||
Number(number) => write!(f, "{}", number),
|
||||
Boolean(bool) => write!(f, "{}", bool),
|
||||
EmptyArray => write!(f, "[]"),
|
||||
Scalar(scalar) => write!(f, "{}", scalar),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PeerPart<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use PeerPart::*;
|
||||
|
||||
match self {
|
||||
PeerPk(peer_pk) => write!(f, "{}", peer_pk),
|
||||
PeerPkWithServiceId(peer_pk, service_id) => write!(f, "({} {})", peer_pk, service_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FunctionPart<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use FunctionPart::*;
|
||||
|
||||
match self {
|
||||
FuncName(func_name) => write!(f, "{}", func_name),
|
||||
ServiceIdWithFuncName(service_id, func_name) => {
|
||||
write!(f, "({} {})", service_id, func_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Triplet<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} ({} {})",
|
||||
self.peer_pk, self.service_id, self.function_name
|
||||
)
|
||||
}
|
||||
}
|
110
crates/air-lib/air-parser/src/ast/instructions.rs
Normal file
110
crates/air-lib/air-parser/src/ast/instructions.rs
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod impls;
|
||||
mod traits;
|
||||
|
||||
use super::*;
|
||||
use serde::Serialize;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(clippy::large_enum_variant)] // for Null and Error variants
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum Instruction<'i> {
|
||||
Null(Null),
|
||||
Call(Call<'i>),
|
||||
Ap(Ap<'i>),
|
||||
Seq(Seq<'i>),
|
||||
Par(Par<'i>),
|
||||
Xor(Xor<'i>),
|
||||
Match(Match<'i>),
|
||||
MisMatch(MisMatch<'i>),
|
||||
FoldScalar(FoldScalar<'i>),
|
||||
FoldStream(FoldStream<'i>),
|
||||
Next(Next<'i>),
|
||||
Error,
|
||||
}
|
||||
|
||||
/// (call (peer part of a triplet: PeerPart) (function part of a triplet: FunctionPart) [arguments] output)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Call<'i> {
|
||||
pub triplet: Triplet<'i>,
|
||||
pub args: Rc<Vec<Value<'i>>>,
|
||||
pub output: CallOutputValue<'i>,
|
||||
}
|
||||
|
||||
/// (ap argument result)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Ap<'i> {
|
||||
pub argument: ApArgument<'i>,
|
||||
pub result: Variable<'i>,
|
||||
}
|
||||
|
||||
/// (seq instruction instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Seq<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||
|
||||
/// (par instruction instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Par<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||
|
||||
/// (xor instruction instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Xor<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||
|
||||
/// (match left_value right_value instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Match<'i> {
|
||||
pub left_value: Value<'i>,
|
||||
pub right_value: Value<'i>,
|
||||
pub instruction: Box<Instruction<'i>>,
|
||||
}
|
||||
|
||||
/// (mismatch left_value right_value instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct MisMatch<'i> {
|
||||
pub left_value: Value<'i>,
|
||||
pub right_value: Value<'i>,
|
||||
pub instruction: Box<Instruction<'i>>,
|
||||
}
|
||||
|
||||
/// (fold scalar_iterable iterator instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct FoldScalar<'i> {
|
||||
pub iterable: ScalarWithLambda<'i>,
|
||||
#[serde(borrow)]
|
||||
pub iterator: Scalar<'i>,
|
||||
pub instruction: Rc<Instruction<'i>>,
|
||||
}
|
||||
|
||||
/// (fold stream_iterable iterator instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct FoldStream<'i> {
|
||||
pub iterable: Stream<'i>,
|
||||
#[serde(borrow)]
|
||||
pub iterator: Scalar<'i>,
|
||||
pub instruction: Rc<Instruction<'i>>,
|
||||
}
|
||||
|
||||
/// (fold stream_iterable iterator instruction)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Next<'i> {
|
||||
pub iterator: Scalar<'i>,
|
||||
}
|
||||
|
||||
/// (null)
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Null;
|
23
crates/air-lib/air-parser/src/ast/instructions/impls.rs
Normal file
23
crates/air-lib/air-parser/src/ast/instructions/impls.rs
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'i> Ap<'i> {
|
||||
pub fn new(argument: ApArgument<'i>, result: Variable<'i>) -> Self {
|
||||
Self { argument, result }
|
||||
}
|
||||
}
|
109
crates/air-lib/air-parser/src/ast/instructions/traits.rs
Normal file
109
crates/air-lib/air-parser/src/ast/instructions/traits.rs
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Instruction<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use Instruction::*;
|
||||
|
||||
match self {
|
||||
Null(null) => write!(f, "{}", null),
|
||||
Call(call) => write!(f, "{}", call),
|
||||
Ap(ap) => write!(f, "{}", ap),
|
||||
Seq(seq) => write!(f, "{}", seq),
|
||||
Par(par) => write!(f, "{}", par),
|
||||
Xor(xor) => write!(f, "{}", xor),
|
||||
Match(match_) => write!(f, "{}", match_),
|
||||
MisMatch(mismatch) => write!(f, "{}", mismatch),
|
||||
FoldScalar(fold) => write!(f, "{}", fold),
|
||||
FoldStream(fold) => write!(f, "{}", fold),
|
||||
Next(next) => write!(f, "{}", next),
|
||||
Error => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Call<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use itertools::Itertools;
|
||||
|
||||
let args = self.args.iter().map(|arg| format!("{}", arg)).join(" ");
|
||||
write!(f, "call {} [{}] {}", self.triplet, args, self.output)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Ap<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ap {} {}", self.argument, self.result)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FoldScalar<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "fold {} {}", self.iterable, self.iterator)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FoldStream<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "fold {} {}", self.iterable, self.iterator)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Seq<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "seq")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Par<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "par")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Null {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "null")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Xor<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "xor")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Match<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "match {} {}", self.left_value, self.right_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MisMatch<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "mismatch {} {}", self.left_value, self.right_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Next<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "next")
|
||||
}
|
||||
}
|
26
crates/air-lib/air-parser/src/ast/mod.rs
Normal file
26
crates/air-lib/air-parser/src/ast/mod.rs
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod instruction_arguments;
|
||||
mod instructions;
|
||||
mod values;
|
||||
|
||||
pub use instruction_arguments::*;
|
||||
pub use instructions::*;
|
||||
pub use values::*;
|
||||
|
||||
pub use crate::parser::lexer::LastErrorPath;
|
||||
pub use crate::parser::lexer::Number;
|
69
crates/air-lib/air-parser/src/ast/values.rs
Normal file
69
crates/air-lib/air-parser/src/ast/values.rs
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod impls;
|
||||
mod traits;
|
||||
|
||||
use air_lambda_parser::LambdaAST;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// A scalar value without lambda.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Scalar<'i> {
|
||||
pub name: &'i str,
|
||||
}
|
||||
|
||||
/// A scalar value with possible lambda expression.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct ScalarWithLambda<'i> {
|
||||
pub name: &'i str,
|
||||
#[serde(borrow)]
|
||||
pub lambda: Option<LambdaAST<'i>>,
|
||||
}
|
||||
|
||||
/// A stream without lambda.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Stream<'i> {
|
||||
pub name: &'i str,
|
||||
}
|
||||
|
||||
/// A stream with possible lambda expression.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct StreamWithLambda<'i> {
|
||||
pub name: &'i str,
|
||||
#[serde(borrow)]
|
||||
pub lambda: Option<LambdaAST<'i>>,
|
||||
}
|
||||
|
||||
/// A variable that could be either scalar or stream without lambda.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum Variable<'i> {
|
||||
#[serde(borrow)]
|
||||
Scalar(Scalar<'i>),
|
||||
#[serde(borrow)]
|
||||
Stream(Stream<'i>),
|
||||
}
|
||||
|
||||
/// A variable that could be either scalar or stream with possible lambda expression.
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum VariableWithLambda<'i> {
|
||||
#[serde(borrow)]
|
||||
Scalar(ScalarWithLambda<'i>),
|
||||
#[serde(borrow)]
|
||||
Stream(StreamWithLambda<'i>),
|
||||
}
|
131
crates/air-lib/air-parser/src/ast/values/impls.rs
Normal file
131
crates/air-lib/air-parser/src/ast/values/impls.rs
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use air_lambda_parser::LambdaAST;
|
||||
use air_lambda_parser::ValueAccessor;
|
||||
|
||||
impl<'i> ScalarWithLambda<'i> {
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>) -> Self {
|
||||
Self { name, lambda }
|
||||
}
|
||||
|
||||
// it's unsafe method that should be used only for tests
|
||||
pub(crate) fn from_raw_lambda(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
||||
Self {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> StreamWithLambda<'i> {
|
||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>) -> Self {
|
||||
Self { name, lambda }
|
||||
}
|
||||
|
||||
// it's unsafe method that should be used only for tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
||||
Self {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Scalar<'i> {
|
||||
pub fn new(name: &'i str) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Stream<'i> {
|
||||
pub fn new(name: &'i str) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Variable<'i> {
|
||||
pub fn scalar(name: &'i str) -> Self {
|
||||
Self::Scalar(Scalar { name })
|
||||
}
|
||||
|
||||
pub fn stream(name: &'i str) -> Self {
|
||||
Self::Stream(Stream { name })
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Variable::Scalar(scalar) => scalar.name,
|
||||
Variable::Stream(stream) => stream.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> VariableWithLambda<'i> {
|
||||
pub fn scalar(name: &'i str) -> Self {
|
||||
Self::Scalar(ScalarWithLambda { name, lambda: None })
|
||||
}
|
||||
|
||||
pub fn scalar_wl(name: &'i str, lambda: LambdaAST<'i>) -> Self {
|
||||
Self::Scalar(ScalarWithLambda {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stream(name: &'i str) -> Self {
|
||||
Self::Stream(StreamWithLambda { name, lambda: None })
|
||||
}
|
||||
|
||||
pub fn stream_wl(name: &'i str, lambda: LambdaAST<'i>) -> Self {
|
||||
Self::Stream(StreamWithLambda {
|
||||
name,
|
||||
lambda: Some(lambda),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
VariableWithLambda::Scalar(scalar) => scalar.name,
|
||||
VariableWithLambda::Stream(stream) => stream.name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lambda(&self) -> &Option<LambdaAST> {
|
||||
match self {
|
||||
VariableWithLambda::Scalar(scalar) => &scalar.lambda,
|
||||
VariableWithLambda::Stream(stream) => &stream.lambda,
|
||||
}
|
||||
}
|
||||
|
||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda_scalar(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let scalar = ScalarWithLambda::from_raw_lambda(name, lambda);
|
||||
Self::Scalar(scalar)
|
||||
}
|
||||
|
||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn from_raw_lambda_stream(name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let stream = StreamWithLambda::from_raw_lambda(name, lambda);
|
||||
Self::Stream(stream)
|
||||
}
|
||||
}
|
71
crates/air-lib/air-parser/src/ast/values/traits.rs
Normal file
71
crates/air-lib/air-parser/src/ast/values/traits.rs
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use air_lambda_ast::format_ast;
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for Scalar<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
None => write!(f, "{}", self.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Stream<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
None => write!(f, "{}", self.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Variable<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use Variable::*;
|
||||
|
||||
match self {
|
||||
Scalar(scalar) => write!(f, "{}", scalar),
|
||||
Stream(stream) => write!(f, "{}", stream),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VariableWithLambda<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use VariableWithLambda::*;
|
||||
|
||||
match self {
|
||||
Scalar(scalar) => write!(f, "{}", scalar),
|
||||
Stream(stream) => write!(f, "{}", stream),
|
||||
}
|
||||
}
|
||||
}
|
@ -24,9 +24,9 @@
|
||||
unreachable_patterns
|
||||
)]
|
||||
|
||||
pub mod ast;
|
||||
mod parser;
|
||||
|
||||
pub use parser::ast;
|
||||
pub use parser::parse;
|
||||
pub use parser::AIRLexer;
|
||||
pub use parser::AIRParser;
|
||||
|
@ -1,9 +1,12 @@
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::air_parser::make_stream_iterable_error;
|
||||
use crate::ast::*;
|
||||
use crate::parser::ParserError;
|
||||
use crate::parser::VariableValidator;
|
||||
use crate::parser::Span;
|
||||
use crate::parser::lexer::Token;
|
||||
use crate::parser::lexer::LastErrorPath;
|
||||
use crate::parser::lexer::Number;
|
||||
use crate::parser::air_utils::*;
|
||||
use crate::make_user_error;
|
||||
|
||||
use air_lambda_parser::LambdaAST;
|
||||
use lalrpop_util::ErrorRecovery;
|
||||
@ -15,25 +18,27 @@ grammar<'err, 'input, 'v>(input: &'input str, errors: &'err mut Vec<ErrorRecover
|
||||
pub AIR = Instr;
|
||||
|
||||
Instr: Box<Instruction<'input>> = {
|
||||
<left: @L> "(" call <peer_part:PeerPart> <function_part:FPart> <args:Args> <output:Output?> ")" <right: @R> => {
|
||||
<left: @L> "(" call <peer_part:PeerPart> <function_part:FPart> <args:Args> <output:CallOutput?> ")" <right: @R> => {
|
||||
let triplet = match try_to_raw_triplet(peer_part, function_part) {
|
||||
Some(raw_triplet) => raw_triplet,
|
||||
None => {
|
||||
// none means error
|
||||
errors.push(make_user_error!(InvalidCallTriplet, left, Token::Call, right));
|
||||
return Box::new(Instruction::Error);
|
||||
}
|
||||
};
|
||||
|
||||
let output = output.map(CallOutputValue::Variable).unwrap_or(CallOutputValue::None);
|
||||
let args = Rc::new(args);
|
||||
let call = Call { peer_part, function_part, args, output };
|
||||
let call = Call { triplet, args, output};
|
||||
let span = Span { left, right };
|
||||
validator.met_call(&call, span);
|
||||
|
||||
Box::new(Instruction::Call(call))
|
||||
},
|
||||
|
||||
<left: @L> "(" ap <arg:ApArgument> <res:Output> ")" <right: @R> => {
|
||||
if let ApArgument::VariableWithLambda(vl) = &arg {
|
||||
if let AstVariable::Stream(_) = &vl.variable {
|
||||
let token = Token::VariableWithLambda(vl.variable.clone(), vl.lambda.clone());
|
||||
errors.push(make_stream_iterable_error(left, token, right));
|
||||
};
|
||||
}
|
||||
|
||||
let apply = Ap::new(arg, res);
|
||||
<left: @L> "(" ap <arg:ApArgument> <result:ApResult> ")" <right: @R> => {
|
||||
let apply = Ap::new(arg, result);
|
||||
let span = Span { left, right };
|
||||
validator.met_ap(&apply, span);
|
||||
|
||||
@ -44,7 +49,8 @@ Instr: Box<Instruction<'input>> = {
|
||||
"(" par <l:Instr> <r:Instr> ")" => Box::new(Instruction::Par(Par(l, r))),
|
||||
"(" null ")" => Box::new(Instruction::Null(Null)),
|
||||
|
||||
<left: @L> "(" fold <iterable:ScalarIterable> <iterator:Alphanumeric> <i:Instr> ")" <right: @R> => {
|
||||
<left: @L> "(" fold <iterable:FoldScalarIterable> <iterator_name:Scalar> <i:Instr> ")" <right: @R> => {
|
||||
let iterator = Scalar { name: iterator_name };
|
||||
let instruction = Rc::new(*i);
|
||||
let fold = FoldScalar { iterable, iterator, instruction };
|
||||
let span = Span { left, right };
|
||||
@ -53,17 +59,21 @@ Instr: Box<Instruction<'input>> = {
|
||||
Box::new(Instruction::FoldScalar(fold))
|
||||
},
|
||||
|
||||
<left: @L> "(" fold <stream:Stream> <iterator:Alphanumeric> <i:Instr> ")" <right: @R> => {
|
||||
<left: @L> "(" fold <stream_name:Stream> <iterator_name:Scalar> <i:Instr> ")" <right: @R> => {
|
||||
let iterable = Stream { name: stream_name };
|
||||
let iterator = Scalar { name: iterator_name };
|
||||
let instruction = Rc::new(*i);
|
||||
let fold = FoldStream { stream_name: stream, iterator, instruction };
|
||||
let fold = FoldStream { iterable, iterator, instruction };
|
||||
|
||||
let span = Span { left, right };
|
||||
validator.meet_fold_stream(&fold, span);
|
||||
|
||||
Box::new(Instruction::FoldStream(fold))
|
||||
},
|
||||
|
||||
<left: @L> "(" next <i:Alphanumeric> ")" <right: @R> => {
|
||||
let next = Next(i);
|
||||
<left: @L> "(" next <iterator_name:Scalar> ")" <right: @R> => {
|
||||
let iterator = Scalar { name: iterator_name };
|
||||
let next = Next { iterator };
|
||||
let span = Span { left, right };
|
||||
validator.met_next(&next, span);
|
||||
|
||||
@ -72,7 +82,7 @@ Instr: Box<Instruction<'input>> = {
|
||||
|
||||
"(" xor <l:Instr> <r:Instr> ")" => Box::new(Instruction::Xor(Xor(l, r))),
|
||||
|
||||
<left: @L> "(" match_ <l:Matchable> <r:Matchable> <i:Instr> ")" <right: @R> => {
|
||||
<left: @L> "(" match_ <l:Value> <r:Value> <i:Instr> ")" <right: @R> => {
|
||||
let match_ = Match { left_value: l, right_value: r, instruction: i};
|
||||
let span = Span { left, right };
|
||||
validator.met_match(&match_, span);
|
||||
@ -80,7 +90,7 @@ Instr: Box<Instruction<'input>> = {
|
||||
Box::new(Instruction::Match(match_))
|
||||
},
|
||||
|
||||
<left: @L> "(" mismatch <l:Matchable> <r:Matchable> <i:Instr> ")" <right: @R> => {
|
||||
<left: @L> "(" mismatch <l:Value> <r:Value> <i:Instr> ")" <right: @R> => {
|
||||
let mismatch = MisMatch { left_value: l, right_value: r, instruction: i};
|
||||
let span = Span { left, right };
|
||||
validator.met_mismatch(&mismatch, span);
|
||||
@ -91,7 +101,7 @@ Instr: Box<Instruction<'input>> = {
|
||||
! => { errors.push(<>); Box::new(Instruction::Error) },
|
||||
}
|
||||
|
||||
Args: Vec<CallInstrArgValue<'input>> = {
|
||||
Args: Vec<Value<'input>> = {
|
||||
"[" <args:(<Arg>)*> "]" => args
|
||||
}
|
||||
|
||||
@ -105,9 +115,17 @@ PeerPart: PeerPart<'input> = {
|
||||
"(" <pid:PeerId> <sid:ServiceId> ")" => PeerPart::PeerPkWithServiceId(pid, sid),
|
||||
}
|
||||
|
||||
Output: AstVariable<'input> = {
|
||||
<a:Alphanumeric> => AstVariable::Scalar(a),
|
||||
<s:Stream> => AstVariable::Stream(s),
|
||||
ApResult = ScriptVariable;
|
||||
CallOutput = ScriptVariable;
|
||||
|
||||
ScriptVariable: Variable<'input> = {
|
||||
<scalar:Scalar> => Variable::scalar(scalar),
|
||||
<stream:Stream> => Variable::stream(stream),
|
||||
};
|
||||
|
||||
FoldScalarIterable: ScalarWithLambda<'input> = {
|
||||
<scalar:Scalar> => ScalarWithLambda::new(scalar, None),
|
||||
<scalar:ScalarWithLambda> => ScalarWithLambda::new(scalar.0, Some(scalar.1)),
|
||||
};
|
||||
|
||||
Function = CallInstrValue;
|
||||
@ -115,71 +133,38 @@ PeerId = CallInstrValue;
|
||||
ServiceId = CallInstrValue;
|
||||
|
||||
CallInstrValue: CallInstrValue<'input> = {
|
||||
<l:Literal> => CallInstrValue::Literal(l),
|
||||
<a:Alphanumeric> => CallInstrValue::Variable(AstVariable::Scalar(a)),
|
||||
<s:Stream> => CallInstrValue::Variable(AstVariable::Stream(s)),
|
||||
<left: @L> <vl:VariableWithLambda> <right: @R> => {
|
||||
let variable = vl.0;
|
||||
let lambda = vl.1;
|
||||
|
||||
CallInstrValue::VariableWithLambda(VariableWithLambda::new(variable, lambda))
|
||||
},
|
||||
InitPeerId => CallInstrValue::InitPeerId,
|
||||
<l:Literal> => CallInstrValue::Literal(l),
|
||||
<scalar:Scalar> => CallInstrValue::Variable(VariableWithLambda::scalar(scalar)),
|
||||
<scalar:ScalarWithLambda> => CallInstrValue::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1)),
|
||||
<stream:Stream> => CallInstrValue::Variable(VariableWithLambda::stream(stream)),
|
||||
<stream:StreamWithLambda> => CallInstrValue::Variable(VariableWithLambda::stream_wl(stream.0, stream.1)),
|
||||
}
|
||||
|
||||
Arg = CallInstrArgValue;
|
||||
Arg = Value;
|
||||
|
||||
CallInstrArgValue: CallInstrArgValue<'input> = {
|
||||
<s:Literal> => CallInstrArgValue::Literal(s),
|
||||
<v:Alphanumeric> => CallInstrArgValue::Variable(AstVariable::Scalar(v)),
|
||||
<v:Stream> => CallInstrArgValue::Variable(AstVariable::Stream(v)),
|
||||
<vl:VariableWithLambda> => CallInstrArgValue::VariableWithLambda(VariableWithLambda::new(vl.0, vl.1)),
|
||||
<n:Number> => CallInstrArgValue::Number(n),
|
||||
<b:Boolean> => CallInstrArgValue::Boolean(b),
|
||||
InitPeerId => CallInstrArgValue::InitPeerId,
|
||||
EmptyArray => CallInstrArgValue::EmptyArray,
|
||||
<p:LastError> => CallInstrArgValue::LastError(p),
|
||||
Value: Value<'input> = {
|
||||
InitPeerId => Value::InitPeerId,
|
||||
<e:LastError> => Value::LastError(e),
|
||||
<l:Literal> => Value::Literal(l),
|
||||
<n:Number> => Value::Number(n),
|
||||
<b:Boolean> => Value::Boolean(b),
|
||||
EmptyArray => Value::EmptyArray,
|
||||
<scalar_name:Scalar> => Value::Variable(VariableWithLambda::scalar(scalar_name)),
|
||||
<scalar:ScalarWithLambda> => Value::Variable(VariableWithLambda::scalar_wl(scalar.0, scalar.1)),
|
||||
<stream_name:Stream> => Value::Variable(VariableWithLambda::stream(stream_name)),
|
||||
<stream:StreamWithLambda> => Value::Variable(VariableWithLambda::stream_wl(stream.0, stream.1)),
|
||||
}
|
||||
|
||||
ApArgument: ApArgument<'input> = {
|
||||
<a:Alphanumeric> => ApArgument::ScalarVariable(a),
|
||||
<vl:VariableWithLambda> => ApArgument::VariableWithLambda(VariableWithLambda::new(vl.0, vl.1)),
|
||||
InitPeerId => ApArgument::InitPeerId,
|
||||
<e:LastError> => ApArgument::LastError(e),
|
||||
<l:Literal> => ApArgument::Literal(l),
|
||||
<n:Number> => ApArgument::Number(n),
|
||||
<b:Boolean> => ApArgument::Boolean(b),
|
||||
EmptyArray => ApArgument::EmptyArray,
|
||||
<s:Literal> => ApArgument::Literal(s),
|
||||
<p:LastError> => ApArgument::LastError(p),
|
||||
};
|
||||
|
||||
ScalarIterable: IterableScalarValue<'input> = {
|
||||
<v:Alphanumeric> => IterableScalarValue::ScalarVariable(v),
|
||||
<l:@L> <vl:VariableWithLambda> <r:@R> => {
|
||||
use crate::parser::air::AstVariable::*;
|
||||
|
||||
let variable = vl.0;
|
||||
let lambda = vl.1;
|
||||
|
||||
let scalar_name = match variable {
|
||||
Stream(name) => {
|
||||
let token = Token::VariableWithLambda(variable, lambda.clone());
|
||||
errors.push(make_stream_iterable_error(l, token, r));
|
||||
name
|
||||
}
|
||||
Scalar(name) => name,
|
||||
};
|
||||
IterableScalarValue::VariableWithLambda { scalar_name, lambda }
|
||||
}
|
||||
}
|
||||
|
||||
Matchable: MatchableValue<'input> = {
|
||||
InitPeerId => MatchableValue::InitPeerId,
|
||||
<v:Alphanumeric> => MatchableValue::Variable(AstVariable::Scalar(v)),
|
||||
<v:Stream> => MatchableValue::Variable(AstVariable::Stream(v)),
|
||||
<s:Literal> => MatchableValue::Literal(s),
|
||||
<b:Boolean> => MatchableValue::Boolean(b),
|
||||
<n:Number> => MatchableValue::Number(n),
|
||||
EmptyArray => MatchableValue::EmptyArray,
|
||||
<vl:VariableWithLambda> => MatchableValue::VariableWithLambda(VariableWithLambda::new(vl.0, vl.1)),
|
||||
<scalar:Scalar> => ApArgument::Scalar(ScalarWithLambda { name: scalar, lambda: None }),
|
||||
<scalar:ScalarWithLambda> => ApArgument::Scalar(ScalarWithLambda { name: scalar.0, lambda: Some(scalar.1) }),
|
||||
}
|
||||
|
||||
extern {
|
||||
@ -194,9 +179,10 @@ extern {
|
||||
EmptyArray => Token::SquareBrackets,
|
||||
|
||||
Literal => Token::StringLiteral(<&'input str>),
|
||||
Alphanumeric => Token::Alphanumeric(<&'input str>),
|
||||
Stream => Token::Stream(<&'input str>),
|
||||
VariableWithLambda => Token::VariableWithLambda(<AstVariable<'input>>, <LambdaAST<'input>>),
|
||||
Scalar => Token::Scalar { name:<&'input str> },
|
||||
ScalarWithLambda => Token::ScalarWithLambda { name: <&'input str>, lambda: <LambdaAST<'input>>},
|
||||
Stream => Token::Stream { name: <&'input str> },
|
||||
StreamWithLambda => Token::StreamWithLambda {name: <&'input str>, lambda:<LambdaAST<'input>>},
|
||||
Number => Token::Number(<Number>),
|
||||
Boolean => Token::Boolean(<bool>),
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
use super::air;
|
||||
use super::ast::Instruction;
|
||||
use super::lexer::AIRLexer;
|
||||
use super::lexer::LexerError;
|
||||
use super::lexer::Token;
|
||||
use super::ParserError;
|
||||
|
||||
use crate::ast::Instruction;
|
||||
use crate::parser::VariableValidator;
|
||||
use air::AIRParser;
|
||||
|
||||
@ -133,6 +133,9 @@ fn parser_error_to_label(file_id: usize, error: ParserError) -> Label<usize> {
|
||||
UndefinedVariable(start, end, _) => {
|
||||
Label::primary(file_id, start..end).with_message(error.to_string())
|
||||
}
|
||||
InvalidCallTriplet(start, end) => {
|
||||
Label::primary(file_id, start..end).with_message(error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,25 +183,3 @@ fn lexical_error_to_label(file_id: usize, error: LexerError) -> Label<usize> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! make_user_error(
|
||||
($error_type:ident, $start_pos: ident, $token:ident, $end_pos: ident) => { {
|
||||
let error = ParserError::$error_type($start_pos, $end_pos);
|
||||
let error = ParseError::User { error };
|
||||
|
||||
let dropped_tokens = vec![($start_pos, $token, $end_pos)];
|
||||
|
||||
ErrorRecovery {
|
||||
error,
|
||||
dropped_tokens,
|
||||
}
|
||||
}}
|
||||
);
|
||||
|
||||
pub(super) fn make_stream_iterable_error(
|
||||
start_pos: usize,
|
||||
token: Token<'_>,
|
||||
end_pos: usize,
|
||||
) -> ErrorRecovery<usize, Token<'_>, ParserError> {
|
||||
make_user_error!(LambdaAppliedToStream, start_pos, token, end_pos)
|
||||
}
|
||||
|
34
crates/air-lib/air-parser/src/parser/air_utils/mod.rs
Normal file
34
crates/air-lib/air-parser/src/parser/air_utils/mod.rs
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod triplet;
|
||||
|
||||
pub(crate) use triplet::try_to_raw_triplet;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! make_user_error(
|
||||
($error_type:ident, $start_pos: ident, $token:expr, $end_pos: ident) => { {
|
||||
let error = crate::parser::ParserError::$error_type($start_pos, $end_pos);
|
||||
let error = lalrpop_util::ParseError::User { error };
|
||||
|
||||
let dropped_tokens = vec![($start_pos, $token, $end_pos)];
|
||||
|
||||
ErrorRecovery {
|
||||
error,
|
||||
dropped_tokens,
|
||||
}
|
||||
}}
|
||||
);
|
49
crates/air-lib/air-parser/src/parser/air_utils/triplet.rs
Normal file
49
crates/air-lib/air-parser/src/parser/air_utils/triplet.rs
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::ast::FunctionPart;
|
||||
use crate::ast::PeerPart;
|
||||
use crate::ast::Triplet;
|
||||
|
||||
/// Build a `Triplet` from `Call`'s `PeerPart` and `FunctionPart`
|
||||
pub(crate) fn try_to_raw_triplet<'i>(
|
||||
peer: PeerPart<'i>,
|
||||
f: FunctionPart<'i>,
|
||||
) -> Option<Triplet<'i>> {
|
||||
use FunctionPart::*;
|
||||
use PeerPart::*;
|
||||
|
||||
let (peer_pk, service_id, function_name) = match (peer, f) {
|
||||
(PeerPkWithServiceId(peer_pk, _), ServiceIdWithFuncName(service_id, func_name)) => {
|
||||
(peer_pk, service_id, func_name)
|
||||
}
|
||||
(PeerPkWithServiceId(peer_pk, peer_service_id), FuncName(func_name)) => {
|
||||
(peer_pk, peer_service_id, func_name)
|
||||
}
|
||||
(PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => {
|
||||
(peer_pk, service_id, func_name)
|
||||
}
|
||||
(PeerPk(_), FuncName(_)) => return None,
|
||||
};
|
||||
|
||||
let raw_triplet = Triplet {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
};
|
||||
|
||||
Some(raw_triplet)
|
||||
}
|
@ -1,178 +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.
|
||||
*/
|
||||
|
||||
mod impls;
|
||||
mod traits;
|
||||
|
||||
pub use crate::parser::lexer::AstVariable;
|
||||
pub use crate::parser::lexer::LastErrorPath;
|
||||
pub use crate::parser::lexer::Number;
|
||||
|
||||
use air_lambda_parser::LambdaAST;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(clippy::large_enum_variant)] // for Null and Error variants
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum Instruction<'i> {
|
||||
Null(Null),
|
||||
Call(Call<'i>),
|
||||
Ap(Ap<'i>),
|
||||
Seq(Seq<'i>),
|
||||
Par(Par<'i>),
|
||||
Xor(Xor<'i>),
|
||||
Match(Match<'i>),
|
||||
MisMatch(MisMatch<'i>),
|
||||
FoldScalar(FoldScalar<'i>),
|
||||
FoldStream(FoldStream<'i>),
|
||||
Next(Next<'i>),
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum PeerPart<'i> {
|
||||
PeerPk(CallInstrValue<'i>),
|
||||
PeerPkWithServiceId(CallInstrValue<'i>, CallInstrValue<'i>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub enum FunctionPart<'i> {
|
||||
FuncName(CallInstrValue<'i>),
|
||||
ServiceIdWithFuncName(CallInstrValue<'i>, CallInstrValue<'i>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Call<'i> {
|
||||
pub peer_part: PeerPart<'i>,
|
||||
pub function_part: FunctionPart<'i>,
|
||||
pub args: Rc<Vec<CallInstrArgValue<'i>>>,
|
||||
pub output: CallOutputValue<'i>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum ApArgument<'i> {
|
||||
ScalarVariable(&'i str),
|
||||
VariableWithLambda(VariableWithLambda<'i>),
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
Literal(&'i str),
|
||||
EmptyArray,
|
||||
LastError(LastErrorPath),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Ap<'i> {
|
||||
pub argument: ApArgument<'i>,
|
||||
pub result: AstVariable<'i>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum CallInstrValue<'i> {
|
||||
InitPeerId,
|
||||
Literal(&'i str),
|
||||
Variable(AstVariable<'i>),
|
||||
VariableWithLambda(VariableWithLambda<'i>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum CallInstrArgValue<'i> {
|
||||
InitPeerId,
|
||||
LastError(LastErrorPath),
|
||||
Literal(&'i str),
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
EmptyArray, // only empty arrays are allowed now
|
||||
Variable(AstVariable<'i>),
|
||||
VariableWithLambda(VariableWithLambda<'i>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum IterableScalarValue<'i> {
|
||||
ScalarVariable(&'i str),
|
||||
VariableWithLambda {
|
||||
scalar_name: &'i str,
|
||||
lambda: LambdaAST<'i>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub enum MatchableValue<'i> {
|
||||
InitPeerId,
|
||||
Literal(&'i str),
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
EmptyArray,
|
||||
Variable(AstVariable<'i>),
|
||||
VariableWithLambda(VariableWithLambda<'i>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq, Clone)]
|
||||
pub enum CallOutputValue<'i> {
|
||||
Variable(AstVariable<'i>),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Seq<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Par<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Xor<'i>(pub Box<Instruction<'i>>, pub Box<Instruction<'i>>);
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Match<'i> {
|
||||
pub left_value: MatchableValue<'i>,
|
||||
pub right_value: MatchableValue<'i>,
|
||||
pub instruction: Box<Instruction<'i>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct MisMatch<'i> {
|
||||
pub left_value: MatchableValue<'i>,
|
||||
pub right_value: MatchableValue<'i>,
|
||||
pub instruction: Box<Instruction<'i>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct FoldScalar<'i> {
|
||||
pub iterable: IterableScalarValue<'i>,
|
||||
pub iterator: &'i str,
|
||||
pub instruction: Rc<Instruction<'i>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct FoldStream<'i> {
|
||||
pub stream_name: &'i str,
|
||||
pub iterator: &'i str,
|
||||
pub instruction: Rc<Instruction<'i>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Next<'i>(pub &'i str);
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq)]
|
||||
pub struct Null;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct VariableWithLambda<'i> {
|
||||
pub variable: AstVariable<'i>,
|
||||
#[serde(borrow)]
|
||||
pub lambda: LambdaAST<'i>,
|
||||
}
|
@ -1,47 +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 super::*;
|
||||
use air_lambda_parser::ValueAccessor;
|
||||
|
||||
impl<'i> Ap<'i> {
|
||||
pub fn new(argument: ApArgument<'i>, result: AstVariable<'i>) -> Self {
|
||||
Self { argument, result }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> VariableWithLambda<'i> {
|
||||
pub fn new(variable: AstVariable<'i>, lambda: LambdaAST<'i>) -> Self {
|
||||
Self { variable, lambda }
|
||||
}
|
||||
|
||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
||||
pub fn from_raw_algebras(variable: AstVariable<'i>, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
||||
Self { variable, lambda }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> IterableScalarValue<'i> {
|
||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
||||
pub fn new_vl(scalar_name: &'i str, lambda: Vec<ValueAccessor<'i>>) -> Self {
|
||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
||||
Self::VariableWithLambda {
|
||||
scalar_name,
|
||||
lambda,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,228 +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 super::*;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for CallInstrArgValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use CallInstrArgValue::*;
|
||||
|
||||
match self {
|
||||
InitPeerId => write!(f, "%init_peer_id%"),
|
||||
LastError(error_path) => write!(f, "%last_error%{}", error_path),
|
||||
Literal(str) => write!(f, r#""{}""#, str),
|
||||
Number(number) => write!(f, "{}", number),
|
||||
Boolean(bool) => write!(f, "{}", bool),
|
||||
EmptyArray => write!(f, "[]"),
|
||||
Variable(str) => write!(f, "{}", str),
|
||||
VariableWithLambda(vl) => write!(f, "{}", vl),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CallInstrValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use CallInstrValue::*;
|
||||
|
||||
match self {
|
||||
InitPeerId => write!(f, "%init_peer_id%"),
|
||||
Literal(str) => write!(f, r#""{}""#, str),
|
||||
Variable(str) => write!(f, "{}", str),
|
||||
VariableWithLambda(vl) => write!(f, "{}", vl),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IterableScalarValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use IterableScalarValue::*;
|
||||
|
||||
match self {
|
||||
ScalarVariable(str) => write!(f, "{}", str),
|
||||
VariableWithLambda {
|
||||
scalar_name,
|
||||
lambda,
|
||||
} => write!(f, "{}.$.{:?}", scalar_name, lambda),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MatchableValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use MatchableValue::*;
|
||||
|
||||
match self {
|
||||
InitPeerId => write!(f, "%init_peer_id%"),
|
||||
Literal(str) => write!(f, r#""{}""#, str),
|
||||
Number(number) => write!(f, "{}", number),
|
||||
Boolean(bool) => write!(f, "{}", bool),
|
||||
EmptyArray => write!(f, "[]"),
|
||||
Variable(str) => write!(f, "{}", str),
|
||||
VariableWithLambda(vl) => write!(f, "{}", vl),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CallOutputValue<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use CallOutputValue::*;
|
||||
|
||||
match self {
|
||||
Variable(variable) => write!(f, "{}", variable),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PeerPart<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use PeerPart::*;
|
||||
|
||||
match self {
|
||||
PeerPk(peer_pk) => write!(f, "{}", peer_pk),
|
||||
PeerPkWithServiceId(peer_pk, service_id) => write!(f, "({} {})", peer_pk, service_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FunctionPart<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use FunctionPart::*;
|
||||
|
||||
match self {
|
||||
FuncName(func_name) => write!(f, "{}", func_name),
|
||||
ServiceIdWithFuncName(service_id, func_name) => {
|
||||
write!(f, "({} {})", service_id, func_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Instruction<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use Instruction::*;
|
||||
|
||||
match self {
|
||||
Null(null) => write!(f, "{}", null),
|
||||
Call(call) => write!(f, "{}", call),
|
||||
Ap(ap) => write!(f, "{}", ap),
|
||||
Seq(seq) => write!(f, "{}", seq),
|
||||
Par(par) => write!(f, "{}", par),
|
||||
Xor(xor) => write!(f, "{}", xor),
|
||||
Match(match_) => write!(f, "{}", match_),
|
||||
MisMatch(mismatch) => write!(f, "{}", mismatch),
|
||||
FoldScalar(fold) => write!(f, "{}", fold),
|
||||
FoldStream(fold) => write!(f, "{}", fold),
|
||||
Next(next) => write!(f, "{}", next),
|
||||
Error => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Call<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use itertools::Itertools;
|
||||
|
||||
write!(f, "call {} {}", self.peer_part, self.function_part)?;
|
||||
|
||||
let args = self.args.iter().map(|arg| format!("{}", arg)).join(" ");
|
||||
write!(f, " [{}]", args)?;
|
||||
|
||||
write!(f, " {}", self.output)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Ap<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ap {} {}", self.argument, self.result)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ApArgument<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ApArgument::ScalarVariable(name) => write!(f, "{}", name),
|
||||
ApArgument::VariableWithLambda(vl) => write!(f, "{}", vl),
|
||||
ApArgument::LastError(error_path) => write!(f, "{}", error_path),
|
||||
ApArgument::Number(value) => write!(f, "{}", value),
|
||||
ApArgument::Boolean(value) => write!(f, "{}", value),
|
||||
ApArgument::Literal(value) => write!(f, "{}", value),
|
||||
ApArgument::EmptyArray => write!(f, "[]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FoldScalar<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "fold {} {}", self.iterable, self.iterator)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FoldStream<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "fold {} {}", self.stream_name, self.iterator)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Seq<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "seq")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Par<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "par")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Null {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "null")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Xor<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "xor")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Match<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "match {} {}", self.left_value, self.right_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MisMatch<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "mismatch {} {}", self.left_value, self.right_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Next<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "next")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VariableWithLambda<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}.$.{:?}", self.variable, self.lambda,)
|
||||
}
|
||||
}
|
@ -30,6 +30,10 @@ pub enum ParserError {
|
||||
|
||||
#[error("iterable '{2}' wasn't defined")]
|
||||
UndefinedIterable(usize, usize, String),
|
||||
|
||||
/// Semantic errors in a call instructions.
|
||||
#[error("call should have service id specified by peer part or function part")]
|
||||
InvalidCallTriplet(usize, usize),
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for ParserError {
|
||||
|
@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::AstVariable;
|
||||
use super::LexerError;
|
||||
use super::LexerResult;
|
||||
use super::Token;
|
||||
@ -273,59 +272,57 @@ impl<'input> CallVariableParser<'input> {
|
||||
self.current_pos() == self.string_to_parse.len() - 1
|
||||
}
|
||||
|
||||
fn to_variable<'v>(&self, variable_name: &'v str) -> AstVariable<'v> {
|
||||
fn to_variable_token<'v>(&self, name: &'v str) -> Token<'v> {
|
||||
if self.state.is_first_stream_tag {
|
||||
// TODO: cut the stream tag after the refactoring.
|
||||
AstVariable::Stream(variable_name)
|
||||
Token::Stream { name }
|
||||
} else {
|
||||
AstVariable::Scalar(variable_name)
|
||||
Token::Scalar { name }
|
||||
}
|
||||
}
|
||||
|
||||
fn try_to_variable_and_lambda(
|
||||
&self,
|
||||
pos: usize,
|
||||
) -> LexerResult<(&'input str, LambdaAST<'input>)> {
|
||||
// +2 to ignore ".$" prefix
|
||||
let lambda = crate::parse_lambda(&self.string_to_parse[pos + 2..]).map_err(|e| {
|
||||
LexerError::LambdaParserError(
|
||||
self.start_pos + pos,
|
||||
self.start_pos + self.string_to_parse.len(),
|
||||
e.to_string(),
|
||||
)
|
||||
})?;
|
||||
fn to_variable_token_with_lambda<'v>(&self, name: &'v str, lambda: LambdaAST<'v>) -> Token<'v> {
|
||||
if self.state.is_first_stream_tag {
|
||||
Token::StreamWithLambda { name, lambda }
|
||||
} else {
|
||||
Token::ScalarWithLambda { name, lambda }
|
||||
}
|
||||
}
|
||||
|
||||
Ok((&self.string_to_parse[0..pos], lambda))
|
||||
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| {
|
||||
LexerError::LambdaParserError(
|
||||
self.start_pos + lambda_start_pos,
|
||||
self.start_pos + self.string_to_parse.len(),
|
||||
e.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let token =
|
||||
self.to_variable_token_with_lambda(&self.string_to_parse[0..lambda_start_pos], lambda);
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
fn to_token(&self) -> LexerResult<Token<'input>> {
|
||||
use super::token::UnparsedNumber;
|
||||
|
||||
match (self.is_possible_to_parse_as_number(), self.dot_met()) {
|
||||
(true, false) => {
|
||||
let is_number = self.is_possible_to_parse_as_number();
|
||||
let token = match (is_number, self.state.first_dot_met_pos) {
|
||||
(true, None) => {
|
||||
let number = UnparsedNumber::Int(self.string_to_parse, self.start_pos);
|
||||
let number: super::Number = number.try_into()?;
|
||||
Ok(number.into())
|
||||
number.into()
|
||||
}
|
||||
(true, true) => {
|
||||
(true, Some(_)) => {
|
||||
let number = UnparsedNumber::Float(self.string_to_parse, self.start_pos);
|
||||
let number: super::Number = number.try_into()?;
|
||||
Ok(number.into())
|
||||
number.into()
|
||||
}
|
||||
(false, false) => {
|
||||
if self.state.is_first_stream_tag {
|
||||
Ok(Token::Stream(self.string_to_parse))
|
||||
} else {
|
||||
Ok(Token::Alphanumeric(self.string_to_parse))
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
let lambda_start_pos = self.state.first_dot_met_pos.unwrap();
|
||||
let (variable, lambda) = self.try_to_variable_and_lambda(lambda_start_pos)?;
|
||||
let variable = self.to_variable(variable);
|
||||
(false, None) => self.to_variable_token(self.string_to_parse),
|
||||
(false, Some(lambda_start_pos)) => self.try_to_variable_and_lambda(lambda_start_pos)?,
|
||||
};
|
||||
|
||||
Ok(Token::VariableWithLambda(variable, lambda))
|
||||
}
|
||||
}
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ mod tests;
|
||||
|
||||
pub use air_lexer::AIRLexer;
|
||||
pub use errors::LexerError;
|
||||
pub use token::AstVariable;
|
||||
pub use token::LastErrorPath;
|
||||
pub use token::Number;
|
||||
pub use token::Token;
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
use super::air_lexer::Spanned;
|
||||
use super::AIRLexer;
|
||||
use super::AstVariable;
|
||||
use super::LastErrorPath;
|
||||
use super::LexerError;
|
||||
use super::Number;
|
||||
@ -159,7 +158,10 @@ fn init_peer_id() {
|
||||
fn stream() {
|
||||
const STREAM: &str = "$stream____asdasd";
|
||||
|
||||
lexer_test(STREAM, Single(Ok((0, Token::Stream(STREAM), STREAM.len()))));
|
||||
lexer_test(
|
||||
STREAM,
|
||||
Single(Ok((0, Token::Stream { name: STREAM }, STREAM.len()))),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -267,20 +269,22 @@ fn too_big_float_number() {
|
||||
fn lambda() {
|
||||
// this lambda contains all allowed in lambda characters
|
||||
const LAMBDA: &str = r#"value.$.field[1]"#;
|
||||
let variable = AstVariable::Scalar("value");
|
||||
|
||||
lexer_test(
|
||||
LAMBDA,
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::VariableWithLambda(variable, unsafe {
|
||||
LambdaAST::new_unchecked(vec![
|
||||
ValueAccessor::FieldAccess {
|
||||
field_name: "field",
|
||||
},
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
])
|
||||
}),
|
||||
Token::ScalarWithLambda {
|
||||
name: "value",
|
||||
lambda: unsafe {
|
||||
LambdaAST::new_unchecked(vec![
|
||||
ValueAccessor::FieldAccess {
|
||||
field_name: "field",
|
||||
},
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
])
|
||||
},
|
||||
},
|
||||
LAMBDA.len(),
|
||||
))),
|
||||
);
|
||||
@ -447,7 +451,9 @@ fn booleans() {
|
||||
NON_BOOL_CONST,
|
||||
Single(Ok((
|
||||
0,
|
||||
Token::Alphanumeric(NON_BOOL_CONST),
|
||||
Token::Scalar {
|
||||
name: NON_BOOL_CONST,
|
||||
},
|
||||
NON_BOOL_CONST.len(),
|
||||
))),
|
||||
);
|
||||
|
@ -32,9 +32,20 @@ pub enum Token<'input> {
|
||||
SquareBrackets, // [] symbolize empty array, it's possible to have it only in an argument position
|
||||
|
||||
StringLiteral(&'input str),
|
||||
Alphanumeric(&'input str),
|
||||
Stream(&'input str),
|
||||
VariableWithLambda(AstVariable<'input>, LambdaAST<'input>),
|
||||
Scalar {
|
||||
name: &'input str,
|
||||
},
|
||||
ScalarWithLambda {
|
||||
name: &'input str,
|
||||
lambda: LambdaAST<'input>,
|
||||
},
|
||||
Stream {
|
||||
name: &'input str,
|
||||
},
|
||||
StreamWithLambda {
|
||||
name: &'input str,
|
||||
lambda: LambdaAST<'input>,
|
||||
},
|
||||
Number(Number),
|
||||
Boolean(bool),
|
||||
|
||||
@ -53,12 +64,6 @@ pub enum Token<'input> {
|
||||
MisMatch,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum AstVariable<'input> {
|
||||
Scalar(&'input str),
|
||||
Stream(&'input str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum LastErrorPath {
|
||||
// %last_error%.instruction
|
||||
|
@ -43,17 +43,6 @@ impl fmt::Display for Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AstVariable<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use AstVariable::*;
|
||||
|
||||
match self {
|
||||
Scalar(name) => write!(f, "{}", name),
|
||||
Stream(name) => write!(f, "&{}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Number> for Token<'_> {
|
||||
fn from(value: Number) -> Self {
|
||||
Token::Number(value)
|
||||
|
@ -15,14 +15,14 @@
|
||||
*/
|
||||
|
||||
pub mod air_parser;
|
||||
mod lexer;
|
||||
mod air_utils;
|
||||
pub(crate) mod lexer;
|
||||
|
||||
// air is auto-generated, so exclude it from `cargo fmt -- --check` and `cargo clippy`
|
||||
#[rustfmt::skip]
|
||||
#[allow(clippy::all)]
|
||||
mod air;
|
||||
|
||||
pub mod ast;
|
||||
mod errors;
|
||||
mod validator;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
76
crates/air-lib/air-parser/src/parser/tests/ap.rs
Normal file
76
crates/air-lib/air-parser/src/parser/tests/ap.rs
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
|
||||
#[test]
|
||||
fn ap_with_literal() {
|
||||
let source_code = r#"
|
||||
(ap "some_string" $stream)
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::Literal("some_string"),
|
||||
Variable::stream("$stream"),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ap_with_number() {
|
||||
let source_code = r#"
|
||||
(ap -100 $stream)
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::Number(Number::Int(-100)),
|
||||
Variable::stream("$stream"),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ap_with_bool() {
|
||||
let source_code = r#"
|
||||
(ap true $stream)
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(ApArgument::Boolean(true), Variable::stream("$stream"));
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ap_with_last_error() {
|
||||
let source_code = r#"
|
||||
(ap %last_error%.$.msg! $stream)
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = ap(
|
||||
ApArgument::LastError(LastErrorPath::Message),
|
||||
Variable::stream("$stream"),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
494
crates/air-lib/air-parser/src/parser/tests/call.rs
Normal file
494
crates/air-lib/air-parser/src/parser/tests/call.rs
Normal file
@ -0,0 +1,494 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
use crate::parser::ParserError;
|
||||
|
||||
use air_lambda_ast::ValueAccessor;
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn parse_json_path() {
|
||||
let source_code = r#"
|
||||
(call peer_id.$.a! ("service_id" "function_name") ["hello" name] $void)
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"peer_id",
|
||||
vec![ValueAccessor::FieldAccess { field_name: "a" }],
|
||||
)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal("hello"),
|
||||
Value::Variable(VariableWithLambda::scalar("name")),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::stream("$void")),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_array() {
|
||||
let source_code = r#"
|
||||
(call peer_id (service_id "function_name") ["" [] arg])
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id")),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal(""),
|
||||
Value::EmptyArray,
|
||||
Value::Variable(VariableWithLambda::scalar("arg")),
|
||||
]),
|
||||
CallOutputValue::None,
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_array_2() {
|
||||
let source_code = r#"
|
||||
(call peer_id ("service_id" "function_name") [k [] []])
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Variable(VariableWithLambda::scalar("k")),
|
||||
Value::EmptyArray,
|
||||
Value::EmptyArray,
|
||||
]),
|
||||
CallOutputValue::None,
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_undefined_variable() {
|
||||
let source_code = r#"
|
||||
(call id.$.a ("" "f") ["hello" name] $void)
|
||||
"#;
|
||||
|
||||
let lexer = crate::AIRLexer::new(source_code);
|
||||
|
||||
let parser = crate::AIRParser::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parser shouldn't fail");
|
||||
|
||||
let errors = validator.finalize();
|
||||
|
||||
assert_eq!(errors.len(), 2);
|
||||
for i in 0..2 {
|
||||
let error = &errors[i].error;
|
||||
let parser_error = match error {
|
||||
ParseError::User { error } => error,
|
||||
_ => panic!("unexpected error type"),
|
||||
};
|
||||
|
||||
assert!(matches!(parser_error, ParserError::UndefinedVariable(..)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_undefined_stream_without_json_path() {
|
||||
let source_code = r#"
|
||||
(call "" ("" "") [$stream])
|
||||
"#;
|
||||
|
||||
let lexer = crate::AIRLexer::new(source_code);
|
||||
|
||||
let parser = crate::AIRParser::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parser shouldn't fail");
|
||||
|
||||
let errors = validator.finalize();
|
||||
|
||||
assert!(errors.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_undefined_stream_with_lambda() {
|
||||
let source_code = r#"
|
||||
(call "" ("" "") [$stream.$.json_path])
|
||||
"#;
|
||||
|
||||
let lexer = crate::AIRLexer::new(source_code);
|
||||
|
||||
let parser = crate::AIRParser::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parser shouldn't fail");
|
||||
|
||||
let errors = validator.finalize();
|
||||
|
||||
assert_eq!(errors.len(), 1);
|
||||
let error = &errors[0].error;
|
||||
let parser_error = match error {
|
||||
ParseError::User { error } => error,
|
||||
_ => panic!("unexpected error type"),
|
||||
};
|
||||
|
||||
assert!(matches!(parser_error, ParserError::UndefinedVariable(..)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_call_with_invalid_triplet() {
|
||||
let source_code = r#"
|
||||
(call "" "" [$stream.$.json_path])
|
||||
"#;
|
||||
|
||||
let lexer = crate::AIRLexer::new(source_code);
|
||||
|
||||
let parser = crate::AIRParser::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parser shouldn't fail");
|
||||
|
||||
assert_eq!(errors.len(), 1);
|
||||
let error = &errors[0].error;
|
||||
let parser_error = match error {
|
||||
ParseError::User { error } => error,
|
||||
_ => panic!("unexpected error type"),
|
||||
};
|
||||
|
||||
assert!(matches!(parser_error, ParserError::InvalidCallTriplet(..)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_json_path_complex() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(call m.$.[1]! ("service_id" "function_name") [] void)
|
||||
(call m.$.abc[0].cde[1][0].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 }],
|
||||
)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("void")),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"m",
|
||||
vec![
|
||||
ValueAccessor::FieldAccess { field_name: "abc" },
|
||||
ValueAccessor::ArrayAccess { idx: 0 },
|
||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
ValueAccessor::ArrayAccess { idx: 0 },
|
||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
],
|
||||
)),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("void")),
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_path_square_braces() {
|
||||
let source_code = r#"
|
||||
(call u.$.peer_id! ("return" "") [u.$[1].cde[0][0].abc u.$.name] $void)
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"u",
|
||||
vec![ValueAccessor::FieldAccess {
|
||||
field_name: "peer_id",
|
||||
}],
|
||||
)),
|
||||
CallInstrValue::Literal("return"),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![
|
||||
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"u",
|
||||
vec![
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
ValueAccessor::FieldAccess { field_name: "cde" },
|
||||
ValueAccessor::ArrayAccess { idx: 0 },
|
||||
ValueAccessor::ArrayAccess { idx: 0 },
|
||||
ValueAccessor::FieldAccess { field_name: "abc" },
|
||||
],
|
||||
)),
|
||||
Value::Variable(VariableWithLambda::from_raw_lambda_scalar(
|
||||
"u",
|
||||
vec![ValueAccessor::FieldAccess { field_name: "name" }],
|
||||
)),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::stream("$void")),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_init_peer_id() {
|
||||
let peer_id = "some_peer_id";
|
||||
let source_code = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call "{}" ("local_service_id" "local_fn_name") [])
|
||||
(call %init_peer_id% ("service_id" "fn_name") [])
|
||||
)"#,
|
||||
peer_id
|
||||
);
|
||||
|
||||
let instruction = parse(&source_code);
|
||||
let expected = seq(
|
||||
call(
|
||||
CallInstrValue::Literal(peer_id),
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
call(
|
||||
CallInstrValue::InitPeerId,
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_last_error() {
|
||||
let source_code = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call %init_peer_id% ("service_id" "fn_name") [%last_error%])
|
||||
(null)
|
||||
)"#,
|
||||
);
|
||||
|
||||
let instruction = parse(&source_code);
|
||||
let expected = seq(
|
||||
call(
|
||||
CallInstrValue::InitPeerId,
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
Rc::new(vec![Value::LastError(LastErrorPath::None)]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
null(),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seq_par_call() {
|
||||
let peer_id = "some_peer_id";
|
||||
let source_code = format!(
|
||||
r#"
|
||||
(seq
|
||||
(par
|
||||
(call "{0}" ("local_service_id" "local_fn_name") [] result_1)
|
||||
(call "{0}" ("service_id" "fn_name") [] g)
|
||||
)
|
||||
(call "{0}" ("local_service_id" "local_fn_name") [] result_2)
|
||||
)"#,
|
||||
peer_id,
|
||||
);
|
||||
|
||||
let instruction = parse(&source_code);
|
||||
let expected = seq(
|
||||
par(
|
||||
call(
|
||||
CallInstrValue::Literal(peer_id),
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("result_1")),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal(peer_id),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("g")),
|
||||
),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal(peer_id),
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::Variable(Variable::scalar("result_2")),
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seq_with_empty_and_dash() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(seq
|
||||
(seq
|
||||
(call "set_variables" ("" "") ["module-bytes"] module-bytes)
|
||||
(call "set_variables" ("" "") ["module_config"] module_config)
|
||||
)
|
||||
(call "set_variables" ("" "") ["blueprint"] blueprint)
|
||||
)
|
||||
(seq
|
||||
(call "A" ("add_module" "") [module-bytes module_config] module)
|
||||
(seq
|
||||
(call "A" ("add_blueprint" "") [blueprint] blueprint_id)
|
||||
(seq
|
||||
(call "A" ("create" "") [blueprint_id] service_id)
|
||||
(call "remote_peer_id" ("" "") [service_id] client_result)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
seq(
|
||||
seq(
|
||||
call(
|
||||
CallInstrValue::Literal("set_variables"),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Literal("module-bytes")]),
|
||||
CallOutputValue::Variable(Variable::scalar("module-bytes")),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("set_variables"),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Literal("module_config")]),
|
||||
CallOutputValue::Variable(Variable::scalar("module_config")),
|
||||
),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("set_variables"),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Literal("blueprint")]),
|
||||
CallOutputValue::Variable(Variable::scalar("blueprint")),
|
||||
),
|
||||
),
|
||||
seq(
|
||||
call(
|
||||
CallInstrValue::Literal("A"),
|
||||
CallInstrValue::Literal("add_module"),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![
|
||||
Value::Variable(VariableWithLambda::scalar("module-bytes")),
|
||||
Value::Variable(VariableWithLambda::scalar("module_config")),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::scalar("module")),
|
||||
),
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
triplet: Triplet {
|
||||
peer_pk: CallInstrValue::Literal("A"),
|
||||
service_id: CallInstrValue::Literal("add_blueprint"),
|
||||
function_name: CallInstrValue::Literal(""),
|
||||
},
|
||||
args: Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
|
||||
"blueprint",
|
||||
))]),
|
||||
output: CallOutputValue::Variable(Variable::scalar("blueprint_id")),
|
||||
}),
|
||||
seq(
|
||||
call(
|
||||
CallInstrValue::Literal("A"),
|
||||
CallInstrValue::Literal("create"),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
|
||||
"blueprint_id",
|
||||
))]),
|
||||
CallOutputValue::Variable(Variable::scalar("service_id")),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("remote_peer_id"),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
|
||||
"service_id",
|
||||
))]),
|
||||
CallOutputValue::Variable(Variable::scalar("client_result")),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_output() {
|
||||
let source_code = r#"
|
||||
(call peer (service fname) [])
|
||||
"#;
|
||||
|
||||
let actual = parse(source_code);
|
||||
|
||||
let expected = call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("fname")),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
121
crates/air-lib/air-parser/src/parser/tests/dsl.rs
Normal file
121
crates/air-lib/air-parser/src/parser/tests/dsl.rs
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::ast::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(super) fn call<'i>(
|
||||
peer_pk: CallInstrValue<'i>,
|
||||
service_id: CallInstrValue<'i>,
|
||||
function_name: CallInstrValue<'i>,
|
||||
args: Rc<Vec<Value<'i>>>,
|
||||
output: CallOutputValue<'i>,
|
||||
) -> Instruction<'i> {
|
||||
let triplet = Triplet {
|
||||
peer_pk,
|
||||
service_id,
|
||||
function_name,
|
||||
};
|
||||
|
||||
Instruction::Call(Call {
|
||||
triplet,
|
||||
args,
|
||||
output,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn seq<'a>(l: Instruction<'a>, r: Instruction<'a>) -> Instruction<'a> {
|
||||
Instruction::Seq(Seq(Box::new(l), Box::new(r)))
|
||||
}
|
||||
|
||||
pub(super) fn par<'a>(l: Instruction<'a>, r: Instruction<'a>) -> Instruction<'a> {
|
||||
Instruction::Par(Par(Box::new(l), Box::new(r)))
|
||||
}
|
||||
|
||||
pub(super) fn xor<'a>(l: Instruction<'a>, r: Instruction<'a>) -> Instruction<'a> {
|
||||
Instruction::Xor(Xor(Box::new(l), Box::new(r)))
|
||||
}
|
||||
|
||||
pub(super) fn seqnn() -> Instruction<'static> {
|
||||
seq(null(), null())
|
||||
}
|
||||
|
||||
pub(super) fn null() -> Instruction<'static> {
|
||||
Instruction::Null(Null)
|
||||
}
|
||||
|
||||
pub(super) fn fold_scalar<'a>(
|
||||
iterable: ScalarWithLambda<'a>,
|
||||
iterator: &'a str,
|
||||
instruction: Instruction<'a>,
|
||||
) -> Instruction<'a> {
|
||||
Instruction::FoldScalar(FoldScalar {
|
||||
iterable,
|
||||
iterator: Scalar::new(iterator),
|
||||
instruction: std::rc::Rc::new(instruction),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn fold_stream<'a>(
|
||||
stream_name: &'a str,
|
||||
iterator: &'a str,
|
||||
instruction: Instruction<'a>,
|
||||
) -> Instruction<'a> {
|
||||
Instruction::FoldStream(FoldStream {
|
||||
iterable: Stream::new(stream_name),
|
||||
iterator: Scalar::new(iterator),
|
||||
instruction: std::rc::Rc::new(instruction),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn match_<'a>(
|
||||
left_value: Value<'a>,
|
||||
right_value: Value<'a>,
|
||||
instruction: Instruction<'a>,
|
||||
) -> Instruction<'a> {
|
||||
Instruction::Match(Match {
|
||||
left_value,
|
||||
right_value,
|
||||
instruction: Box::new(instruction),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn mismatch<'a>(
|
||||
left_value: Value<'a>,
|
||||
right_value: Value<'a>,
|
||||
instruction: Instruction<'a>,
|
||||
) -> Instruction<'a> {
|
||||
Instruction::MisMatch(MisMatch {
|
||||
left_value,
|
||||
right_value,
|
||||
instruction: Box::new(instruction),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn ap<'i>(argument: ApArgument<'i>, result: Variable<'i>) -> Instruction<'i> {
|
||||
Instruction::Ap(Ap { argument, result })
|
||||
}
|
||||
|
||||
pub(super) fn binary_instruction<'a, 'b>(
|
||||
name: &'a str,
|
||||
) -> impl Fn(Instruction<'b>, Instruction<'b>) -> Instruction<'b> {
|
||||
match name {
|
||||
"xor" => |l, r| xor(l, r),
|
||||
"par" => |l, r| par(l, r),
|
||||
"seq" => |l, r| seq(l, r),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
147
crates/air-lib/air-parser/src/parser/tests/fold.rs
Normal file
147
crates/air-lib/air-parser/src/parser/tests/fold.rs
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
use crate::parser::ParserError;
|
||||
|
||||
use air_lambda_ast::ValueAccessor;
|
||||
use fstrings::f;
|
||||
use lalrpop_util::ParseError;
|
||||
|
||||
#[test]
|
||||
fn parse_undefined_iterable() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(call "" ("" "") [] iterable)
|
||||
(fold iterable i
|
||||
(seq
|
||||
(call "" ("" "") ["hello" ""] $void)
|
||||
(next j)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let lexer = crate::AIRLexer::new(source_code);
|
||||
|
||||
let parser = crate::AIRParser::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parser shouldn't fail");
|
||||
|
||||
let errors = validator.finalize();
|
||||
|
||||
assert_eq!(errors.len(), 1);
|
||||
|
||||
let error = &errors[0].error;
|
||||
let parser_error = match error {
|
||||
ParseError::User { error } => error,
|
||||
_ => panic!("unexpected error type"),
|
||||
};
|
||||
|
||||
assert!(matches!(parser_error, ParserError::UndefinedIterable(..)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_fold() {
|
||||
let source_code = r#"
|
||||
(fold iterable i
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = fold_scalar(ScalarWithLambda::new("iterable", None), "i", null());
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_json_path() {
|
||||
let source_code = r#"
|
||||
; comment
|
||||
(fold members.$.[123321] m (null)) ;;; comment
|
||||
;;; comment
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = fold_scalar(
|
||||
ScalarWithLambda::from_raw_lambda(
|
||||
"members",
|
||||
vec![ValueAccessor::ArrayAccess { idx: 123321 }],
|
||||
),
|
||||
"m",
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_on_stream() {
|
||||
let source_code = r#"
|
||||
(fold $stream iterator (null))
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = fold_stream("$stream", "iterator", null());
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comments() {
|
||||
let source_code = r#"
|
||||
; comment
|
||||
(fold members.$.field[1] m (null)) ;;; comment ;;?()()
|
||||
;;; comme;?!.$. nt[][][][()()()null;$::!
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = fold_scalar(
|
||||
ScalarWithLambda::from_raw_lambda(
|
||||
"members",
|
||||
vec![
|
||||
ValueAccessor::FieldAccess {
|
||||
field_name: "field",
|
||||
},
|
||||
ValueAccessor::ArrayAccess { idx: 1 },
|
||||
],
|
||||
),
|
||||
"m",
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
fn source_fold_with(name: &str) -> String {
|
||||
f!(r#"(fold iterable i
|
||||
({name} (null) (null))
|
||||
)"#)
|
||||
}
|
||||
#[test]
|
||||
fn parse_fold_with_xor_par_seq() {
|
||||
for name in &["xor", "par", "seq"] {
|
||||
let source_code = source_fold_with(name);
|
||||
let instruction = parse(&source_code);
|
||||
let instr = binary_instruction(*name);
|
||||
let expected = fold_scalar(
|
||||
ScalarWithLambda::new("iterable", None),
|
||||
"i",
|
||||
instr(null(), null()),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
}
|
101
crates/air-lib/air-parser/src/parser/tests/match_.rs
Normal file
101
crates/air-lib/air-parser/src/parser/tests/match_.rs
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
|
||||
#[test]
|
||||
fn parse_match() {
|
||||
let source_code = r#"
|
||||
(match v1 v2
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = match_(
|
||||
Value::Variable(VariableWithLambda::scalar("v1")),
|
||||
Value::Variable(VariableWithLambda::scalar("v2")),
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_match_with_init_peer_id() {
|
||||
let source_code = r#"
|
||||
(match v1 %init_peer_id%
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = match_(
|
||||
Value::Variable(VariableWithLambda::scalar("v1")),
|
||||
Value::InitPeerId,
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mismatch() {
|
||||
let source_code = r#"
|
||||
(mismatch v1 v2
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(&source_code);
|
||||
let expected = mismatch(
|
||||
Value::Variable(VariableWithLambda::scalar("v1")),
|
||||
Value::Variable(VariableWithLambda::scalar("v2")),
|
||||
null(),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_with_bool() {
|
||||
let source_code = r#"
|
||||
(match isOnline true
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
|
||||
let left_value = Value::Variable(VariableWithLambda::scalar("isOnline"));
|
||||
let right_value = Value::Boolean(true);
|
||||
let null = null();
|
||||
let expected = match_(left_value, right_value, null);
|
||||
|
||||
let instruction = parse(source_code);
|
||||
assert_eq!(expected, instruction);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mismatch_with_bool() {
|
||||
let source_code = r#"
|
||||
(mismatch true isOnline
|
||||
(null)
|
||||
)
|
||||
"#;
|
||||
|
||||
let left_value = Value::Boolean(true);
|
||||
let right_value = Value::Variable(VariableWithLambda::scalar("isOnline"));
|
||||
let null = null();
|
||||
let expected = mismatch(left_value, right_value, null);
|
||||
|
||||
let instruction = parse(source_code);
|
||||
assert_eq!(expected, instruction);
|
||||
}
|
43
crates/air-lib/air-parser/src/parser/tests/mod.rs
Normal file
43
crates/air-lib/air-parser/src/parser/tests/mod.rs
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
mod ap;
|
||||
mod call;
|
||||
mod dsl;
|
||||
mod fold;
|
||||
mod match_;
|
||||
mod null;
|
||||
mod par;
|
||||
mod seq;
|
||||
|
||||
use crate::ast::Instruction;
|
||||
use crate::parser::AIRParser;
|
||||
|
||||
thread_local!(static TEST_PARSER: AIRParser = AIRParser::new());
|
||||
|
||||
fn parse(source_code: &str) -> Instruction {
|
||||
*TEST_PARSER.with(|parser| {
|
||||
let mut errors = Vec::new();
|
||||
let lexer = crate::parser::AIRLexer::new(source_code);
|
||||
let mut validator = crate::parser::VariableValidator::new();
|
||||
let res = parser
|
||||
.parse(source_code, &mut errors, &mut validator, lexer)
|
||||
.expect("parsing should be successful");
|
||||
|
||||
println!("{:?}", errors);
|
||||
res
|
||||
})
|
||||
}
|
33
crates/air-lib/air-parser/src/parser/tests/null.rs
Normal file
33
crates/air-lib/air-parser/src/parser/tests/null.rs
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
|
||||
#[test]
|
||||
fn parse_null() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(null)
|
||||
|
||||
( null )
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = Instruction::Seq(Seq(Box::new(null()), Box::new(null())));
|
||||
assert_eq!(instruction, expected)
|
||||
}
|
90
crates/air-lib/air-parser/src/parser/tests/par.rs
Normal file
90
crates/air-lib/air-parser/src/parser/tests/par.rs
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn parse_seq() {
|
||||
let source_code = r#"
|
||||
(par
|
||||
(call "" ("" "") [])
|
||||
(call "" ("" "") [])
|
||||
)
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = par(
|
||||
call(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_par_par() {
|
||||
let source_code = r#"
|
||||
(par
|
||||
(par
|
||||
(call "" ("" "") [])
|
||||
(call ("" "") ("" "") [])
|
||||
)
|
||||
(call "" ("" "") [])
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = par(
|
||||
par(
|
||||
call(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
120
crates/air-lib/air-parser/src/parser/tests/seq.rs
Normal file
120
crates/air-lib/air-parser/src/parser/tests/seq.rs
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2021 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::dsl::*;
|
||||
use super::parse;
|
||||
use crate::ast::*;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn parse_seq() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(call peer_id (service_id function_name) [[] []] output)
|
||||
(call "peer_id" ("service_id" "function_name") ["hello" [] name])
|
||||
)
|
||||
"#;
|
||||
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name")),
|
||||
Rc::new(vec![Value::EmptyArray, Value::EmptyArray]),
|
||||
CallOutputValue::Variable(Variable::scalar("output")),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("peer_id"),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal("hello"),
|
||||
Value::EmptyArray,
|
||||
Value::Variable(VariableWithLambda::scalar("name")),
|
||||
]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_seq_seq() {
|
||||
let source_code = r#"
|
||||
(seq
|
||||
(seq
|
||||
(call peer_id (service_id function_name) [])
|
||||
(call (peer_id service_A) ("service_B" function_name) [])
|
||||
)
|
||||
(call "peer_id" ("service_id" "function_name") ["hello" name] $output)
|
||||
)
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
seq(
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("service_id")),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name")),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("peer_id")),
|
||||
CallInstrValue::Literal("service_B"),
|
||||
CallInstrValue::Variable(VariableWithLambda::scalar("function_name")),
|
||||
Rc::new(vec![]),
|
||||
CallOutputValue::None,
|
||||
),
|
||||
),
|
||||
call(
|
||||
CallInstrValue::Literal("peer_id"),
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("function_name"),
|
||||
Rc::new(vec![
|
||||
Value::Literal("hello"),
|
||||
Value::Variable(VariableWithLambda::scalar("name")),
|
||||
]),
|
||||
CallOutputValue::Variable(Variable::stream("$output")),
|
||||
),
|
||||
);
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
fn source_seq_with(name: &'static str) -> String {
|
||||
f!(r#"
|
||||
(seq
|
||||
({name}
|
||||
(seq (null) (null))
|
||||
(null)
|
||||
)
|
||||
({name} (null) (seq (null) (null)) )
|
||||
)
|
||||
"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_seq_par_xor_seq() {
|
||||
for name in &["xor", "par", "seq"] {
|
||||
let source_code = source_seq_with(name);
|
||||
let instruction = parse(&source_code);
|
||||
let instr = binary_instruction(*name);
|
||||
let expected = seq(instr(seqnn(), null()), instr(null(), seqnn()));
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use super::ast::*;
|
||||
use crate::ast::*;
|
||||
|
||||
use crate::parser::lexer::Token;
|
||||
use crate::parser::ParserError;
|
||||
@ -55,9 +55,11 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
|
||||
pub(super) fn met_call(&mut self, call: &Call<'i>, span: Span) {
|
||||
self.met_peer_part(&call.peer_part, span);
|
||||
self.met_function_part(&call.function_part, span);
|
||||
self.met_args(&call.args, span);
|
||||
self.met_call_instr_value(&call.triplet.peer_pk, span);
|
||||
self.met_call_instr_value(&call.triplet.service_id, span);
|
||||
self.met_call_instr_value(&call.triplet.function_name, span);
|
||||
|
||||
self.met_args(call.args.deref(), span);
|
||||
|
||||
match &call.output {
|
||||
CallOutputValue::Variable(variable) => self.met_variable_definition(variable, span),
|
||||
@ -76,32 +78,34 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
|
||||
pub(super) fn met_fold_scalar(&mut self, fold: &FoldScalar<'i>, span: Span) {
|
||||
self.met_iterable_value(&fold.iterable, span);
|
||||
self.met_iterator_definition(fold.iterator, span);
|
||||
self.met_variable_name(fold.iterable.name, span);
|
||||
self.met_iterator_definition(&fold.iterator, span);
|
||||
}
|
||||
|
||||
pub(super) fn meet_fold_stream(&mut self, fold: &FoldStream<'i>, span: Span) {
|
||||
self.met_variable(&AstVariable::Stream(fold.stream_name), span);
|
||||
self.met_iterator_definition(fold.iterator, span);
|
||||
self.met_variable_name(fold.iterable.name, span);
|
||||
self.met_iterator_definition(&fold.iterator, span);
|
||||
}
|
||||
|
||||
pub(super) fn met_next(&mut self, next: &Next<'i>, span: Span) {
|
||||
let iterable_name = next.0;
|
||||
// due to the right to left convolution in lalrpop, next will be met earlier than
|
||||
// a corresponding fold with the definition of this iterable, so they're just put
|
||||
// without a check for being already met
|
||||
let iterable_name = next.iterator.name;
|
||||
// due to the right to left convolution in lalrpop, a next instruction will be met earlier
|
||||
// than a corresponding fold instruction with the definition of this iterable, so they're
|
||||
// just put without a check for being already met
|
||||
self.unresolved_iterables.insert(iterable_name, span);
|
||||
}
|
||||
|
||||
pub(super) fn met_ap(&mut self, ap: &Ap<'i>, span: Span) {
|
||||
match &ap.argument {
|
||||
ApArgument::ScalarVariable(name) => self.met_variable(&AstVariable::Scalar(name), span),
|
||||
ApArgument::VariableWithLambda(vl) => self.met_variable(&vl.variable, span),
|
||||
ApArgument::Number(_)
|
||||
| ApArgument::InitPeerId
|
||||
| ApArgument::Boolean(_)
|
||||
| ApArgument::Literal(_)
|
||||
| ApArgument::EmptyArray
|
||||
| ApArgument::LastError(_) => {}
|
||||
ApArgument::Scalar(scalar) => {
|
||||
self.met_variable_wl(&VariableWithLambda::Scalar(scalar.clone()), span)
|
||||
}
|
||||
}
|
||||
self.met_variable_definition(&ap.result, span);
|
||||
}
|
||||
@ -123,59 +127,37 @@ impl<'i> VariableValidator<'i> {
|
||||
errors
|
||||
}
|
||||
|
||||
fn met_peer_part(&mut self, peer_part: &PeerPart<'i>, span: Span) {
|
||||
match peer_part {
|
||||
PeerPart::PeerPk(peer_pk) => self.met_instr_value(peer_pk, span),
|
||||
PeerPart::PeerPkWithServiceId(peer_pk, service_id) => {
|
||||
self.met_instr_value(peer_pk, span);
|
||||
self.met_instr_value(service_id, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn met_function_part(&mut self, function_part: &FunctionPart<'i>, span: Span) {
|
||||
match function_part {
|
||||
FunctionPart::FuncName(func_name) => self.met_instr_value(func_name, span),
|
||||
FunctionPart::ServiceIdWithFuncName(service_id, func_name) => {
|
||||
self.met_instr_value(service_id, span);
|
||||
self.met_instr_value(func_name, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn met_args(&mut self, args: &[CallInstrArgValue<'i>], span: Span) {
|
||||
fn met_args(&mut self, args: &[Value<'i>], span: Span) {
|
||||
for arg in args {
|
||||
self.met_instr_arg_value(arg, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn met_instr_value(&mut self, instr_value: &CallInstrValue<'i>, span: Span) {
|
||||
match instr_value {
|
||||
CallInstrValue::VariableWithLambda(vl) => self.met_variable(&vl.variable, span),
|
||||
CallInstrValue::Variable(variable) => self.met_variable(variable, span),
|
||||
_ => {}
|
||||
fn met_call_instr_value(&mut self, instr_value: &CallInstrValue<'i>, span: Span) {
|
||||
if let CallInstrValue::Variable(variable) = instr_value {
|
||||
self.met_variable_wl(variable, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn met_instr_arg_value(&mut self, instr_arg_value: &CallInstrArgValue<'i>, span: Span) {
|
||||
match instr_arg_value {
|
||||
CallInstrArgValue::VariableWithLambda(vl) => self.met_variable(&vl.variable, span),
|
||||
CallInstrArgValue::Variable(variable) => {
|
||||
// skipping streams here allows treating non-defined streams as empty arrays
|
||||
if let AstVariable::Scalar(_) = variable {
|
||||
self.met_variable(variable, span)
|
||||
fn met_instr_arg_value(&mut self, instr_arg_value: &Value<'i>, span: Span) {
|
||||
if let Value::Variable(variable) = instr_arg_value {
|
||||
// skipping streams without lambdas here allows treating non-defined streams as empty arrays
|
||||
if let VariableWithLambda::Stream(stream) = variable {
|
||||
if stream.lambda.is_none() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
self.met_variable_wl(variable, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn met_variable(&mut self, variable: &AstVariable<'i>, span: Span) {
|
||||
let name = match variable {
|
||||
AstVariable::Scalar(name) => name,
|
||||
AstVariable::Stream(name) => name,
|
||||
};
|
||||
fn met_variable_wl(&mut self, variable: &VariableWithLambda<'i>, span: Span) {
|
||||
let name = variable_wl_name(variable);
|
||||
self.met_variable_name(name, span);
|
||||
}
|
||||
|
||||
fn met_variable_name(&mut self, name: &'i str, span: Span) {
|
||||
if !self.contains_variable(name, span) {
|
||||
self.unresolved_variables.insert(name, span);
|
||||
}
|
||||
@ -196,15 +178,15 @@ impl<'i> VariableValidator<'i> {
|
||||
found_spans.iter().any(|s| s < &key_span)
|
||||
}
|
||||
|
||||
fn met_variable_definition(&mut self, variable: &AstVariable<'i>, span: Span) {
|
||||
fn met_variable_definition(&mut self, variable: &Variable<'i>, span: Span) {
|
||||
let name = variable_name(variable);
|
||||
self.met_variable_name_definition(name, span);
|
||||
}
|
||||
|
||||
fn met_variable_name_definition(&mut self, name: &'i str, span: Span) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let variable_name = match variable {
|
||||
AstVariable::Scalar(name) => name,
|
||||
AstVariable::Stream(name) => name,
|
||||
};
|
||||
|
||||
match self.met_variables.entry(variable_name) {
|
||||
match self.met_variables.entry(name) {
|
||||
Entry::Occupied(occupied) => {
|
||||
if occupied.get() > &span {
|
||||
*occupied.into_mut() = span;
|
||||
@ -216,15 +198,15 @@ impl<'i> VariableValidator<'i> {
|
||||
}
|
||||
}
|
||||
|
||||
fn met_matchable(&mut self, matchable: &MatchableValue<'i>, span: Span) {
|
||||
fn met_matchable(&mut self, matchable: &Value<'i>, span: Span) {
|
||||
match matchable {
|
||||
MatchableValue::InitPeerId
|
||||
| MatchableValue::Number(_)
|
||||
| MatchableValue::Boolean(_)
|
||||
| MatchableValue::Literal(_)
|
||||
| MatchableValue::EmptyArray => {}
|
||||
MatchableValue::Variable(variable) => self.met_variable(variable, span),
|
||||
MatchableValue::VariableWithLambda(vl) => self.met_variable(&vl.variable, span),
|
||||
Value::InitPeerId
|
||||
| Value::Number(_)
|
||||
| Value::Boolean(_)
|
||||
| Value::Literal(_)
|
||||
| Value::LastError(_)
|
||||
| Value::EmptyArray => {}
|
||||
Value::Variable(variable) => self.met_variable_wl(variable, span),
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,23 +222,14 @@ impl<'i> VariableValidator<'i> {
|
||||
.any(|s| s.left < key_span.left && s.right > key_span.right)
|
||||
}
|
||||
|
||||
fn met_iterable_value(&mut self, iterable_value: &IterableScalarValue<'i>, span: Span) {
|
||||
match iterable_value {
|
||||
IterableScalarValue::VariableWithLambda { scalar_name, .. } => {
|
||||
self.met_variable(&AstVariable::Scalar(scalar_name), span)
|
||||
}
|
||||
IterableScalarValue::ScalarVariable(name) => {
|
||||
self.met_variable(&AstVariable::Scalar(name), span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn met_iterator_definition(&mut self, iterator: &'i str, span: Span) {
|
||||
self.met_iterators.insert(iterator, span);
|
||||
fn met_iterator_definition(&mut self, iterator: &Scalar<'i>, span: Span) {
|
||||
self.met_iterators.insert(iterator.name, span);
|
||||
}
|
||||
}
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Deref;
|
||||
|
||||
impl PartialOrd for Span {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
let self_min = std::cmp::min(self.left, self.right);
|
||||
@ -294,3 +267,17 @@ fn add_to_errors<'err, 'i>(
|
||||
|
||||
errors.push(error);
|
||||
}
|
||||
|
||||
fn variable_name<'v>(variable: &Variable<'v>) -> &'v str {
|
||||
match variable {
|
||||
Variable::Scalar(scalar) => scalar.name,
|
||||
Variable::Stream(stream) => stream.name,
|
||||
}
|
||||
}
|
||||
|
||||
fn variable_wl_name<'v>(variable: &VariableWithLambda<'v>) -> &'v str {
|
||||
match variable {
|
||||
VariableWithLambda::Scalar(scalar) => scalar.name,
|
||||
VariableWithLambda::Stream(stream) => stream.name,
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "air-lambda-ast"
|
||||
description = "Parser of AIR value algebra values"
|
||||
description = "Definition of the AIR lambda AST"
|
||||
version = "0.1.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
|
@ -99,10 +99,10 @@ pub(crate) enum ValueType<'i> {
|
||||
|
||||
impl<'i> ValueType<'i> {
|
||||
pub(self) fn from_output_value(output_value: &'i CallOutputValue<'_>) -> Self {
|
||||
use air_parser::ast::AstVariable;
|
||||
use air_parser::ast::Variable;
|
||||
|
||||
match output_value {
|
||||
CallOutputValue::Variable(AstVariable::Stream(stream_name)) => ValueType::Stream(stream_name),
|
||||
CallOutputValue::Variable(Variable::Stream(stream)) => ValueType::Stream(stream.name),
|
||||
_ => ValueType::Scalar,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user