#![cfg(all(feature = "serde", feature = "serde_derive"))] extern crate ketos; extern crate serde; #[macro_use] extern crate serde_derive; use std::collections::BTreeMap; use std::path::PathBuf; use ketos::{ BuiltinModuleLoader, FileModuleLoader, ModuleLoader, Error, Interpreter, decode_value, encode_value, }; macro_rules! map { ( $( $k:expr => $v:expr ),* ) => { { let mut _m = BTreeMap::new(); $( _m.insert($k, $v); )* _m } } } macro_rules! test { ( $a:expr , $b:expr ) => { { let interp = interp(&format!(r#" (define (give v) (assert-eq v '{0})) (define (take) '{0}) "#, $b)).unwrap(); let v = $a; interp.call("give", vec![ encode_value(interp.scope(), &v).unwrap(), ]).unwrap(); let v2 = decode_value(interp.scope(), &interp.call("take", vec![]).unwrap()).unwrap(); assert_eq!(v, v2); } } } fn interp(code: &str) -> Result { let mut loader = FileModuleLoader::with_search_paths(vec![PathBuf::from("lib")]); loader.set_read_bytecode(false); loader.set_write_bytecode(false); let interp = Interpreter::with_loader( Box::new(BuiltinModuleLoader.chain(loader))); interp.run_code("(use test (assert-eq))", None)?; interp.run_code(code, None)?; Ok(interp) } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct StructA { a: i32, b: char, c: String, } const STRUCT_0: &'static str = r#"(StructA (:a 123 :b #'x' :c "foo"))"#; fn struct_0() -> StructA { StructA{ a: 123, b: 'x', c: "foo".to_owned(), } } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct StructB { a: Vec, b: BTreeMap, } const STRUCT_1: &'static str = r#"(StructB (:a () :b ()))"#; fn struct_1() -> StructB { StructB{ a: vec![], b: map!(), } } const STRUCT_2: &'static str = r#"(StructB (:a (1 2 3) :b (("a" "b") ("c" "d"))))"#; fn struct_2() -> StructB { StructB{ a: vec![1, 2, 3], b: map!("a".to_owned() => "b".to_owned(), "c".to_owned() => "d".to_owned()), } } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct StructC(u8, (u16, u32), [i32; 2]); const STRUCT_3: &'static str = "(StructC (1 (2 3) (4 5)))"; fn struct_3() -> StructC { StructC(1, (2, 3), [4, 5]) } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct StructD; const STRUCT_4: &'static str = "(StructD ())"; fn struct_4() -> StructD { StructD } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct StructE { a: StructA, b: StructB, c: StructC, d: StructD, } const STRUCT_5: &'static str = r#"(StructE (:a (StructA (:a -1 :b #'.' :c "lol")) :b (StructB (:a (0) :b (("a" "b")))) :c (StructC (2 (1 0) (-1 -2))) :d (StructD ())))"#; fn struct_5() -> StructE { StructE{ a: StructA{a: -1, b: '.', c: "lol".to_owned()}, b: StructB{a: vec![0], b: map!("a".to_owned() => "b".to_owned())}, c: StructC(2, (1, 0), [-1, -2]), d: StructD, } } #[test] fn test_struct() { test!(struct_0(), STRUCT_0); test!(struct_1(), STRUCT_1); test!(struct_2(), STRUCT_2); test!(struct_3(), STRUCT_3); test!(struct_4(), STRUCT_4); test!(struct_5(), STRUCT_5); } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] enum Enum { Alpha, Beta(i32), Gamma(char, ()), Delta{a: String, b: u32}, } const ENUM_0: &'static str = "(Enum Alpha ())"; fn enum_0() -> Enum { Enum::Alpha } const ENUM_1: &'static str = "(Enum Beta (1))"; fn enum_1() -> Enum { Enum::Beta(1) } const ENUM_2: &'static str = "(Enum Gamma (#'a' ()))"; fn enum_2() -> Enum { Enum::Gamma('a', ()) } const ENUM_3: &'static str = r#"(Enum Delta (:a "foo" :b 123))"#; fn enum_3() -> Enum { Enum::Delta{a: "foo".to_owned(), b: 123} } #[test] fn test_enum() { test!(enum_0(), ENUM_0); test!(enum_1(), ENUM_1); test!(enum_2(), ENUM_2); test!(enum_3(), ENUM_3); } macro_rules! de { ( $ty:ty => $e:expr ) => { { let interp = interp(&format!(" (define (make) '{}) ", $e)).unwrap(); decode_value::<$ty>(interp.scope(), &interp.call("make", vec![]).unwrap()) } } } #[test] fn test_primitive() { assert_eq!(de!((u32, String) => r#"(1 "foo")"#).unwrap(), (1, "foo".to_owned())); assert_eq!(de!(Vec => "(1 2 3)").unwrap(), [1, 2, 3]); assert_eq!(de!(BTreeMap => "((1 2) (3 4))").unwrap(), map!(1 => 2, 3 => 4)); } #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct OptStruct { a: Option, } #[test] fn test_option() { assert_eq!(de!(Option => "()").unwrap(), None); assert_eq!(de!(Option => "1").unwrap(), Some(1)); assert_eq!(de!(OptStruct => "(OptStruct (:a 1))").unwrap(), OptStruct{a: Some(1)}); assert_eq!(de!(OptStruct => "(OptStruct (:a ()))").unwrap(), OptStruct{a: None}); } #[test] fn test_error() { assert!(de!(StructA => "0").is_err()); assert!(de!(StructA => "()").is_err()); assert!(de!(StructA => "(StructA)").is_err()); assert!(de!(StructA => "(StructA ())").is_err()); assert!(de!(StructA => "(StructA () ())").is_err()); assert!(de!(StructA => "(StructB (:a ()))").is_err()); assert!(de!(StructA => "(StructB (:a () :b () :c ()))").is_err()); assert!(de!(Enum => "0").is_err()); assert!(de!(Enum => "()").is_err()); assert!(de!(Enum => "(Enum)").is_err()); assert!(de!(Enum => "(Enum Alpha)").is_err()); assert!(de!(Enum => "(Enum Alpha (0))").is_err()); assert!(de!(Enum => "(Enum Alpha () ())").is_err()); assert!(de!(Enum => "(Enum Gamma (#'x'))").is_err()); assert!(de!(Enum => "(Enum Lol ())").is_err()); assert!(de!(BTreeMap => "((0 1) ())").is_err()); assert!(de!(BTreeMap => "((0 1) (1))").is_err()); assert!(de!(BTreeMap => "((0 1) (1 2 3))").is_err()); assert!(de!(Vec => "(1 2 ())").is_err()); }