Files
aquavm/crates/air-lib/air-parser/src/parser/tests/call.rs
2022-09-08 16:58:04 +03:00

686 lines
21 KiB
Rust

/*
* 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::{LambdaAST, ValueAccessor};
use fstrings::f;
use fstrings::format_args_f;
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_value_path(
"peer_id",
vec![ValueAccessor::FieldAccessByName { field_name: "a" }],
15,
)),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("function_name"),
Rc::new(vec![
Value::Literal("hello"),
Value::Variable(VariableWithLambda::scalar("name", 68)),
]),
CallOutputValue::Stream(Stream::new("$void", 74)),
);
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", 15)),
CallInstrValue::Variable(VariableWithLambda::scalar("service_id", 24)),
CallInstrValue::Literal("function_name"),
Rc::new(vec![
Value::Literal(""),
Value::EmptyArray,
Value::Variable(VariableWithLambda::scalar("arg", 59)),
]),
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", 15)),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("function_name"),
Rc::new(vec![
Value::Variable(VariableWithLambda::scalar("k", 55)),
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_lambda_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_value_path(
"m",
vec![ValueAccessor::ArrayAccess { idx: 1 }],
32,
)),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("function_name"),
Rc::new(vec![]),
CallOutputValue::Scalar(Scalar::new("void", 75)),
),
call(
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
"m",
vec![
ValueAccessor::FieldAccessByName { field_name: "abc" },
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByName { field_name: "cde" },
ValueAccessor::ArrayAccess { idx: 1 },
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByName { field_name: "cde" },
ValueAccessor::ArrayAccess { idx: 1 },
],
99,
)),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("function_name"),
Rc::new(vec![]),
CallOutputValue::Scalar(Scalar::new("void", 162)),
),
);
assert_eq!(instruction, expected);
}
#[test]
fn parse_lambda_with_scalars_complex() {
let source_code = r#"
(seq
(call m.$.[1].[scalar_1].[scalar_2]! ("service_id" "function_name") [] void)
(call m.$.abc[0].[scalar_2].cde[1][0][scalar_3].cde[1]! ("service_id" "function_name") [] void)
)
"#;
let instruction = parse(source_code);
let expected = seq(
call(
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
"m",
vec![
ValueAccessor::ArrayAccess { idx: 1 },
ValueAccessor::FieldAccessByScalar {
scalar_name: "scalar_1",
},
ValueAccessor::FieldAccessByScalar {
scalar_name: "scalar_2",
},
],
32,
)),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("function_name"),
Rc::new(vec![]),
CallOutputValue::Scalar(Scalar::new("void", 97)),
),
call(
CallInstrValue::Variable(VariableWithLambda::from_raw_value_path(
"m",
vec![
ValueAccessor::FieldAccessByName { field_name: "abc" },
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByScalar {
scalar_name: "scalar_2",
},
ValueAccessor::FieldAccessByName { field_name: "cde" },
ValueAccessor::ArrayAccess { idx: 1 },
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByScalar {
scalar_name: "scalar_3",
},
ValueAccessor::FieldAccessByName { field_name: "cde" },
ValueAccessor::ArrayAccess { idx: 1 },
],
121,
)),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("function_name"),
Rc::new(vec![]),
CallOutputValue::Scalar(Scalar::new("void", 205)),
),
);
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_value_path(
"u",
vec![ValueAccessor::FieldAccessByName {
field_name: "peer_id",
}],
15,
)),
CallInstrValue::Literal("return"),
CallInstrValue::Literal(""),
Rc::new(vec![
Value::Variable(VariableWithLambda::from_raw_value_path(
"u",
vec![
ValueAccessor::ArrayAccess { idx: 1 },
ValueAccessor::FieldAccessByName { field_name: "cde" },
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByName { field_name: "abc" },
],
43,
)),
Value::Variable(VariableWithLambda::from_raw_value_path(
"u",
vec![ValueAccessor::FieldAccessByName { field_name: "name" }],
64,
)),
]),
CallOutputValue::Stream(Stream::new("$void", 74)),
);
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_timestamp() {
let source_code = r#"
(call "peer_id" ("service_id" "fn_name") [%timestamp%])
"#;
let instruction = parse(source_code);
let expected = call(
CallInstrValue::Literal("peer_id"),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("fn_name"),
Rc::new(vec![Value::Timestamp]),
CallOutputValue::None,
);
assert_eq!(instruction, expected);
}
#[test]
fn parse_ttl() {
let source_code = r#"
(call "peer_id" ("service_id" "fn_name") [%ttl%])
"#;
let instruction = parse(source_code);
let expected = call(
CallInstrValue::Literal("peer_id"),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("fn_name"),
Rc::new(vec![Value::TTL]),
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(None)]),
CallOutputValue::None,
),
null(),
);
assert_eq!(instruction, expected);
}
#[test]
fn canon_stream_in_args() {
let service_id = "service_id";
let function_name = "function_name";
let canon_stream = "#canon_stream";
let source_code = f!(r#"
(call %init_peer_id% ("{service_id}" "{function_name}") [{canon_stream}])
"#);
let instruction = parse(&source_code);
let expected = call(
CallInstrValue::InitPeerId,
CallInstrValue::Literal(service_id),
CallInstrValue::Literal(function_name),
Rc::new(vec![Value::Variable(VariableWithLambda::canon_stream(
canon_stream,
66,
))]),
CallOutputValue::None,
);
assert_eq!(instruction, expected);
}
#[test]
fn canon_stream_in_triplet() {
let service_id = "service_id";
let function_name = "function_name";
let canon_stream = "#canon_stream";
let source_code = f!(r#"
(call {canon_stream} ("{service_id}" "{function_name}") [])
"#);
let instruction = parse(&source_code);
let expected = call(
CallInstrValue::Variable(VariableWithLambda::canon_stream(canon_stream, 19)),
CallInstrValue::Literal(service_id),
CallInstrValue::Literal(function_name),
Rc::new(vec![]),
CallOutputValue::None,
);
assert_eq!(instruction, expected);
}
#[test]
fn canon_stream_with_lambda_in_triplet() {
let service_id = "service_id";
let function_name = "function_name";
let canon_stream = "#canon_stream";
let canon_stream_lambda = ".$.[0].path!";
let source_code = f!(r#"
(call {canon_stream}{canon_stream_lambda} ("{service_id}" "{function_name}") [])
"#);
let instruction = parse(&source_code);
let expected = call(
CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(
canon_stream,
LambdaAST::try_from_accessors(vec![
ValueAccessor::ArrayAccess { idx: 0 },
ValueAccessor::FieldAccessByName { field_name: "path" },
])
.unwrap(),
19,
)),
CallInstrValue::Literal(service_id),
CallInstrValue::Literal(function_name),
Rc::new(vec![]),
CallOutputValue::None,
);
assert_eq!(instruction, expected);
}
#[test]
fn seq_par_call() {
let peer_id = "some_peer_id";
let source_code = f!(r#"
(seq
(par
(call "{peer_id}" ("local_service_id" "local_fn_name") [] result_1)
(call "{peer_id}" ("service_id" "fn_name") [] g)
)
(call "{peer_id}" ("local_service_id" "local_fn_name") [] result_2)
)"#);
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::Scalar(Scalar::new("result_1", 108)),
),
call(
CallInstrValue::Literal(peer_id),
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("fn_name"),
Rc::new(vec![]),
CallOutputValue::Scalar(Scalar::new("g", 183)),
),
),
call(
CallInstrValue::Literal(peer_id),
CallInstrValue::Literal("local_service_id"),
CallInstrValue::Literal("local_fn_name"),
Rc::new(vec![]),
CallOutputValue::Scalar(Scalar::new("result_2", 273)),
),
);
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::Scalar(Scalar::new("module-bytes", 119)),
),
call(
CallInstrValue::Literal("set_variables"),
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
Rc::new(vec![Value::Literal("module_config")]),
CallOutputValue::Scalar(Scalar::new("module_config", 201)),
),
),
call(
CallInstrValue::Literal("set_variables"),
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
Rc::new(vec![Value::Literal("blueprint")]),
CallOutputValue::Scalar(Scalar::new("blueprint", 294)),
),
),
seq(
call(
CallInstrValue::Literal("A"),
CallInstrValue::Literal("add_module"),
CallInstrValue::Literal(""),
Rc::new(vec![
Value::Variable(VariableWithLambda::scalar("module-bytes", 381)),
Value::Variable(VariableWithLambda::scalar("module_config", 394)),
]),
CallOutputValue::Scalar(Scalar::new("module", 409)),
),
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",
490,
))]),
output: CallOutputValue::Scalar(Scalar::new("blueprint_id", 501)),
}),
seq(
call(
CallInstrValue::Literal("A"),
CallInstrValue::Literal("create"),
CallInstrValue::Literal(""),
Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
"blueprint_id",
589,
))]),
CallOutputValue::Scalar(Scalar::new("service_id", 603)),
),
call(
CallInstrValue::Literal("remote_peer_id"),
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
Rc::new(vec![Value::Variable(VariableWithLambda::scalar(
"service_id",
671,
))]),
CallOutputValue::Scalar(Scalar::new("client_result", 683)),
),
),
),
),
);
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", 15)),
CallInstrValue::Variable(VariableWithLambda::scalar("service", 21)),
CallInstrValue::Variable(VariableWithLambda::scalar("fname", 29)),
Rc::new(vec![]),
CallOutputValue::None,
);
assert_eq!(actual, expected);
}
#[test]
fn not_defined_scalar_in_lambda() {
let source_code = r#"
(seq
(call "" ("" "") [] value)
(call "" ("" "") [value.$.[not_defined_scalar]])
)
"#;
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 { .. }
));
}