726 lines
21 KiB
Rust
Raw Normal View History

2020-11-03 17:43:58 +03:00
/*
* 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.
*/
2021-01-22 18:54:05 +03:00
use crate::ast;
use ast::Instruction;
2020-11-03 17:43:58 +03:00
use fstrings::f;
2020-11-10 17:30:49 +03:00
use std::rc::Rc;
2020-11-03 17:43:58 +03:00
fn parse(source_code: &str) -> Instruction {
2020-11-05 16:50:09 +03:00
*crate::parse(source_code).expect("parsing failed")
2020-11-03 17:43:58 +03:00
}
#[test]
fn parse_seq() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrArgValue;
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-03 17:43:58 +03:00
let source_code = r#"
(seq
2020-11-09 14:44:04 +03:00
(call peerid function [] output)
(call "id" "f" ["hello" name])
2020-11-03 17:43:58 +03:00
)
"#;
let instruction = parse(source_code);
let expected = seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Variable("peerid")),
function_part: FuncName(CallInstrValue::Variable("function")),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-09 14:44:04 +03:00
output: Scalar("output"),
2020-11-03 17:43:58 +03:00
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("id")),
function_part: FuncName(CallInstrValue::Literal("f")),
args: Rc::new(vec![
CallInstrArgValue::Literal("hello"),
CallInstrArgValue::Variable("name"),
]),
2020-11-09 14:44:04 +03:00
output: None,
2020-11-03 17:43:58 +03:00
}),
);
assert_eq!(instruction, expected);
}
#[test]
fn parse_seq_seq() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrArgValue;
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-03 17:43:58 +03:00
let source_code = r#"
(seq
(seq
2020-11-09 14:44:04 +03:00
(call peerid function [])
(call (peerid serviceA) ("serviceB" function) [])
2020-11-03 17:43:58 +03:00
)
2020-11-09 14:44:04 +03:00
(call "id" "f" ["hello" name] output[])
2020-11-03 17:43:58 +03:00
)
"#;
let instruction = parse(source_code);
let expected = seq(
seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Variable("peerid")),
function_part: FuncName(CallInstrValue::Variable("function")),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-09 14:44:04 +03:00
output: None,
2020-11-03 17:43:58 +03:00
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPkWithServiceId(
CallInstrValue::Variable("peerid"),
CallInstrValue::Variable("serviceA"),
),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("serviceB"),
CallInstrValue::Variable("function"),
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-09 14:44:04 +03:00
output: None,
2020-11-03 17:43:58 +03:00
}),
),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("id")),
function_part: FuncName(CallInstrValue::Literal("f")),
args: Rc::new(vec![
CallInstrArgValue::Literal("hello"),
CallInstrArgValue::Variable("name"),
]),
2020-11-09 14:44:04 +03:00
output: Accumulator("output"),
2020-11-03 17:43:58 +03:00
}),
);
assert_eq!(instruction, expected);
}
#[test]
fn parse_json_path() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrArgValue;
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-03 17:43:58 +03:00
let source_code = r#"
2021-02-18 17:30:14 +03:00
(call id.$.a! "f" ["hello" name] void[])
2020-11-03 17:43:58 +03:00
"#;
let instruction = parse(source_code);
let expected = Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::JsonPath {
2020-11-03 17:43:58 +03:00
variable: "id",
path: "$.a",
2021-02-18 17:30:14 +03:00
should_flatten: true,
2020-11-03 17:43:58 +03:00
}),
2021-02-11 15:39:37 +03:00
function_part: FuncName(CallInstrValue::Literal("f")),
args: Rc::new(vec![
CallInstrArgValue::Literal("hello"),
CallInstrArgValue::Variable("name"),
]),
2020-11-03 17:43:58 +03:00
output: Accumulator("void"),
});
assert_eq!(instruction, expected);
}
2021-02-18 17:30:14 +03:00
#[test]
fn parse_json_path_without_flattening() {
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();
parser
.parse(source_code, &mut errors, lexer)
.expect("parser shoudn't fail");
assert_eq!(errors.len(), 1);
assert!(matches!(errors[0], lalrpop_util::ErrorRecovery { .. }));
}
2020-11-03 17:43:58 +03:00
#[test]
fn parse_json_path_complex() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-03 17:43:58 +03:00
let source_code = r#"
(seq
2021-02-18 17:30:14 +03:00
(call m.$.[1]! "f" [] void)
(call m.$.abc["c"].cde[a][0].cde["bcd"]! "f" [] void)
2020-11-03 17:43:58 +03:00
)
"#;
let instruction = parse(source_code);
let expected = seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::JsonPath {
2020-11-03 17:43:58 +03:00
variable: "m",
path: "$.[1]",
2021-02-18 17:30:14 +03:00
should_flatten: true,
2020-11-03 17:43:58 +03:00
}),
2021-02-11 15:39:37 +03:00
function_part: FuncName(CallInstrValue::Literal("f")),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-03 17:43:58 +03:00
output: Scalar("void"),
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::JsonPath {
2020-11-03 17:43:58 +03:00
variable: "m",
2020-11-05 15:13:11 +03:00
path: r#"$.abc["c"].cde[a][0].cde["bcd"]"#,
2021-02-18 17:30:14 +03:00
should_flatten: true,
2020-11-03 17:43:58 +03:00
}),
2021-02-11 15:39:37 +03:00
function_part: FuncName(CallInstrValue::Literal("f")),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-03 17:43:58 +03:00
output: Scalar("void"),
}),
);
assert_eq!(instruction, expected);
}
2020-11-05 15:13:11 +03:00
#[test]
fn json_path_square_braces() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrArgValue;
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-05 15:13:11 +03:00
let source_code = r#"
2021-02-18 17:30:14 +03:00
(call u.$["peer_id"]! ("return" "") [u.$["peer_id"].cde[0]["abc"].abc u.$["name"]] void[])
2020-11-05 15:13:11 +03:00
"#;
let instruction = parse(source_code);
let expected = Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::JsonPath {
2020-11-05 15:13:11 +03:00
variable: "u",
path: r#"$["peer_id"]"#,
2021-02-18 17:30:14 +03:00
should_flatten: true,
2020-11-05 15:13:11 +03:00
}),
2021-02-11 15:39:37 +03:00
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("return"),
CallInstrValue::Literal(""),
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![
2021-02-11 15:39:37 +03:00
CallInstrArgValue::JsonPath {
2020-11-05 15:13:11 +03:00
variable: "u",
path: r#"$["peer_id"].cde[0]["abc"].abc"#,
2021-02-18 17:30:14 +03:00
should_flatten: false,
2020-11-05 15:13:11 +03:00
},
2021-02-11 15:39:37 +03:00
CallInstrArgValue::JsonPath {
2020-11-05 15:13:11 +03:00
variable: "u",
path: r#"$["name"]"#,
2021-02-18 17:30:14 +03:00
should_flatten: false,
2020-11-05 15:13:11 +03:00
},
2020-12-22 21:05:04 +03:00
]),
2020-11-05 15:13:11 +03:00
output: Accumulator("void"),
});
assert_eq!(instruction, expected);
}
2020-11-03 17:43:58 +03:00
#[test]
fn parse_null() {
let source_code = r#"
(seq
(null)
( null )
)
"#;
let instruction = parse(source_code);
2021-01-22 18:54:05 +03:00
let expected = Instruction::Seq(ast::Seq(Box::new(null()), Box::new(null())));
2020-11-03 17:43:58 +03:00
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.as_ref());
let instr = binary_instruction(*name);
let expected = seq(instr(seqnn(), null()), instr(null(), seqnn()));
assert_eq!(instruction, expected);
}
}
#[test]
fn parse_fold() {
let source_code = r#"
(fold iterable i
(null)
)
"#;
let instruction = parse(&source_code.as_ref());
2021-01-22 18:54:05 +03:00
let expected = fold(ast::IterableValue::Variable("iterable"), "i", null());
2020-11-03 17:43:58 +03:00
assert_eq!(instruction, expected);
}
2021-02-01 18:53:00 +03:00
#[test]
fn parse_match() {
use ast::MatchableValue::Variable;
let source_code = r#"
(match v1 v2
(null)
)
"#;
let instruction = parse(&source_code.as_ref());
let expected = match_(Variable("v1"), Variable("v2"), null());
assert_eq!(instruction, expected);
}
#[test]
fn parse_mismatch() {
use ast::MatchableValue::Variable;
let source_code = r#"
(mismatch v1 v2
(null)
)
"#;
let instruction = parse(&source_code.as_ref());
let expected = mismatch(Variable("v1"), Variable("v2"), null());
assert_eq!(instruction, expected);
}
2020-11-03 17:43:58 +03:00
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.as_ref());
let instr = binary_instruction(*name);
2020-12-22 21:05:04 +03:00
let expected = fold(
2021-01-22 18:54:05 +03:00
ast::IterableValue::Variable("iterable"),
2020-12-22 21:05:04 +03:00
"i",
instr(null(), null()),
);
2020-11-03 17:43:58 +03:00
assert_eq!(instruction, expected);
}
}
2020-11-11 14:31:53 +03:00
#[test]
2020-12-28 00:12:11 +03:00
fn parse_init_peer_id() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-12-28 00:12:11 +03:00
let peer_id = String::from("some_peer_id");
let source_code = format!(
r#"
2020-11-11 14:31:53 +03:00
(seq
2020-12-28 00:12:11 +03:00
(call "{}" ("local_service_id" "local_fn_name") [])
2020-11-11 14:31:53 +03:00
(call %init_peer_id% ("service_id" "fn_name") [])
2020-12-28 00:12:11 +03:00
)"#,
peer_id
);
2020-11-11 14:31:53 +03:00
let instruction = parse(&source_code.as_ref());
let expected = seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
2020-11-11 14:31:53 +03:00
function_part: ServiceIdWithFuncName(
2021-02-11 15:39:37 +03:00
CallInstrValue::Literal("local_service_id"),
CallInstrValue::Literal("local_fn_name"),
2020-11-11 14:31:53 +03:00
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-11 14:31:53 +03:00
output: None,
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::InitPeerId),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("fn_name"),
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-11 14:31:53 +03:00
output: None,
}),
);
2020-12-28 00:12:11 +03:00
2020-11-11 14:31:53 +03:00
assert_eq!(instruction, expected);
}
2021-02-11 15:39:37 +03:00
#[test]
fn parse_last_error() {
use ast::Call;
use ast::CallInstrArgValue;
use ast::CallInstrValue;
use ast::CallOutputValue::*;
use ast::FunctionPart::*;
use ast::PeerPart::*;
let source_code = format!(
r#"
(seq
(call %init_peer_id% ("service_id" "fn_name") [%last_error%])
(null)
)"#,
);
let instruction = parse(&source_code.as_ref());
let expected = seq(
Instruction::Call(Call {
peer_part: PeerPk(CallInstrValue::InitPeerId),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("fn_name"),
),
args: Rc::new(vec![CallInstrArgValue::LastError]),
output: None,
}),
Instruction::Null(ast::Null),
);
assert_eq!(instruction, expected);
}
2020-11-03 17:43:58 +03:00
#[test]
fn seq_par_call() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-12-28 00:12:11 +03:00
let peer_id = String::from("some_peer_id");
let source_code = format!(
r#"
2020-11-03 17:43:58 +03:00
(seq
(par
2020-12-28 00:12:11 +03:00
(call "{0}" ("local_service_id" "local_fn_name") [] result_1)
(call "{0}" ("service_id" "fn_name") [] g)
2020-11-03 17:43:58 +03:00
)
2020-12-28 00:12:11 +03:00
(call "{0}" ("local_service_id" "local_fn_name") [] result_2)
)"#,
peer_id,
);
2020-11-03 17:43:58 +03:00
let instruction = parse(&source_code.as_ref());
let expected = seq(
par(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
2020-11-03 17:43:58 +03:00
function_part: ServiceIdWithFuncName(
2021-02-11 15:39:37 +03:00
CallInstrValue::Literal("local_service_id"),
CallInstrValue::Literal("local_fn_name"),
2020-11-03 17:43:58 +03:00
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-03 17:43:58 +03:00
output: Scalar("result_1"),
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("service_id"),
CallInstrValue::Literal("fn_name"),
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-03 17:43:58 +03:00
output: Scalar("g"),
}),
),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
2020-11-03 17:43:58 +03:00
function_part: ServiceIdWithFuncName(
2021-02-11 15:39:37 +03:00
CallInstrValue::Literal("local_service_id"),
CallInstrValue::Literal("local_fn_name"),
2020-11-03 17:43:58 +03:00
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-03 17:43:58 +03:00
output: Scalar("result_2"),
}),
);
2020-12-28 00:12:11 +03:00
2020-11-03 17:43:58 +03:00
assert_eq!(instruction, expected);
}
#[test]
fn seq_with_empty_and_dash() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrArgValue;
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-03 17:43:58 +03:00
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.as_ref());
let expected = seq(
seq(
seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("set_variables")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![CallInstrArgValue::Literal("module-bytes")]),
2020-11-03 17:43:58 +03:00
output: Scalar("module-bytes"),
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("set_variables")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![CallInstrArgValue::Literal("module_config")]),
2020-11-03 17:43:58 +03:00
output: Scalar("module_config"),
}),
),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("set_variables")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![CallInstrArgValue::Literal("blueprint")]),
2020-11-03 17:43:58 +03:00
output: Scalar("blueprint"),
}),
),
seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("A")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("add_module"),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![
CallInstrArgValue::Variable("module-bytes"),
CallInstrArgValue::Variable("module_config"),
]),
2020-11-03 17:43:58 +03:00
output: Scalar("module"),
}),
seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("A")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("add_blueprint"),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![CallInstrArgValue::Variable("blueprint")]),
2020-11-03 17:43:58 +03:00
output: Scalar("blueprint_id"),
}),
seq(
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("A")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal("create"),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![CallInstrArgValue::Variable("blueprint_id")]),
2020-11-03 17:43:58 +03:00
output: Scalar("service_id"),
}),
Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Literal("remote_peer_id")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Literal(""),
CallInstrValue::Literal(""),
),
args: Rc::new(vec![CallInstrArgValue::Variable("service_id")]),
2020-11-03 17:43:58 +03:00
output: Scalar("client_result"),
}),
),
),
),
);
assert_eq!(instruction, expected);
}
2020-11-09 14:44:04 +03:00
#[test]
fn no_output() {
2021-01-22 18:54:05 +03:00
use ast::Call;
2021-02-11 15:39:37 +03:00
use ast::CallInstrValue;
2021-01-27 00:37:58 +03:00
use ast::CallOutputValue::*;
2021-01-22 18:54:05 +03:00
use ast::FunctionPart::*;
use ast::PeerPart::*;
2020-11-09 14:44:04 +03:00
let source_code = r#"
(call peer (service fname) [])
"#;
let instruction = parse(&source_code.as_ref());
let expected = Instruction::Call(Call {
2021-02-11 15:39:37 +03:00
peer_part: PeerPk(CallInstrValue::Variable("peer")),
function_part: ServiceIdWithFuncName(
CallInstrValue::Variable("service"),
CallInstrValue::Variable("fname"),
),
2020-12-22 21:05:04 +03:00
args: Rc::new(vec![]),
2020-11-09 14:44:04 +03:00
output: None,
});
assert_eq!(instruction, expected);
}
2020-11-10 17:30:49 +03:00
#[test]
fn fold_json_path() {
2021-01-22 18:54:05 +03:00
use ast::Fold;
use ast::IterableValue::*;
2020-11-10 17:30:49 +03:00
let source_code = r#"
; comment
(fold members.$.["users"] m (null)) ;;; comment
;;; comment
"#;
let instruction = parse(&source_code.as_ref());
let expected = Instruction::Fold(Fold {
iterable: JsonPath {
variable: "members",
path: "$.[\"users\"]",
2021-02-18 17:30:14 +03:00
should_flatten: false,
},
iterator: "m",
instruction: Rc::new(null()),
});
assert_eq!(instruction, expected);
}
#[test]
fn comments() {
2021-01-22 18:54:05 +03:00
use ast::Fold;
use ast::IterableValue::*;
let source_code = r#"
; comment
(fold members.$.["users"] m (null)) ;;; comment ;;?()()
;;; comme;?!.$. nt[][][][()()()null;$::!
2020-11-10 17:30:49 +03:00
"#;
let instruction = parse(&source_code.as_ref());
let expected = Instruction::Fold(Fold {
iterable: JsonPath {
variable: "members",
path: "$.[\"users\"]",
2021-02-18 17:30:14 +03:00
should_flatten: false,
2020-11-10 17:30:49 +03:00
},
iterator: "m",
instruction: Rc::new(null()),
});
assert_eq!(instruction, expected);
}
2020-11-03 17:43:58 +03:00
// Test DSL
fn seq<'a>(l: Instruction<'a>, r: Instruction<'a>) -> Instruction<'a> {
2021-01-22 18:54:05 +03:00
Instruction::Seq(ast::Seq(Box::new(l), Box::new(r)))
2020-11-03 17:43:58 +03:00
}
2021-02-01 18:53:00 +03:00
2020-11-03 17:43:58 +03:00
fn par<'a>(l: Instruction<'a>, r: Instruction<'a>) -> Instruction<'a> {
2021-01-22 18:54:05 +03:00
Instruction::Par(ast::Par(Box::new(l), Box::new(r)))
2020-11-03 17:43:58 +03:00
}
2021-02-01 18:53:00 +03:00
2020-11-03 17:43:58 +03:00
fn xor<'a>(l: Instruction<'a>, r: Instruction<'a>) -> Instruction<'a> {
2021-01-22 18:54:05 +03:00
Instruction::Xor(ast::Xor(Box::new(l), Box::new(r)))
2020-11-03 17:43:58 +03:00
}
2021-02-01 18:53:00 +03:00
2020-11-03 17:43:58 +03:00
fn seqnn() -> Instruction<'static> {
seq(null(), null())
}
2021-02-01 18:53:00 +03:00
2020-11-03 17:43:58 +03:00
fn null() -> Instruction<'static> {
2021-01-22 18:54:05 +03:00
Instruction::Null(ast::Null)
2020-11-03 17:43:58 +03:00
}
2021-02-01 18:53:00 +03:00
2020-11-10 17:30:49 +03:00
fn fold<'a>(
2021-01-22 18:54:05 +03:00
iterable: ast::IterableValue<'a>,
2020-11-10 17:30:49 +03:00
iterator: &'a str,
instruction: Instruction<'a>,
) -> Instruction<'a> {
2021-01-22 18:54:05 +03:00
Instruction::Fold(ast::Fold {
2020-11-03 17:43:58 +03:00
iterable,
iterator,
instruction: std::rc::Rc::new(instruction),
})
}
2021-02-01 18:53:00 +03:00
fn match_<'a>(
left_value: ast::MatchableValue<'a>,
right_value: ast::MatchableValue<'a>,
instruction: Instruction<'a>,
) -> Instruction<'a> {
Instruction::Match(ast::Match {
left_value,
right_value,
instruction: Box::new(instruction),
})
}
fn mismatch<'a>(
left_value: ast::MatchableValue<'a>,
right_value: ast::MatchableValue<'a>,
instruction: Instruction<'a>,
) -> Instruction<'a> {
Instruction::MisMatch(ast::MisMatch {
left_value,
right_value,
instruction: Box::new(instruction),
})
}
2020-11-03 17:43:58 +03:00
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!(),
}
}