support empty array literal in fold (#230)

This commit is contained in:
Mike Voronov 2022-03-07 13:50:08 +03:00 committed by GitHub
parent c00b453a48
commit a64a4fc0a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 804 additions and 672 deletions

View File

@ -24,14 +24,21 @@ use crate::joinable;
use crate::log_instruction;
use air_parser::ast::FoldScalar;
use air_parser::ast::FoldScalarIterable;
use air_parser::ast::Instruction;
use std::rc::Rc;
impl<'i> ExecutableInstruction<'i> for FoldScalar<'i> {
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut TraceHandler) -> ExecutionResult<()> {
log_instruction!(fold, exec_ctx, trace_ctx);
let scalar_iterable = joinable!(construct_scalar_iterable_value(&self.iterable, exec_ctx), exec_ctx)?;
let scalar = match &self.iterable {
FoldScalarIterable::Scalar(scalar) => scalar,
// just do nothing on an empty array
FoldScalarIterable::EmptyArray => return Ok(()),
};
let scalar_iterable = joinable!(construct_scalar_iterable_value(scalar, exec_ctx), exec_ctx)?;
match scalar_iterable {
// just exit on empty iterable

View File

@ -163,7 +163,7 @@ fn inner_fold_with_same_iterator() {
}
#[test]
fn empty_fold() {
fn empty_iterable_fold() {
let mut vm = create_avm(echo_call_service(), "A");
let mut set_variable_vm = create_avm(set_variable_call_service(json!([])), "set_variable");
@ -188,6 +188,24 @@ fn empty_fold() {
assert_eq!(actual_trace[0], expected_state);
}
#[test]
fn empty_literal_array_fold() {
let mut vm = create_avm(echo_call_service(), "A");
let empty_fold = r#"
(fold [] i
(seq
(call "A" ("" "") [i] $acc)
(next i)
)
)"#;
let result = checked_call_vm!(vm, "", empty_fold, "", "");
let actual_trace = trace_from_result(&result);
assert!(actual_trace.is_empty());
}
#[test]
fn empty_fold_json_path() {
let mut vm = create_avm(echo_call_service(), "A");

View File

@ -92,3 +92,10 @@ pub enum Number {
Int(i64),
Float(f64),
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum FoldScalarIterable<'i> {
#[serde(borrow)]
Scalar(ScalarWithLambda<'i>),
EmptyArray,
}

View File

@ -117,6 +117,17 @@ impl fmt::Display for Number {
}
}
impl fmt::Display for FoldScalarIterable<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use FoldScalarIterable::*;
match self {
Scalar(variable) => write!(f, "{}", variable),
EmptyArray => write!(f, "[]"),
}
}
}
impl From<Number> for serde_json::Value {
fn from(number: Number) -> Self {
(&number).into()

View File

@ -99,7 +99,8 @@ pub enum Fail<'i> {
/// (fold scalar_iterable iterator instruction)
#[derive(Serialize, Debug, PartialEq)]
pub struct FoldScalar<'i> {
pub iterable: ScalarWithLambda<'i>,
#[serde(borrow)]
pub iterable: FoldScalarIterable<'i>,
#[serde(borrow)]
pub iterator: Scalar<'i>,
pub instruction: Rc<Instruction<'i>>,

View File

@ -93,7 +93,7 @@ impl<'i> MisMatch<'i> {
impl<'i> FoldScalar<'i> {
pub fn new(
iterable: ScalarWithLambda<'i>,
iterable: FoldScalarIterable<'i>,
iterator: Scalar<'i>,
instruction: Instruction<'i>,
span: Span,

View File

@ -147,9 +147,10 @@ FailBody: Fail<'input> = {
}
}
FoldScalarIterable: ScalarWithLambda<'input> = {
<scalar:Scalar> => ScalarWithLambda::new(scalar.0, None, scalar.1),
<scalar:ScalarWithLambda> => ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2),
FoldScalarIterable: FoldScalarIterable<'input> = {
<scalar:Scalar> => FoldScalarIterable::Scalar(ScalarWithLambda::new(scalar.0, None, scalar.1)),
<scalar:ScalarWithLambda> => FoldScalarIterable::Scalar(ScalarWithLambda::new(scalar.0, Some(scalar.1), scalar.2)),
"[" "]" => FoldScalarIterable::EmptyArray,
};
Function = CallInstrValue;

File diff suppressed because it is too large Load Diff

View File

@ -84,16 +84,29 @@ pub(super) fn fail_last_error() -> Instruction<'static> {
Instruction::Fail(Fail::LastError)
}
pub(super) fn fold_scalar<'i>(
iterable: ScalarWithLambda<'i>,
pub(super) fn fold_scalar_variable<'i>(
scalar: ScalarWithLambda<'i>,
iterator: Scalar<'i>,
instruction: Instruction<'i>,
span: Span,
) -> Instruction<'i> {
Instruction::FoldScalar(FoldScalar {
iterable,
iterable: FoldScalarIterable::Scalar(scalar),
iterator,
instruction: std::rc::Rc::new(instruction),
instruction: Rc::new(instruction),
span,
})
}
pub(super) fn fold_scalar_empty_array<'i>(
iterator: Scalar<'i>,
instruction: Instruction<'i>,
span: Span,
) -> Instruction<'i> {
Instruction::FoldScalar(FoldScalar {
iterable: FoldScalarIterable::EmptyArray,
iterator,
instruction: Rc::new(instruction),
span,
})
}
@ -107,7 +120,7 @@ pub(super) fn fold_stream<'i>(
Instruction::FoldStream(FoldStream {
iterable,
iterator,
instruction: std::rc::Rc::new(instruction),
instruction: Rc::new(instruction),
span,
})
}

