use qcode::context::Context;
use qcode::lower::lower_str;
use qcode::value::insn::segment::instruction_segments;
#[track_caller]
fn check(src: &str) {
let mut ctx = Context::new();
lower_str(&mut ctx, src).unwrap_or_else(|e| panic!("lower failed: {e}\n--- src ---\n{src}"));
let mut checked = 0usize;
for block in ctx.blocks() {
for insn in block.iter() {
let concat: String = instruction_segments(&insn)
.iter()
.map(|t| t.text.as_str())
.collect();
assert_eq!(
concat,
insn.as_statement().to_string(),
"segment concat must equal Display"
);
checked += 1;
}
}
assert!(checked > 0, "no instructions checked for:\n{src}");
}
#[test]
fn int_binops() {
check(
"
<b @x:i32 @y:i32>
%add = @x + @y;
%sub = @x - @y;
%mul = @x * @y;
%and = @x & @y;
%or = @x | @y;
%xor = @x ^ @y;
%shl = @x << @y;
%sshr = @x s>> @y;
%eq = @x == @y;
%slt = @x s< @y;
%sdiv = @x s/ @y;
return @x;
",
);
}
#[test]
fn bool_and_float_binops() {
check(
"
<b @x:i8 @y:i8>
%cmp = @x == @y;
%neg = %cmp == false;
return @x;
",
);
check(
"
<b @x:f64 @y:f64>
%add = @x f+ @y;
%div = @x f/ @y;
%le = @x f<= @y;
return @x;
",
);
}
#[test]
fn unops() {
check(
"
<b @x:i32 @f:f64>
%neg = - @x;
%not = ~ @x;
%abs = abs(@f);
%sqrt = sqrt(@f);
return @x;
",
);
}
#[test]
fn casts_and_range() {
check(
"
<b @x:i32 @f:f32>
%z = zext(i64, @x);
%s = sext(i64, @x);
%i2f = int2float(f64, @x);
%f2f = float2float(f64, @f);
%f2i = trunc(i16, @f);
return @x;
",
);
check(
"
<b @x:i64>
%a = @x[0:4];
%b = @x[4:8];
return @x;
",
);
}
#[test]
fn flags() {
check(
"
<b @x:i32 @y:i32 @f:f64>
%c = carry(@x, @y);
%sc = scarry(@x, @y);
%sb = sborrow(@x, @y);
%pc = popcount(@x);
%lz = lzcount(@x);
%nan = nan(@f);
return @x;
",
);
}
#[test]
fn memory() {
check(
"
<b @p:i64 @v:i32>
%l = load(ram:4, @p);
store(ram:4, @p <- @v);
return @p;
",
);
}
#[test]
fn tuple_and_extract() {
check(
"
<b @x:i32 @y:i64>
%t = pack(lo=@x, hi=@y);
%e = extract(%t.lo);
return @x;
",
);
}
#[test]
fn control_flow() {
check(
"
<entry @c:i8 @p:i64>
if @c goto <t @v=@p> else goto <f>;
<t @v:i64>
goto <f>;
<f>
return @c;
",
);
check(
"
<b @p:i64>
goto [@p];
",
);
check(
"
<b @v:i64 @p:i64>
return @v at @p;
",
);
}
#[test]
fn switch_terminator() {
check(
"
<entry @i:i64 @p:i64>
switch @i { 0x0 => <a>, 0x3 => <b @v=@p>, default => <d> };
<a>
return @i;
<b @v:i64>
return @v;
<d>
return @i;
",
);
check(
"
<entry @i:i64>
switch @i { 0x0 => <a>, 0x1 => <b> };
<a>
return @i;
<b>
return @i;
",
);
}
#[test]
fn assert_stmt() {
check(
"
<b @c:i8>
assert @c;
return @c;
",
);
}
#[test]
fn intrinsic() {
check(
"
<b @x:i32 @k:i8>
%r = $rol(@x, @k);
return @x;
",
);
}
#[test]
fn apply_and_map() {
check(
"
lambda inc:
<b @x:i64>
%r = @x + 1;
return %r;
fn main:
<e @y:i64>
%v = apply inc(@y);
return %v;
",
);
check(
"
lambda body:
<b @x:i32>
return @x;
fn main:
<e @a:i64>
%r = body <$> @a;
return @a;
",
);
}
#[test]
fn calls() {
check(
"
fn callee:
<c @a:i32 @b:i32>
return @a;
fn main:
<e @x:i32 @y:i32>
call fn callee(@a=@x, @b=@y);
",
);
check(
"
<b @p:i64 @x:i32 @y:i32>
call [@p](@x, @y);
",
);
}