use core::f32;
use std::{
net::IpAddr,
sync::{
Arc,
atomic::{AtomicI32, AtomicUsize, Ordering},
},
};
use crate::{
Context, FileTree, List, NoCtx, Runtime,
file_tree::FileSpec,
library,
pipeline::Package,
runtime::OptCtx,
source_file, src,
value::{Val, Verdict},
};
use inetnum::{addr::Prefix, asn::Asn};
#[track_caller]
fn compile(f: FileTree) -> Package<NoCtx> {
let runtime = Runtime::new();
compile_with_runtime(f, runtime)
}
#[track_caller]
fn compile_with_runtime<Ctx: OptCtx>(
f: FileTree,
runtime: Runtime<Ctx>,
) -> Package<Ctx> {
#[cfg(feature = "logger")]
let _ = env_logger::try_init();
let res = f.parse().and_then(|x| x.typecheck(&runtime)).map(|x| {
let x = x.lower_to_mir().lower_to_lir();
x.codegen()
});
match res {
Ok(x) => x,
Err(err) => {
println!("{err}");
panic!("Compilation error: see above");
}
}
}
#[test]
fn unit() {
let s = src!(
"
fn unit_expression() {
if true {
()
} else {
()
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> ()>("unit_expression")
.expect("No function found (or mismatched types)");
f.call();
}
#[test]
fn unit_type() {
let s = src!(
"
fn unit_type() -> () {
let x: () = ();
x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> ()>("unit_type")
.expect("No function found (or mismatched types)");
f.call();
}
#[test]
fn accept() {
let s = src!(
"
filtermap main() {
accept
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, Verdict::Accept(()));
}
#[test]
fn accept_with_semicolon() {
let s = src!(
"
filtermap main() {
accept;
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, Verdict::Accept(()));
}
#[test]
fn reject() {
let s = src!(
"
filtermap main() {
reject
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, Verdict::Reject(()));
}
#[test]
fn equal_to_10() {
let s = src!(
"
filtermap main(x: u32) {
if x == 10 {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(u32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(5);
assert_eq!(res, Verdict::Reject(()));
let res = f.call(10);
assert_eq!(res, Verdict::Accept(()));
}
#[test]
fn equal_to_10_with_function() {
let s = src!(
"
fn is_10(x: i32) -> bool {
x == 10
}
filtermap main(x: i32) {
if is_10(x) {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(5);
assert_eq!(res, Verdict::Reject(()));
let res = f.call(10);
assert_eq!(res, Verdict::Accept(()));
}
#[test]
fn equal_to_10_with_two_functions() {
let s = src!(
"
fn equals(x: u32, y: u32) -> bool {
x == y
}
fn is_10(x: u32) -> bool {
equals(x, 10)
}
filtermap main(x: u32) {
if is_10(x) {
accept
} else if equals(x, 20) {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(u32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
assert_eq!(f.call(5), Verdict::Reject(()));
assert_eq!(f.call(10), Verdict::Accept(()));
assert_eq!(f.call(15), Verdict::Reject(()));
assert_eq!(f.call(20), Verdict::Accept(()));
}
#[test]
fn negated_literal() {
let s = src!(
"
fn negate() -> i32 {
-5
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> i32>("negate")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, -5)
}
#[test]
fn negation() {
let s = src!(
"
fn negate(x: i32) -> i32 {
-x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> i32>("negate")
.expect("No function found (or mismatched types)");
let res = f.call(5);
assert_eq!(res, -5)
}
#[test]
fn inversion() {
let s = src!(
"
filtermap main(x: i32) {
if not (x == 10) {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for x in 0..20 {
let res = f.call(x);
let exp = if x != 10 {
Verdict::Accept(())
} else {
Verdict::Reject(())
};
assert_eq!(res, exp, "{x}");
}
}
#[test]
fn not_not() {
let s = src!(
"
fn main(x: i32) -> bool {
not not (x == 10)
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> bool>("main")
.expect("No function found (or mismatched types)");
for x in 0..20 {
let res = f.call(x);
assert_eq!(res, x == 10, "{x}");
}
}
#[test]
fn a_bunch_of_comparisons() {
let s = src!(
"
filtermap main(x: i32) {
if (
(x > 10 && x < 20)
|| (x >= 30 && x <= 40)
|| x == 55
){
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for x in 0..100 {
#[allow(clippy::manual_range_contains)]
let expected =
if (x > 10 && x < 20) || (x >= 30 && x <= 40) || x == 55 {
Verdict::Accept(())
} else {
Verdict::Reject(())
};
let res = f.call(x);
assert_eq!(res, expected);
}
}
#[test]
fn record() {
let s = src!(
"
record Foo { a: i32, b: i32 }
filtermap main(x: i32) {
let foo = Foo { a: x, b: 20 };
if foo.a == foo.b {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for x in 0..100 {
let expected = if x == 20 {
Verdict::Accept(())
} else {
Verdict::Reject(())
};
let res = f.call(x);
assert_eq!(res, expected);
}
}
#[test]
fn record_with_fields_flipped() {
let s = src!(
"
record Foo { a: i32, b: i32 }
filtermap main(x: i32) {
# These are flipped, to ensure that the order in which
# the fields are given doesn't matter:
let foo = Foo { b: 20, a: x };
if foo.a == foo.b {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)")
.into_func();
for x in 0..100 {
let expected = if x == 20 {
Verdict::Accept(())
} else {
Verdict::Reject(())
};
let res = f(x);
assert_eq!(res, expected);
}
}
#[test]
fn nested_record() {
let s = src!(
"
record Foo { x: Bar, y: Bar }
record Bar { a: i32, b: i32 }
filtermap main(x: i32) {
let bar = Bar { a: 20, b: x };
let foo = Foo { x: bar, y: bar };
if foo.x.a == foo.y.b {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for x in 0..100 {
let expected = if x == 20 {
Verdict::Accept(())
} else {
Verdict::Reject(())
};
let res = f.call(x);
assert_eq!(res, expected, "for {x}");
}
}
#[test]
fn misaligned_fields() {
let s = src!(
"
record Foo { a: i16, b: i32 }
filtermap main(x: i32) {
let foo = Foo { a: 10, b: x };
if foo.b == 20 {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for x in 0..100 {
let expected = if x == 20 {
Verdict::Accept(())
} else {
Verdict::Reject(())
};
let res = f.call(x);
assert_eq!(res, expected, "for {x}");
}
}
#[test]
fn arithmetic() {
let s = src!(
"
filtermap main(x: i32) {
if x + 10 * 20 < 250 {
accept
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(5);
assert_eq!(res, Verdict::Accept(()));
let res = f.call(20);
assert_eq!(res, Verdict::Accept(()));
let res = f.call(100);
assert_eq!(res, Verdict::Reject(()));
}
#[test]
fn call_runtime_function() {
let s = src!(
"
filtermap main(x: u32) {
if pow(x, 2) > 100 {
accept
} else {
reject
}
}
"
);
let rt = Runtime::from_lib(library! {
fn pow(x: u32, y: u32) -> u32 {
x.pow(y)
}
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(u32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for (value, expected) in
[(5, Verdict::Reject(())), (11, Verdict::Accept(()))]
{
let res = f.call(value);
assert_eq!(res, expected);
}
}
#[test]
fn call_runtime_method() {
let s = src!(
"
filtermap main(x: u32) {
if x.is_even() {
accept
} else {
reject
}
}
"
);
let rt = Runtime::from_lib(library! {
impl u32 {
fn is_even(self) -> bool {
self.is_multiple_of(2)
}
}
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(u32) -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
for (value, expected) in
[(5, Verdict::Reject(())), (10, Verdict::Accept(()))]
{
let res = f.call(value);
assert_eq!(res, expected);
}
}
#[test]
fn int_var() {
let s = src!(
"
filtermap main() {
accept 32
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<i32, ()>>("main")
.expect("No function found (or mismatched types)");
assert_eq!(f.call(), Verdict::Accept(32));
}
#[test]
fn issue_52() {
#[derive(Clone)]
struct Foo {
_x: i32,
}
let rt = Runtime::from_lib(library! {
#[clone] type Foo = Val<Foo>;
impl Val<Foo> {
fn bar(_x: u32) -> u32 {
2
}
}
})
.unwrap();
let s = src!(
"
filtermap main(foo: Foo) {
Foo.bar(1);
accept
}
"
);
let _p = compile_with_runtime(s, rt);
}
#[test]
fn register_with_non_registered_type() {
#[derive(Clone)]
struct Foo {
_x: i32,
}
Runtime::from_lib(library! {
fn bar(_foo: Val<Foo>, _x: u32) {}
})
.unwrap_err();
}
#[test]
fn asn() {
let s = src!(
"
filtermap main(x: Asn) {
if x == AS1000 {
accept x
} else {
reject x
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(Asn) -> Verdict<Asn, Asn>>("main")
.expect("No function found (or mismatched types)");
assert_eq!(
f.call(Asn::from_u32(1000)),
Verdict::Accept(Asn::from_u32(1000))
);
assert_eq!(
f.call(Asn::from_u32(2000)),
Verdict::Reject(Asn::from_u32(2000))
);
}
#[test]
fn mismatched_types() {
let s = src!(
"
filtermap main(x: i32) {
accept x
}
"
);
let mut p = compile(s);
let err = p
.get_function::<fn(i8) -> Verdict<i8, ()>>("main")
.unwrap_err();
eprintln!("{err}");
assert!(err.to_string().contains("do not match"));
}
#[test]
fn multiply() {
let s = src!(
"
filtermap main(x: u8) {
if x > 10 {
accept 2 * x
} else {
reject
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(u8) -> Verdict<u8, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(20);
assert_eq!(res, Verdict::Accept(40));
}
#[test]
fn remainder() {
let s = src!(
"
fn remainder(x: u64, y: u64) -> u64 {
while x > y {
x = x - y;
}
x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(u64, u64) -> u64>("remainder")
.expect("No function found (or mismatched types)");
let res = f.call(55, 10);
assert_eq!(res, 5);
}
#[test]
fn factorial() {
let s = src!(
"
fn factorial(x: u64) -> u64 {
let i = 1;
let n = 1;
while i <= x {
n = n * i;
i = i + 1;
}
n
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(u64) -> u64>("factorial")
.expect("No function found (or mismatched types)");
let res = f.call(5);
assert_eq!(res, 120);
}
#[test]
fn nested_while_loop() {
let s = src!(
"
fn nested_while_loop() -> u64 {
let res = 0;
let x = 0;
while x < 10 {
let y = 0;
while y < 10 {
res = res + 1;
y = y + 1;
}
x = x + 1;
}
res
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> u64>("nested_while_loop")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, 100);
}
#[test]
fn repeat_string_manually() {
let s = src!(
"
fn repeat(s: String, n: u64) -> String {
let res = \"\";
while n > 0 {
res = res + s;
n = n - 1;
}
res
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>, u64) -> Arc<str>>("repeat")
.expect("No function found (or mismatched types)");
let res = f.call("foo".into(), 6);
assert_eq!(res, "foofoofoofoofoofoo".into());
}
#[test]
fn float_mul() {
let s = src!(
"
filtermap main(x: f32) {
accept 2.0 * x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> Verdict<f32, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(20.0);
assert_eq!(res, Verdict::Accept(40.0));
}
#[test]
fn float_add() {
let s = src!(
"
filtermap main(x: f32) {
accept 2.0 + x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> Verdict<f32, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(20.0);
assert_eq!(res, Verdict::Accept(22.0));
}
#[test]
fn float_sub() {
let s = src!(
"
filtermap main(x: f32) {
accept 20.0 - x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> Verdict<f32, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(2.0);
assert_eq!(res, Verdict::Accept(18.0));
}
#[test]
fn float_cmp() {
let s = src!(
"
filtermap main(x: f32) {
accept x == 20.0
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> Verdict<bool, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(20.0);
assert_eq!(res, Verdict::Accept(true));
}
#[test]
fn float_div_zero() {
let s = src!(
"
filtermap main(x: f32) {
accept x / 0.0
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> Verdict<f32, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(20.0);
assert_eq!(res, Verdict::Accept(f32::INFINITY));
let res = f.call(-20.0);
assert_eq!(res, Verdict::Accept(-f32::INFINITY));
let Verdict::Accept(res) = f.call(0.0) else {
panic!("should have returned accept")
};
assert!(res.is_nan());
}
#[test]
fn float_floor() {
let s = src!("fn floor(x: f32) -> f32 { x.floor() }");
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> f32>("floor")
.expect("No function found (or mismatched types)");
let res = f.call(20.5);
assert_eq!(res, 20.0);
let res = f.call(-20.5);
assert_eq!(res, -21.0);
}
#[test]
fn float_ceil() {
let s = src!("fn ceil(x: f32) -> f32 { x.ceil() }");
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> f32>("ceil")
.expect("No function found (or mismatched types)");
let res = f.call(20.5);
assert_eq!(res, 21.0);
let res = f.call(-20.5);
assert_eq!(res, -20.0);
}
#[test]
fn float_round() {
let s = src!("fn round(x: f32) -> f32 { x.round() }");
let mut p = compile(s);
let f = p
.get_function::<fn(f32) -> f32>("round")
.expect("No function found (or mismatched types)");
let res = f.call(20.6);
assert_eq!(res, 21.0);
let res = f.call(20.4);
assert_eq!(res, 20.0);
}
#[test]
fn float_pow() {
let s = src!("fn pow(x: f32, y: f32) -> f32 { x.pow(y) }");
let mut p = compile(s);
let f = p
.get_function::<fn(f32, f32) -> f32>("pow")
.expect("No function found (or mismatched types)");
let res = f.call(2.0, 2.0);
assert_eq!(res, 4.0);
let res = f.call(25.0, 0.5);
assert_eq!(res, 5.0);
}
#[test]
fn float_scientific_notation_one() {
let s = src!("fn main() -> f32 { 20.0e4 }");
let mut p = compile(s);
let f = p
.get_function::<fn() -> f32>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, 20.0e4);
}
#[test]
fn float_scientific_notation_two() {
let s = src!("fn main() -> f32 { 20.0e-4 }");
let mut p = compile(s);
let f = p
.get_function::<fn() -> f32>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, 20.0e-4);
}
#[test]
fn float_add_f64() {
let s = src!(
"
filtermap main(x: f64) {
accept x + 20.0
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(f64) -> Verdict<f64, ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call(20.0);
assert_eq!(res, Verdict::Accept(40.0));
}
#[test]
fn ip_output() {
let s = src!(
"
filtermap main() {
accept 1.2.3.4
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<IpAddr, ()>>("main")
.expect("No function found (or mismatched types)");
let ip = IpAddr::from([1, 2, 3, 4]);
let res = f.call();
assert_eq!(res, Verdict::Accept(ip));
}
#[test]
fn ip_passthrough() {
let s = src!(
"
filtermap main(x: IpAddr) {
accept x
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(IpAddr) -> Verdict<IpAddr, ()>>("main")
.expect("No function found (or mismatched types)");
let ip = IpAddr::from([1, 2, 3, 4]);
let res = f.call(ip);
assert_eq!(res, Verdict::Accept(ip));
}
#[test]
fn ipv4_compare() {
let s = src!(
"
filtermap main(x: IpAddr) {
if x == 0.0.0.0 {
accept x
} else if x == 192.168.0.0 {
accept x
} else {
reject x
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(IpAddr) -> Verdict<IpAddr, IpAddr>>("main")
.expect("No function found (or mismatched types)");
let ip = IpAddr::from([0, 0, 0, 0]);
let res = f.call(ip);
assert_eq!(res, Verdict::Accept(ip));
let ip = IpAddr::from([192, 168, 0, 0]);
let res = f.call(ip);
assert_eq!(res, Verdict::Accept(ip));
let ip = IpAddr::from([1, 2, 3, 4]);
let res = f.call(ip);
assert_eq!(res, Verdict::Reject(ip));
let ip = IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0]);
let res = f.call(ip);
assert_eq!(res, Verdict::Reject(ip));
}
#[test]
fn ipv6_compare() {
let s = src!(
"
filtermap main(x: IpAddr) {
if x == :: {
accept x
} else if x == 192.168.0.0 {
accept x
} else if x == ::1 {
accept x
} else {
reject x
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(IpAddr) -> Verdict<IpAddr, IpAddr>>("main")
.expect("No function found (or mismatched types)");
let ip = IpAddr::from([0, 0, 0, 0, 0, 0, 0, 0]);
let res = f.call(ip);
assert_eq!(res, Verdict::Accept(ip));
let ip = IpAddr::from([0, 0, 0, 0, 0, 0, 0, 1]);
let res = f.call(ip);
assert_eq!(res, Verdict::Accept(ip));
let ip = IpAddr::from([192, 168, 0, 0]);
let res = f.call(ip);
assert_eq!(res, Verdict::Accept(ip));
let ip = IpAddr::from([1, 2, 3, 4]);
let res = f.call(ip);
assert_eq!(res, Verdict::Reject(ip));
}
#[test]
fn construct_prefix() {
let s = src!(
"
filtermap main() {
accept 192.168.0.0 / 16
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<Prefix, ()>>("main")
.expect("No function found (or mismatched types)");
let p = Prefix::new("192.168.0.0".parse().unwrap(), 16).unwrap();
let res = f.call();
assert_eq!(res, Verdict::Accept(p));
}
#[test]
fn function_returning_unit() {
let rt = Runtime::from_lib(library! {
fn unit_unit() {}
})
.unwrap();
let s = src!(
"
filtermap main() {
accept unit_unit()
}
"
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn() -> Verdict<(), ()>>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, Verdict::Accept(()));
}
#[test]
fn to_string() {
let s = src!(
r#"
fn foo() -> String {
let a: u8 = 10;
let b: i32 = 20;
let c: f64 = 15.5;
let d = false;
let e = 1.1.1.1;
let f = 1.1.0.0 / 16;
let g = AS1000;
let h = "foo";
a.to_string()
+ " "
+ b.to_string()
+ " "
+ c.to_string()
+ " "
+ d.to_string()
+ " "
+ e.to_string()
+ " "
+ f.to_string()
+ " "
+ g.to_string()
+ " "
+ h.to_string()
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "10 20 15.5 false 1.1.1.1 1.1.0.0/16 AS1000 foo".into());
}
#[test]
fn simple_f_string() {
let s = src!(
r#"
fn foo(name: String) -> String {
f"Hello {name}!"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn(Arc<str>) -> Arc<str>>("foo").unwrap();
let res = f.call("John".into());
assert_eq!(res, "Hello John!".into());
}
#[test]
fn simple_f_string_number() {
let s = src!(
r#"
fn foo(x: i32) -> String {
f"Hello {x}!"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn(i32) -> Arc<str>>("foo").unwrap();
let res = f.call(10);
assert_eq!(res, "Hello 10!".into());
}
#[test]
fn complex_f_string() {
let s = src!(
r#"
fn foo() -> String {
f"This is a string with { f"another string which prints {true}" }, isn't that wonderful?"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "This is a string with another string which prints true, isn't that wonderful?".into());
}
#[test]
fn escape_curly_in_f_string() {
let s = src!(
r#"
fn foo() -> String {
f"Here is a single curly {{ and a closing one }}"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "Here is a single curly { and a closing one }".into());
}
#[test]
fn unicode_val_in_f_string() {
let s = src!(
r#"
fn foo() -> String {
f"Here is an uppercase \u{41} and a lowercase \u{61}."
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "Here is an uppercase A and a lowercase a.".into());
}
#[test]
fn f_string_and_int_var_1() {
let s = src!(
r#"
fn foo() -> String {
let x = 10;
f"This should just work: {x}"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "This should just work: 10".into());
}
#[test]
fn f_string_and_int_var_2() {
let s = src!(
r#"
fn foo() -> String {
let x = 10;
let s = f"This should just work: {x}";
let y: u8 = x;
s
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "This should just work: 10".into());
}
#[test]
fn f_string_and_float_var_1() {
let s = src!(
r#"
fn foo() -> String {
let x = 10.0;
f"This should just work: {x}"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "This should just work: 10".into());
}
#[test]
fn f_string_and_float_var_2() {
let s = src!(
r#"
fn foo() -> String {
let x = 10.0;
let s = f"This should just work: {x}";
let y: f32 = x;
s
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("foo").unwrap();
let res = f.call();
assert_eq!(res, "This should just work: 10".into());
}
#[test]
fn arc_type() {
use std::sync::atomic::Ordering;
static CLONES: AtomicUsize = AtomicUsize::new(0);
static DROPS: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
struct CloneDrop {
clones: &'static AtomicUsize,
drops: &'static AtomicUsize,
}
impl Clone for CloneDrop {
fn clone(&self) -> Self {
self.clones.fetch_add(1, Ordering::Relaxed);
Self {
clones: self.clones,
drops: self.drops,
}
}
}
impl Drop for CloneDrop {
fn drop(&mut self) {
self.drops.fetch_add(1, Ordering::Relaxed);
}
}
let rt = Runtime::from_lib(library! {
#[clone] type CloneDrop = Val<CloneDrop>;
})
.unwrap();
let s = src!(
"
filtermap main(choose: bool, x: CloneDrop, y: CloneDrop) {
if choose {
accept x
} else {
accept y
}
}
"
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(
bool,
Val<CloneDrop>,
Val<CloneDrop>,
) -> Verdict<Val<CloneDrop>, ()>>("main")
.unwrap();
let input = CloneDrop {
clones: &CLONES,
drops: &DROPS,
};
println!("{input:?}");
let output = f.call(true, Val(input.clone()), Val(input));
let output = output.into_result().unwrap().0;
println!("{output:?}");
assert_eq!(
output.clones.load(Ordering::Relaxed),
output.drops.load(Ordering::Relaxed)
);
}
#[test]
fn use_constant() {
let s = src!(
"
filtermap main() {
let safi = 127.0.0.1;
if safi == IpAddr.LOCALHOSTV4 {
reject
}
accept
}"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Verdict<(), ()>>("main").unwrap();
let output = f.call();
assert_eq!(output, Verdict::Reject(()));
}
#[test]
fn import_associated_constant() {
let s = src!(
"
import IpAddr.LOCALHOSTV4;
filtermap main() {
let safi = 127.0.0.1;
if safi == LOCALHOSTV4 {
reject
}
accept
}"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Verdict<(), ()>>("main").unwrap();
let output = f.call();
assert_eq!(output, Verdict::Reject(()));
}
#[test]
fn registered_constant() {
let rt = Runtime::from_lib(library! {
const PI: f64 = std::f64::consts::PI;
})
.unwrap();
let s = src!(
"
fn circumference(radius: f64) -> f64 {
2.0 * PI * radius
}"
);
let mut p = compile_with_runtime(s, rt);
let f = p.get_function::<fn(f64) -> f64>("circumference").unwrap();
let output = f.call(2.0);
let exp = 2.0 * std::f64::consts::PI * 2.0;
assert!(exp - 0.1 < output && output < exp + 0.1);
}
#[test]
fn use_context() {
#[derive(Clone, Context)]
struct Ctx {
pub foo: i32,
pub bar: bool,
}
let rt = Runtime::new().with_context_type::<Ctx>().unwrap();
let s = src!(
"
filtermap main() {
if bar {
accept foo + 1
} else {
accept foo
}
}"
);
let mut p = compile_with_runtime(s, rt);
let f = p.get_function::<fn() -> Verdict<i32, ()>>("main").unwrap();
let mut ctx = Ctx { foo: 9, bar: false };
let output = f.call(&mut ctx);
assert_eq!(output, Verdict::Accept(9));
let mut ctx = Ctx { foo: 10, bar: true };
let output = f.call(&mut ctx);
assert_eq!(output, Verdict::Accept(11));
}
#[test]
fn use_a_roto_function() {
let s = src!(
"
fn double(x: i32) -> i32 {
2 * x
}"
);
let mut p = compile(s);
let f = p.get_function::<fn(i32) -> i32>("double").unwrap();
let output = f.call(2);
assert_eq!(output, 4);
let output = f.call(16);
assert_eq!(output, 32);
}
#[test]
fn use_a_test() {
let s = src!(
"
fn double(x: i32) -> i32 {
x # oops! not correct
}
test check_double {
if double(4) != 8 {
reject;
}
if double(16) != 32 {
reject;
}
accept
}
"
);
let mut p = compile(s);
p.run_tests().unwrap_err();
let s = src!(
"
fn double(x: i32) -> i32 {
2 * x
}
test check_double {
if double(4) != 8 {
reject;
}
if double(16) != 32 {
reject;
}
accept
}
"
);
let mut p = compile(s);
p.run_tests().unwrap();
}
#[test]
fn get_tests() {
let s = src!(
"
test check_output {
accept
}
"
);
let mut p = compile(s);
let tests: Vec<_> = p.get_tests().collect();
assert!(!tests.is_empty());
assert_eq!(tests[0].name(), "pkg.check_output");
tests[0].run(&mut NoCtx).unwrap();
}
#[test]
fn get_no_tests() {
let s = src!(
"
fn double(x: i32) -> i32 {
2 * x
}
"
);
let mut p = compile(s);
let tests = p.get_tests();
assert_eq!(tests.count(), 0);
}
#[test]
fn string() {
let s = src!(
r#"
filtermap main() {
accept "hello"
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<Arc<str>, ()>>("main")
.unwrap();
let res = f.call();
assert_eq!(res, Verdict::Accept("hello".into()));
}
#[test]
fn escape_string() {
let s = src!(
r#"
fn main() -> String {
"\t\tfoo"
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Arc<str>>("main").unwrap();
let res = f.call();
assert_eq!(res, "\t\tfoo".into());
}
#[test]
fn string_append() {
let s = src!(
r#"
filtermap main(name: String) {
accept "Hello ".append(name).append("!")
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>) -> Verdict<Arc<str>, ()>>("main")
.unwrap();
let res = f.call("Martin".into());
assert_eq!(res, Verdict::Accept("Hello Martin!".into()));
}
#[test]
fn string_append_as_function_call() {
let s = src!(
r#"
fn main(name: String) -> String {
String.append("Hello ", name)
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn(Arc<str>) -> Arc<str>>("main").unwrap();
let res = f.call("Martin".into());
assert_eq!(res, "Hello Martin".into());
}
#[test]
fn string_append_as_imported_function() {
let s = src!(
r#"
import String.append;
fn main(name: String) -> String {
append("Hello ", name)
}
"#
);
let mut p = compile(s);
let f = p.get_function::<fn(Arc<str>) -> Arc<str>>("main").unwrap();
let res = f.call("Martin".into());
assert_eq!(res, "Hello Martin".into());
}
#[test]
fn string_plus_operator() {
let s = src!(
r#"
filtermap main(name: String) {
accept "Hello " + name + "!"
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>) -> Verdict<Arc<str>, ()>>("main")
.unwrap();
let res = f.call("Martin".into());
assert_eq!(res, Verdict::Accept("Hello Martin!".into()));
}
#[test]
fn string_contains() {
let s = src!(
r#"
filtermap main(s: String) {
if "incomprehensibilities".contains(s) {
accept
} else {
reject
}
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>) -> Verdict<(), ()>>("main")
.unwrap();
let res = f.call("incompre".into());
assert_eq!(res, Verdict::Accept(()));
let res = f.call("hensi".into());
assert_eq!(res, Verdict::Accept(()));
let res = f.call("bilities".into());
assert_eq!(res, Verdict::Accept(()));
let res = f.call("nananana".into());
assert_eq!(res, Verdict::Reject(()));
}
#[test]
fn string_starts_with() {
let s = src!(
r#"
filtermap main(s: String) {
if "incomprehensibilities".starts_with(s) {
accept
} else {
reject
}
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>) -> Verdict<(), ()>>("main")
.unwrap();
let res = f.call("incompre".into());
assert_eq!(res, Verdict::Accept(()));
let res = f.call("hensi".into());
assert_eq!(res, Verdict::Reject(()));
let res = f.call("bilities".into());
assert_eq!(res, Verdict::Reject(()));
let res = f.call("nananana".into());
assert_eq!(res, Verdict::Reject(()));
}
#[test]
fn string_ends_with() {
let s = src!(
r#"
filtermap main(s: String) {
if "incomprehensibilities".ends_with(s) {
accept
} else {
reject
}
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>) -> Verdict<(), ()>>("main")
.unwrap();
let res = f.call("incompre".into());
assert_eq!(res, Verdict::Reject(()));
let res = f.call("hensi".into());
assert_eq!(res, Verdict::Reject(()));
let res = f.call("bilities".into());
assert_eq!(res, Verdict::Accept(()));
let res = f.call("nananana".into());
assert_eq!(res, Verdict::Reject(()));
}
#[test]
fn string_to_lowercase_and_uppercase() {
let s = src!(
r#"
filtermap main(lower: bool, s: String) {
if lower {
accept s.to_lowercase()
} else {
accept s.to_uppercase()
}
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(bool, Arc<str>) -> Verdict<Arc<str>, ()>>("main")
.unwrap();
let res = f.call(true, "WHISPER THIS!".into());
assert_eq!(res, Verdict::Accept("whisper this!".into()));
let res = f.call(false, "now shout this!".into());
assert_eq!(res, Verdict::Accept("NOW SHOUT THIS!".into()));
}
#[test]
fn string_repeat() {
let s = src!(
r#"
filtermap main(s: String) {
let exclamation = (s + "!").to_uppercase();
accept (exclamation + " ").repeat(4) + exclamation
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Arc<str>) -> Verdict<Arc<str>, ()>>("main")
.unwrap();
let res = f.call("boo".into());
assert_eq!(res, Verdict::Accept("BOO! BOO! BOO! BOO! BOO!".into()));
}
#[test]
fn match_option_value() {
let s = src!(
"
fn or_fortytwo(x: u32?) -> u32 {
match x {
Some(x) -> x,
None -> 42,
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(Option<u32>) -> u32>("or_fortytwo")
.unwrap();
let res = f.call(Some(10));
assert_eq!(res, 10);
let res = f.call(None);
assert_eq!(res, 42);
}
#[test]
fn match_option_string() {
let s = src!(
"
fn foo() -> String? {
Option.Some(\"Foobar\")
}
fn bar() -> Verdict[String, String] {
match foo() {
Some(v) -> accept v,
_ -> reject \"nope\",
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Verdict<Arc<str>, Arc<str>>>("bar")
.unwrap();
let res = f.call();
assert_eq!(res, Verdict::Accept("Foobar".into()));
}
#[test]
fn match_on_string_with_guards() {
let s = src!(
r#"
fn foo(s: String?, x: i32) -> String {
match s {
Some(s) | s == "hello" -> "hey!",
_ | x == 5 -> "x is 5",
Some(s) | s.starts_with("lorem ipsum") -> {
"You've generated lorem ipsum: " + s
}
_ -> "Can't recognize",
}
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(Option<Arc<str>>, i32) -> Arc<str>>("foo")
.unwrap();
let res = f.call(Some("hello".into()), 5);
assert_eq!(&*res, "hey!");
let res = f.call(Some("hello".into()), 4);
assert_eq!(&*res, "hey!");
let res = f.call(Some("lorem ipsum dolor".into()), 5);
assert_eq!(&*res, "x is 5");
let res = f.call(Some("lorem ipsum dolor".into()), 4);
assert_eq!(&*res, "You've generated lorem ipsum: lorem ipsum dolor");
let res = f.call(Some("nothing".into()), 5);
assert_eq!(&*res, "x is 5");
let res = f.call(Some("nothing".into()), 4);
assert_eq!(&*res, "Can't recognize");
}
#[test]
fn construct_option_value() {
let s = src!(
"
fn sub_one(x: u32) -> u32? {
if x == 0 {
Option.None
} else {
Option.Some(x - 1)
}
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn(u32) -> Option<u32>>("sub_one").unwrap();
let res = f.call(0);
assert_eq!(res, None);
let res = f.call(2);
assert_eq!(res, Some(1));
}
#[test]
fn construct_imported_option() {
let s = src!(
"
import Option.{Some, None};
fn sub_one(x: u32) -> u32? {
if x == 0 {
None
} else {
Some(x - 1)
}
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn(u32) -> Option<u32>>("sub_one").unwrap();
let res = f.call(0);
assert_eq!(res, None);
let res = f.call(2);
assert_eq!(res, Some(1));
}
#[test]
fn construct_option_value_from_prelude() {
let s = src!(
"
fn sub_one(x: u32) -> u32? {
if x == 0 {
None
} else {
Some(x - 1)
}
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn(u32) -> Option<u32>>("sub_one").unwrap();
let res = f.call(0);
assert_eq!(res, None);
let res = f.call(2);
assert_eq!(res, Some(1));
}
#[test]
fn filter_map_with_manual_verdict() {
let s = src!(
"
filtermap foo(x: i32) {
if x < 10 {
return Verdict.Reject(10)
} else {
return Verdict.Accept(x)
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<i32, i32>>("foo")
.unwrap();
let res = f.call(0);
assert_eq!(res, Verdict::Reject(10));
let res = f.call(12);
assert_eq!(res, Verdict::Accept(12));
}
#[test]
fn unused_accept() {
let s = src!(
"
fn foo() {
Verdict.Accept(5);
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> ()>("foo").unwrap();
f.call();
}
#[test]
fn match_on_verdict() {
let s = src!(
"
filtermap over_10(x: i32) {
if x > 10 {
accept x
} else {
reject x
}
}
filtermap foo(x: i32) {
match over_10(x) {
Accept(x) -> accept x + 1,
Reject(x) -> reject x,
};
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32) -> Verdict<i32, i32>>("foo")
.unwrap();
let res = f.call(5);
assert_eq!(res, Verdict::Reject(5));
let res = f.call(15);
assert_eq!(res, Verdict::Accept(16));
let res = f.call(25);
assert_eq!(res, Verdict::Accept(26));
}
#[test]
fn non_sugar_option() {
let s = src!(
"
fn foo() -> Option[u32] {
Option.Some(2)
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Option<u32>>("foo").unwrap();
let res = f.call();
assert_eq!(res, Some(2));
}
#[test]
fn none_with_unknown_type() {
let s = src!(
"
fn foo() {
Option.None;
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> ()>("foo").unwrap();
f.call();
}
#[test]
fn question_mark() {
let s = src!(
"
fn bar() -> u32? {
Option.None
}
fn foo() -> u32? {
bar()?;
Option.Some(3)
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Option<u32>>("foo").unwrap();
let res = f.call();
assert_eq!(res, None);
}
#[test]
fn question_mark_in_chain() {
let s = src!(
"
fn is_finite(x: f64) -> f64? {
if x.is_finite() {
Option.Some(x)
} else {
Option.None
}
}
fn foo(x: f64) -> f64? {
let y = is_finite(x)?.pow(2.0);
Option.Some(y)
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn(f64) -> Option<f64>>("foo").unwrap();
let res = f.call(10.0);
assert_eq!(res, Some(100.0));
let res = f.call(f64::NAN);
assert_eq!(res, None);
}
#[test]
fn question_unit() {
let s = src!(
"
fn maybe_a_unit(x: bool) -> ()? {
if x {
Some(())
} else {
None
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(bool) -> Option<()>>("maybe_a_unit")
.unwrap();
let res = f.call(false);
assert_eq!(res, None);
let res = f.call(true);
assert_eq!(res, Some(()));
}
#[test]
fn question_record() {
let s = src!(
"
fn maybe_an_i32(x: bool) -> i32? {
Some(maybe_a_record(x)?.a)
}
fn maybe_a_record(x: bool) -> { a: i32 }? {
if x {
Some({ a: 5 })
} else {
None
}
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(bool) -> Option<i32>>("maybe_an_i32")
.unwrap();
let res = f.call(false);
assert_eq!(res, None);
let res = f.call(true);
assert_eq!(res, Some(5));
}
#[test]
fn add_options() {
let s = src!(
"
fn small(x: i32) -> i32? {
if x < 10 {
Option.Some(x)
} else {
Option.None
}
}
fn foo(x: i32, y: i32) -> i32? {
let z = small(x)? + small(y)?;
Option.Some(z)
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn(i32, i32) -> Option<i32>>("foo")
.unwrap();
let res = f.call(2, 2);
assert_eq!(res, Some(4));
let res = f.call(5, 5);
assert_eq!(res, Some(10));
let res = f.call(15, 5);
assert_eq!(res, None);
let res = f.call(5, 10);
assert_eq!(res, None);
}
#[test]
fn question_question() {
let s = src!(
"
fn bar() -> u32?? {
Option.Some(Option.None)
}
fn foo() -> u32? {
bar()??;
Option.Some(4)
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Option<u32>>("foo").unwrap();
let res = f.call();
assert_eq!(res, None);
}
#[test]
fn question_mark_none() {
let s = src!(
"
fn foo() -> u32? {
Option.None?;
Option.Some(3)
}
"
);
let mut p = compile(s);
let f = p.get_function::<fn() -> Option<u32>>("foo").unwrap();
let res = f.call();
assert_eq!(res, None);
}
#[test]
fn top_level_import() {
let pkg = source_file!(
"pkg",
"
import foo.bar;
fn main(x: i32) -> i32 {
bar(x)
}
"
);
let foo = source_file!(
"foo",
"
fn bar(x: i32) -> i32 {
2 * x
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 8);
}
#[test]
fn local_import() {
let pkg = source_file!(
"pkg",
"
fn main(x: i32) -> i32 {
import foo.bar;
bar(x)
}
"
);
let foo = source_file!(
"foo",
"
fn bar(x: i32) -> i32 {
2 * x
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 8);
}
#[test]
fn parent_import() {
let pkg = source_file!(
"pkg",
"
import foo.quadruple;
fn main(x: i32) -> i32 {
quadruple(x)
}
fn double(x: i32) -> i32 {
2 * x
}
"
);
let foo = source_file!(
"foo",
"
import super.double;
fn quadruple(x: i32) -> i32 {
double(double(x))
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 16);
}
#[test]
fn package_import() {
let pkg = source_file!(
"pkg",
"
import foo.quadruple;
fn main(x: i32) -> i32 {
quadruple(x)
}
fn double(x: i32) -> i32 {
2 * x
}
"
);
let foo = source_file!(
"foo",
"
import pkg.double;
fn quadruple(x: i32) -> i32 {
double(double(x))
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 16);
}
#[test]
fn import_via_super() {
let pkg = source_file!(
"pkg",
"
import foo.a;
fn main(x: i32) -> i32 {
a(x)
}
"
);
let foo = source_file!(
"foo",
"
import super.bar.b;
fn a(x: i32) -> i32 {
b(x)
}
"
);
let bar = source_file!(
"bar",
"
fn b(x: i32) -> i32 {
2 * x
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo), FileSpec::File(bar)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 8);
}
#[test]
fn import_module_first() {
let pkg = source_file!(
"pkg",
"
import foo.a;
fn main(x: i32) -> i32 {
a(x)
}
"
);
let foo = source_file!(
"foo",
"
import super.bar;
import bar.b;
fn a(x: i32) -> i32 {
b(x)
}
"
);
let bar = source_file!(
"bar",
"
fn b(x: i32) -> i32 {
2 * x
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo), FileSpec::File(bar)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 8);
}
#[test]
fn import_module_second() {
let pkg = source_file!(
"pkg",
"
import foo.a;
fn main(x: i32) -> i32 {
a(x)
}
"
);
let foo = source_file!(
"foo",
"
import bar.b;
import super.bar;
fn a(x: i32) -> i32 {
b(x)
}
"
);
let bar = source_file!(
"bar",
"
fn b(x: i32) -> i32 {
2 * x
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo), FileSpec::File(bar)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 8);
}
#[test]
fn use_type_from_module() {
let pkg = source_file!(
"pkg",
"
fn main(x: i32) -> i32 {
let foofoo = foo.Foo { bar: x };
foofoo.bar
}
"
);
let foo = source_file!(
"foo",
"
record Foo {
bar: i32,
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 4);
}
#[test]
fn use_imported_type() {
let pkg = source_file!(
"pkg",
"
import foo.Foo;
fn main(x: i32) -> i32 {
let foofoo = Foo { bar: x };
foofoo.bar
}
"
);
let foo = source_file!(
"foo",
"
record Foo {
bar: i32,
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 4);
}
#[test]
fn use_type_in_function_argument() {
let pkg = source_file!(
"pkg",
"
fn main(x: i32) -> i32 {
get_bar(foo.Foo { bar: x })
}
fn get_bar(f: foo.Foo) -> i32 {
f.bar
}
"
);
let foo = source_file!(
"foo",
"
record Foo {
bar: i32,
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 4);
}
#[test]
fn import_list() {
let pkg = source_file!(
"pkg",
"
import foo.{double, triple};
fn main(x: i32) -> i32 {
triple(double(x))
}
"
);
let foo = source_file!(
"foo",
"
fn double(x: i32) -> i32 {
2 * x
}
fn triple(x: i32) -> i32 {
3 * x
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(1);
assert_eq!(res, 6);
}
#[test]
fn use_type_in_function_return_type() {
let pkg = source_file!(
"pkg",
"
fn main(x: i32) -> i32 {
make_foo(x).bar
}
fn make_foo(x: i32) -> foo.Foo {
foo.Foo { bar: x }
}
"
);
let foo = source_file!(
"foo",
"
record Foo {
bar: i32,
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 4);
}
#[test]
fn use_type_from_other_module_in_type() {
let pkg = source_file!(
"pkg",
"
record Bli {
bla: foo.Bla,
}
fn main(x: i32) -> i32 {
Bli { bla: foo.Bla { blubb: x }}.bla.blubb
}
"
);
let foo = source_file!(
"foo",
"
record Bla {
blubb: i32,
}
"
);
let tree = FileTree::file_spec(FileSpec::Directory(
pkg,
vec![FileSpec::File(foo)],
));
let mut p = compile(tree);
let main = p.get_function::<fn(i32) -> i32>("main").unwrap();
let res = main.call(4);
assert_eq!(res, 4);
}
#[test]
fn mutate() {
#[derive(Copy, Clone, Debug)]
struct MyType {
i: i16,
}
let rt = Runtime::from_lib(library! {
#[copy] type MyType = Val<*mut MyType>;
impl Val<*mut MyType> {
fn increase(mut self) {
eprintln!("increase, pre: {}", unsafe { (**self).i });
unsafe { (**self).i += 1 };
eprintln!("increase, post: {}", unsafe { (**self).i });
}
}
})
.unwrap();
let s = src!(
"
filter main(t: MyType) {
t.increase();
accept t;
}
"
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(Val<*mut MyType>) -> Verdict<Val<*mut MyType>, ()>>("main")
.expect("No function found (or mismatched types)");
let mut t = MyType { i: 0 };
let res = f.call(Val(&mut t));
match res {
Verdict::Accept(val) => {
let val = unsafe { &*val.0 };
assert_eq!(val.i, 1, "returned value should be 1")
}
Verdict::Reject(_) => todo!(),
}
assert_eq!(t.i, 1, "mutated value should be 1");
}
#[test]
fn return_vec() {
#[derive(Clone, Debug, Default)]
#[allow(dead_code)]
struct MyType {
v: Vec<u8>,
}
let rt = Runtime::from_lib(library! {
#[clone] type MyType = Val<MyType>;
})
.unwrap();
let s = src!(
"
fn main(t: MyType) -> MyType {
t
}
"
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(Val<MyType>) -> Val<MyType>>("main")
.expect("No function found (or mismatched types)");
let t = MyType { v: vec![0x01] };
let res = f.call(Val(t));
println!("Returned with {:?}", res.0.v.as_ptr());
}
#[test]
fn name_collision() {
#[derive(Clone)]
struct A {
_x: u32,
}
#[derive(Clone)]
struct B {
_x: u32,
}
let rt = Runtime::from_lib(library! {
#[clone] type A = Val<A>;
impl Val<A> {
fn foo(self) -> bool {
true
}
}
#[clone] type B = Val<B>;
impl Val<B> {
fn foo(self) -> bool {
unreachable!()
}
}
})
.unwrap();
let s = src!(
"
filter foo(t: B) {
accept t.foo()
}
filter main(t: A) {
accept t.foo()
}
"
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(Val<A>) -> Verdict<bool, ()>>("main")
.expect("No function found (or mismatched types)");
let t = A { _x: 1 };
let res = f.call(Val(t));
assert_eq!(res, Verdict::Accept(true));
}
#[test]
fn refcounting_in_a_recursive_function() {
#[derive(Debug, Clone)]
struct Foo;
let rt = Runtime::from_lib(library! {
#[clone] type Foo = Val<Arc<Foo>>;
})
.unwrap();
let s = src!(
r##"
fn f(foo: Foo, idx: i32) {
if idx > 0 { f(foo, idx - 1); }
}
fn main(foo: Foo) -> Verdict[i32, i32] {
f(foo, 1);
reject 3
}
"##
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(Val<Arc<Foo>>) -> Verdict<i32, i32>>("main")
.unwrap();
let v = Arc::new(Foo);
let res = f.call(Val(v.clone()));
assert_eq!(res, Verdict::Reject(3));
assert_eq!(Arc::strong_count(&v), 1);
}
#[test]
fn str_equals() {
let s = src!(
r#"
fn is_slash(s: String) -> bool {
s == "/"
}
"#
);
let mut compiled = compile(s);
let func = compiled
.get_function::<fn(Arc<str>) -> bool>("is_slash")
.unwrap();
assert!(func.call("/".into()));
assert!(!func.call("foo".into()));
}
#[test]
fn str_not_equals() {
let s = src!(
r#"
fn is_not_slash(s: String) -> bool {
s != "/"
}
"#
);
let mut compiled = compile(s);
let func = compiled
.get_function::<fn(Arc<str>) -> bool>("is_not_slash")
.unwrap();
assert!(func.call("foo".into()));
assert!(!func.call("/".into()));
}
#[test]
fn assignment() {
let s = src!(
"
fn foo() -> i32 {
let x = 4;
x = x + 3;
x
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 7);
}
#[test]
fn assignment_record_field() {
let s = src!(
"
fn foo() -> i32 {
let x = { bar: 4 };
x.bar = x.bar + 3;
x.bar
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 7);
}
#[test]
fn assignment_record() {
let s = src!(
"
fn foo() -> i32 {
let x = { bar: 4 };
x = { bar: x.bar + 3 };
x.bar
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 7);
}
#[test]
fn assignment_record_is_by_value() {
let s = src!(
"
fn foo() -> i32 {
let x = { bar: 4 };
let y = { bar: 5 };
x = y;
x.bar = 6;
y.bar
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 5);
}
#[test]
fn assignment_nested_record_1() {
let s = src!(
"
fn foo() -> i32 {
let x = { bar: { baz: 1 } };
x.bar.baz = 6;
x.bar.baz
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 6);
}
#[test]
fn assignment_nested_record_2() {
let s = src!(
"
fn foo() -> i32 {
let x = { bar: { baz: 1 } };
x.bar = { baz: 6 };
x.bar.baz
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 6);
}
#[test]
fn assignment_string() {
let s = src!(
"
fn foo() -> String {
let x = \"foo\";
x = x + x;
x = x + x;
x
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> Arc<str>>("foo").unwrap();
assert_eq!(func.call(), "foofoofoofoo".into());
}
#[test]
fn let_declaration_is_by_value() {
let s = src!(
"
fn foo() -> i32 {
let y = { bar: 5 };
let x = y;
x.bar = 6;
y.bar
}
"
);
let mut compiled = compile(s);
let func = compiled.get_function::<fn() -> i32>("foo").unwrap();
assert_eq!(func.call(), 5);
}
#[test]
fn sigill() {
struct Arcane(Arc<()>);
impl Clone for Arcane {
fn clone(&self) -> Self {
assert!(Arc::strong_count(&self.0) > 0);
Arcane(self.0.clone())
}
}
impl Drop for Arcane {
fn drop(&mut self) {
assert!(Arc::strong_count(&self.0) > 0);
}
}
let rt = Runtime::from_lib(library! {
#[clone] type Arcane = Val<Arcane>;
impl Val<Arcane> {
fn get(self) -> u64 {
Arc::strong_count(&self.0.0) as u64
}
}
fn make_arcane() -> Val<Arcane> {
Val(Arcane(Arc::new(())))
}
})
.unwrap();
let s = src!(
"
fn bar(a: Arcane) -> u64 {
a.get()
}
fn foo() -> Arcane {
let a = make_arcane();
bar(a);
bar(a);
a
}
"
);
let mut compiled = compile_with_runtime(s, rt);
let func = compiled.get_function::<fn() -> Val<Arcane>>("foo").unwrap();
let Val(a) = func.call();
assert_eq!(Arc::strong_count(&a.0), 1);
}
#[test]
fn rust_string_string() {
let rt = Runtime::from_lib(library! {
#[clone] type RustString = Val<String>;
impl Val<String> {
fn new(s: Arc<str>) -> Val<String> {
Val(s.as_ref().into())
}
}
})
.unwrap();
let s = src!(
"
fn foo() -> RustString {
let s = RustString.new(\"hello\");
s = s;
s
}
fn bar() -> RustString {
let s = RustString.new(\"hello\");
let a = { str: s };
a.str = a.str;
a.str
}
"
);
let mut compiled = compile_with_runtime(s, rt);
let func = compiled.get_function::<fn() -> Val<String>>("foo").unwrap();
assert_eq!(func.call(), Val("hello".into()));
let func = compiled.get_function::<fn() -> Val<String>>("bar").unwrap();
assert_eq!(func.call(), Val("hello".into()));
}
#[test]
fn return_verdict_from_runtime_function() {
let rt = Runtime::from_lib(library! {
fn foo() -> Verdict<(), ()> {
Verdict::Accept(())
}
})
.unwrap();
let s = src!(
"
filtermap bar() {
foo()
}
"
);
let mut compiled = compile_with_runtime(s, rt);
let func = compiled
.get_function::<fn() -> Verdict<(), ()>>("bar")
.unwrap();
assert_eq!(Verdict::Accept(()), func.call());
}
#[test]
fn string_global() {
let s = src!(
"fn use_foo() -> String {
FOO
}"
);
let rt = Runtime::from_lib(library! {
const FOO: Arc<str> = "BAR".into();
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p.get_function::<fn() -> Arc<str>>("use_foo").unwrap();
assert_eq!(f.call(), "BAR".into());
}
#[test]
fn layered_option_matching_none() {
let s = src!(
"
fn reproducer() -> String? {
let s = match None {
Some(v) -> v,
None -> { return None; },
};
Some(s)
}
"
);
let mut p = compile(s);
let f = p
.get_function::<fn() -> Option<Arc<str>>>("reproducer")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, None);
}
#[test]
fn strings_from_if() {
let s = src!(
r#"
fn foo(x: bool) -> String {
if x { "true" } else { "false" }
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(bool) -> Arc<str>>("foo")
.expect("No function found (or mismatched types)");
let res = f.call(false);
assert_eq!(res, "false".into());
let res = f.call(true);
assert_eq!(res, "true".into());
}
#[test]
fn bool_is_true() {
let s = src!(
r#"
fn foo(x: bool) -> bool {
x == true
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(bool) -> bool>("foo")
.expect("No function found (or mismatched types)");
let res = f.call(false);
assert!(!res);
let res = f.call(true);
assert!(res);
}
#[test]
fn bool_is_not_true() {
let s = src!(
r#"
fn foo(x: bool) -> bool {
x != true
}
"#
);
let mut p = compile(s);
let f = p
.get_function::<fn(bool) -> bool>("foo")
.expect("No function found (or mismatched types)");
let res = f.call(false);
assert!(res);
let res = f.call(true);
assert!(!res);
}
#[test]
fn register_on_optstr() {
let rt = Runtime::from_lib(library! {
#[clone] type OptStr = Val<Option<Arc<str>>>;
impl Val<Option<Arc<str>>> {
fn unwrap_or_empty(self) -> Arc<str> {
self.0.unwrap_or_default()
}
}
})
.unwrap();
let s = src!(
r#"
fn foo(x: OptStr) -> String {
x.unwrap_or_empty()
}
"#
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn(Val<Option<Arc<str>>>) -> Arc<str>>("foo")
.expect("No function found (or mismatched types)");
let res = f.call(Val(None));
assert_eq!(res, "".into());
let res = f.call(Val(Some("hello".into())));
assert_eq!(res, "hello".into());
}
#[test]
fn register_closure() {
let some_number = 10;
let rt = Runtime::from_lib(library! {
let get = move || { some_number };
})
.unwrap();
let s = src!(
r#"
fn foo() -> i32 {
get()
}
"#
);
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn() -> i32>("foo")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, 10);
}
#[test]
fn increment_via_closure() {
static COUNTER: AtomicI32 = AtomicI32::new(0);
let runtime = Runtime::from_lib(library! {
let inc = || {
COUNTER.fetch_add(1, Ordering::Relaxed)
};
})
.unwrap();
let s = src!(
r#"
fn foo() {
inc();
inc();
}
"#
);
let mut p = compile_with_runtime(s, runtime);
let f = p
.get_function::<fn()>("foo")
.expect("No function found (or mismatched types)");
f.call();
assert_eq!(COUNTER.load(Ordering::Relaxed), 2);
}
#[test]
fn call_runtime_function_in_f_string() {
let s = src!(
r#"
fn foo() -> String {
f"foo{gimme_an_asn()}bar"
}
"#
);
let rt = Runtime::from_lib(library! {
fn gimme_an_asn() -> Asn {
Asn::from_u32(2)
}
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn() -> Arc<str>>("foo")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, "fooAS2bar".into());
}
#[test]
fn f32_issue() {
let s = src!(
r#"
fn foo() {
let x = 5.0;
let y: f32 = 2.0;
let z: f32 = x * y;
}
"#
);
let rt = Runtime::from_lib(library! {
fn rand() -> f32 {
10.0
}
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn()>("foo")
.expect("No function found (or mismatched types)");
f.call();
}
#[test]
fn register_module() {
let s = src!(
r#"
import math.{PI, sin, cos};
fn foo() -> f32 {
cos(PI) + sin(PI)
}
"#
);
let rt = Runtime::from_lib(library! {
mod math {
const PI: f32 = std::f32::consts::PI;
fn sin(x: f32) -> f32 {
x.sin()
}
fn cos(x: f32) -> f32 {
x.cos()
}
}
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn() -> f32>("foo")
.expect("No function found (or mismatched types)");
let res = f.call();
assert!(-1.1 < res && res < -0.9);
}
#[test]
fn register_type_in_module() {
let s = src!(
r#"
fn main() -> u32 {
foo.Foo.new().bar()
}
"#
);
#[derive(Clone)]
struct Foo(u32);
let rt = Runtime::from_lib(library! {
mod foo {
#[clone] type Foo = Val<Foo>;
impl Val<Foo> {
fn new() -> Self {
Val(Foo(3))
}
fn bar(self) -> u32 {
self.0.0
}
}
}
})
.unwrap();
let mut p = compile_with_runtime(s, rt);
let f = p
.get_function::<fn() -> u32>("main")
.expect("No function found (or mismatched types)");
let res = f.call();
assert_eq!(res, 3);
}
#[test]
fn assignment_with_question_mark() {
let s = src!(
r#"
fn foo() -> ()? {
let x: String? = None;
let y = "hello";
y = x?;
Some(())
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> Option<()>>("foo").unwrap();
f.call();
}
#[test]
fn prefix_eq() {
let s = src!(
r#"
fn foo() -> bool {
let x = 10.0.0.0 / 10;
let y = 10.0.0.0 / 10;
x == y
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> bool>("foo").unwrap();
f.call();
}
#[test]
fn define_variant_type() {
let s = src!(
"
variant Foo {
Bar,
Baz,
}
fn make_foo(x: bool) -> Foo {
if x {
Foo.Bar
} else {
Foo.Baz
}
}
fn match_on_foo(x: bool) -> i32 {
match make_foo(x) {
Bar -> 10,
Baz -> 20,
}
}
"
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn(bool) -> i32>("match_on_foo").unwrap();
let res = f.call(true);
assert_eq!(res, 10);
let res = f.call(false);
assert_eq!(res, 20);
}
#[test]
fn haskeller_wants_to_feel_at_home() {
let s = src!(
"
variant Maybe {
Just(i32),
Nothing,
}
import Maybe.{Just, Nothing};
fn from_option(x: i32?) -> Maybe {
match x {
None -> Nothing,
Some(x) -> Just(x)
}
}
fn to_option(x: Maybe) -> i32? {
match x {
Nothing -> None,
Just(x) -> Some(x),
}
}
fn useless(x: i32?) -> i32? {
to_option(from_option(x))
}
"
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn(Option<i32>) -> Option<i32>>("useless")
.unwrap();
let res = f.call(Some(5));
assert_eq!(res, Some(5));
let res = f.call(None);
assert_eq!(res, None);
}
#[test]
fn generic_haskeller_wants_to_feel_at_home() {
let s = src!(
"
variant Maybe[T] {
Just(T),
Nothing,
}
import Maybe.{Just, Nothing};
fn from_option(x: i32?) -> Maybe[i32] {
match x {
None -> Nothing,
Some(x) -> Just(x)
}
}
fn to_option(x: Maybe[i32]) -> i32? {
match x {
Nothing -> None,
Just(x) -> Some(x),
}
}
fn useless(x: i32?) -> i32? {
to_option(from_option(x))
}
"
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn(Option<i32>) -> Option<i32>>("useless")
.unwrap();
let res = f.call(Some(5));
assert_eq!(res, Some(5));
let res = f.call(None);
assert_eq!(res, None);
}
#[test]
fn match_on_empty_variant() {
let s = src!(
"
variant Foo {}
fn foo(x: Foo) {
match x {}
}
"
);
let _pkg = compile(s);
}
#[test]
fn match_on_empty_variant_2() {
let s = src!(
"
variant Foo {}
fn foo() {
let x: Foo = return;
match x {}
}
"
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> ()>("foo").unwrap();
f.call()
}
#[test]
fn match_on_uninhabited_variant() {
let s = src!(
"
variant Foo { Baz(!) }
fn foo(x: Foo) {
match x {
Baz(x) -> {}
}
}
"
);
let _pkg = compile(s);
}
#[test]
fn lets_make_a_result() {
let s = src!(
"
variant Result[T, E] {
Ok(T),
Err(E),
}
import Result.{Ok, Err};
fn from(x: Verdict[i32, u32]) -> Result[i32, u32] {
match x {
Reject(x) -> Err(x),
Accept(x) -> Ok(x)
}
}
fn to(x: Result[i32, u32]) -> Verdict[i32, u32] {
match x {
Ok(x) -> Verdict.Accept(x),
Err(x) -> Verdict.Reject(x),
}
}
fn useless(x: Verdict[i32, u32]) -> Verdict[i32, u32] {
to(from(x))
}
"
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn(Verdict<i32, u32>) -> Verdict<i32, u32>>("useless")
.unwrap();
assert_eq!(f.call(Verdict::Accept(2)), Verdict::Accept(2));
assert_eq!(f.call(Verdict::Reject(4)), Verdict::Reject(4));
}
#[test]
fn variant_with_unused_type_param() {
let s = src!(
r#"
variant Foo[T] { Bar }
fn foo() {
let x = Foo.Bar;
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn()>("foo").unwrap();
f.call();
}
#[test]
fn variant_with_never_type_param() {
let s = src!(
r#"
variant Foo[T] { Bar }
fn foo() {
let x: Foo[!] = Foo.Bar;
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn()>("foo").unwrap();
f.call();
}
#[test]
fn generic_record() {
let s = src!(
"
record Foo[T] {
x: T,
}
fn wrap(x: i32) -> Foo[i32] {
Foo { x: x }
}
fn unwrap(x: Foo[i32]) -> i32 {
x.x
}
fn bar(x: i32) -> i32 {
let y = wrap(x);
unwrap(y)
}
"
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn(i32) -> i32>("bar").unwrap();
let res = f.call(23);
assert_eq!(res, 23);
}
#[test]
fn generic_record_contains_option() {
let s = src!(
"
record Foo[T] {
x: T?,
}
fn wrap(x: i32?) -> Foo[i32] {
Foo { x: x }
}
fn unwrap(x: Foo[i32]) -> i32? {
x.x
}
fn bar(x: i32?) -> i32? {
let y = wrap(x);
unwrap(y)
}
"
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn(Option<i32>) -> Option<i32>>("bar")
.unwrap();
let res = f.call(None);
assert_eq!(res, None);
let res = f.call(Some(20));
assert_eq!(res, Some(20));
}
#[test]
fn generic_record_inferred() {
let s = src!(
"
record Foo[T] {
x: T,
}
fn wrap(x: i32) -> Foo[i32] {
{ x: x } # this is now inferred to be Foo[i32]
}
fn unwrap(x: Foo[i32]) -> i32 {
x.x
}
fn bar(x: i32) -> i32 {
let y = wrap(x);
unwrap(y)
}
"
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn(i32) -> i32>("bar").unwrap();
let res = f.call(23);
assert_eq!(res, 23);
}
#[test]
fn generics_all_the_way_down() {
let s = src!(
"
record Foo[T] {
bar: Bar[T]?
}
record Bar[T] {
inner: T
}
fn main(x: i32) -> i32 {
let foo = Foo {
bar: Some(Bar {
inner: x
}),
};
match foo.bar {
Some(bar) -> bar.inner,
None -> 0,
}
}
"
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn(i32) -> i32>("main").unwrap();
let res = f.call(23);
assert_eq!(res, 23);
}
#[test]
fn char_literal() {
let s = src!(
r#"
fn main() -> char {
'a'
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> char>("main").unwrap();
let res = f.call();
assert_eq!(res, 'a');
}
#[test]
fn stringbuf() {
let s = src!(
r#"
fn quote(s: String) -> String {
let buf = StringBuf.new();
buf.push_char('"');
buf.push_string(s);
buf.push_char('"');
buf.as_string()
}
"#
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn(Arc<str>) -> Arc<str>>("quote")
.unwrap();
let res = f.call("hello".into());
assert_eq!(res, "\"hello\"".into());
}
#[test]
fn list_basic() {
let s = src!(
r#"
fn main() -> i32? {
let list = List.new();
list.push(5);
list.get(0)
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> Option<i32>>("main").unwrap();
let res = f.call();
assert_eq!(res, Some(5));
}
#[test]
fn list_push_twice() {
let s = src!(
r#"
fn main() -> i32? {
let list = List.new();
list.push(5);
list.push(10);
list.push(15);
list.get(0)
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> Option<i32>>("main").unwrap();
let res = f.call();
assert_eq!(res, Some(5));
}
#[test]
fn list_get_many() {
let s = src!(
r#"
fn main() -> i32? {
let list = List.new();
list.push(5);
list.push(10);
list.push(15);
list.push(20);
let a = list.get(0)?
+ list.get(1)?
+ list.get(2)?
+ list.get(3)?;
Some(a)
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> Option<i32>>("main").unwrap();
let res = f.call();
assert_eq!(res, Some(50));
}
#[test]
fn list_len() {
let s = src!(
r#"
fn main() -> u64 {
let list = List.new();
list.push(1);
list.push(2);
list.push(3);
list.len()
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> u64>("main").unwrap();
let res = f.call();
assert_eq!(res, 3);
}
#[test]
fn list_is_empty_1() {
let s = src!(
r#"
fn main() -> bool {
let list = List.new();
list.is_empty()
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> bool>("main").unwrap();
let res = f.call();
assert!(res);
}
#[test]
fn list_is_empty_2() {
let s = src!(
r#"
fn main() -> bool {
let list = List.new();
list.push(5);
list.is_empty()
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> bool>("main").unwrap();
let res = f.call();
assert!(!res);
}
#[test]
fn list_of_strings_1() {
let s = src!(
r#"
fn main() -> String? {
let list = List.new();
list.push("hello");
list.get(0)
}
"#
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn() -> Option<Arc<str>>>("main")
.unwrap();
let res = f.call();
assert_eq!(res, Some("hello".into()));
}
#[test]
fn list_of_strings_2() {
let s = src!(
r#"
fn main() -> String? {
let list = List.new();
list.push("hello");
list.push("world");
Some(f"{list.get(0)?} {list.get(1)?}")
}
"#
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn() -> Option<Arc<str>>>("main")
.unwrap();
let res = f.call();
assert_eq!(res, Some("hello world".into()));
}
#[test]
fn list_through_script() {
let s = src!(
r#"
fn main(x: List[u64]) -> List[u64] {
x.push(3);
x.push(4);
x
}
"#
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn(List<u64>) -> List<u64>>("main")
.unwrap();
let x = List::new();
x.push(1);
x.push(2);
let res = f.call(x);
assert_eq!(res.to_vec(), vec![1, 2, 3, 4]);
}
#[test]
fn list_of_lists() {
let s = src!(
r#"
fn main() -> List[List[u64]] {
let x = List.new();
let a = List.new();
a.push(1);
a.push(2);
let b = List.new();
b.push(3);
b.push(4);
x.push(a);
x.push(b);
x
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> List<List<u64>>>("main").unwrap();
let res = f.call();
assert_eq!(
res.to_vec().iter().map(|l| l.to_vec()).collect::<Vec<_>>(),
vec![vec![1, 2], vec![3, 4]]
);
}
#[test]
fn list_literal() {
let s = src!(
r#"
fn main() -> List[u64] {
[1, 2, 3, 4]
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> List<u64>>("main").unwrap();
let res = f.call();
assert_eq!(res.to_vec(), vec![1, 2, 3, 4]);
}
#[test]
fn list_concat() {
let s = src!(
r#"
fn main() -> List[u64] {
[1, 2, 3, 4].concat([5, 6, 7, 8])
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> List<u64>>("main").unwrap();
let res = f.call();
assert_eq!(res.to_vec(), vec![1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn list_concat_strings() {
let s = src!(
r#"
fn main() -> List[String] {
["a", "b"].concat(["c", "d"])
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> List<Arc<str>>>("main").unwrap();
let res = f.call();
let expected: Vec<Arc<str>> =
["a", "b", "c", "d"].into_iter().map(Into::into).collect();
assert_eq!(res.to_vec(), expected);
}
#[test]
fn list_swap() {
let s = src!(
r#"
fn main() -> List[i32] {
let x = [1, 2, 3, 4];
x.swap(1, 2);
x.swap(0, 3);
x
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> List<i32>>("main").unwrap();
let res = f.call();
let expected: Vec<i32> = vec![4, 3, 2, 1];
assert_eq!(res.to_vec(), expected);
}
#[test]
fn list_plus_strings() {
let s = src!(
r#"
fn main() -> List[String] {
["a", "b"] + ["c", "d"]
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> List<Arc<str>>>("main").unwrap();
let res = f.call();
let expected: Vec<Arc<str>> =
["a", "b", "c", "d"].into_iter().map(Into::into).collect();
assert_eq!(res.to_vec(), expected);
}
#[test]
fn list_of_options() {
let s = src!(
r#"
fn main() -> List[i32?] {
[Some(1), None, Some(2), None]
}
"#
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn() -> List<Option<i32>>>("main")
.unwrap();
let res = f.call();
let expected = vec![Some(1), None, Some(2), None];
assert_eq!(res.to_vec(), expected);
}
#[test]
fn list_of_option_of_strings() {
let s = src!(
r#"
fn main() -> List[String?] {
[Some("hello"), None, Some("bonjour"), None]
}
"#
);
let mut pkg = compile(s);
let f = pkg
.get_function::<fn() -> List<Option<Arc<str>>>>("main")
.unwrap();
let res = f.call();
let expected: Vec<Option<Arc<str>>> =
vec![Some("hello".into()), None, Some("bonjour".into()), None];
assert_eq!(res.to_vec(), expected);
}
#[test]
fn for_loop() {
let s = src!(
r#"
fn main() -> u64 {
let total = 0;
for x in [1, 2, 3, 4] {
total = total + x;
}
total
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> u64>("main").unwrap();
let res = f.call();
assert_eq!(res, 10);
}
#[test]
fn for_loop_strings() {
let s = src!(
r#"
fn main() -> String {
let total = "";
for x in ["a", "b", "c", "d"] {
total = total + x;
}
total
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> Arc<str>>("main").unwrap();
let res = f.call();
assert_eq!(res, "abcd".into());
}
#[test]
fn empty_for_loop() {
let s = src!(
r#"
fn main() -> u64 {
let total = 0;
for x in [] {
total = total + x;
}
total
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> u64>("main").unwrap();
let res = f.call();
assert_eq!(res, 0);
}
#[test]
fn cartesian_for_loop() {
let s = src!(
r#"
fn main() -> u64 {
let total = 0;
for x in [1, 2, 3, 4] {
for y in [1, 2, 3, 4] {
total = total + x * y;
}
}
total
}
"#
);
let mut pkg = compile(s);
let f = pkg.get_function::<fn() -> u64>("main").unwrap();
let res = f.call();
assert_eq!(res, 100);
}