View File

@ -150,7 +150,7 @@ fn parse_fold() {
)
"#;
let instruction = parse(&source_code);
let expected = fold_scalar(
let expected = fold_scalar_variable(
ScalarWithLambda::new("iterable", None, 15),
Scalar::new("i", 24),
null(),
@ -168,7 +168,7 @@ fn fold_json_path() {
"#;
let instruction = parse(source_code);
let expected = fold_scalar(
let expected = fold_scalar_variable(
ScalarWithLambda::from_raw_lambda(
"members",
vec![ValueAccessor::ArrayAccess { idx: 123321 }],
@ -181,6 +181,19 @@ fn fold_json_path() {
assert_eq!(instruction, expected);
}
#[test]
fn fold_empty_array_iterable() {
let source_code = r#"
(fold [] m
(null)
)
"#;
let instruction = parse(source_code);
let expected = fold_scalar_empty_array(Scalar::new("m", 18), null(), Span::new(9, 48));
assert_eq!(instruction, expected);
}
#[test]
fn fold_on_stream() {
let source_code = r#"
@ -205,7 +218,7 @@ fn comments() {
;;; comme;?!.$. nt[][][][()()()null;$::!
"#;
let instruction = parse(source_code);
let expected = fold_scalar(
let expected = fold_scalar_variable(
ScalarWithLambda::from_raw_lambda(
"members",
vec![
@ -234,7 +247,7 @@ fn parse_fold_with_xor_par_seq() {
let source_code = source_fold_with(name);
let instruction = parse(&source_code);
let instr = binary_instruction(*name);
let expected = fold_scalar(
let expected = fold_scalar_variable(
ScalarWithLambda::new("iterable", None, 6),
Scalar::new("i", 15),
instr(null(), null()),

View File

@ -83,7 +83,12 @@ impl<'i> VariableValidator<'i> {
}
pub(super) fn met_fold_scalar(&mut self, fold: &FoldScalar<'i>, span: Span) {
self.met_variable_name(fold.iterable.name, span);
use FoldScalarIterable::*;
match &fold.iterable {
Scalar(variable) => self.met_variable_name(variable.name, span),
EmptyArray => {}
};
self.met_iterator_definition(&fold.iterator, span);
}