#![allow(clippy::needless_pass_by_value)]
use super::*;
use crate::sname;
use assert_approx_eq::assert_approx_eq;
use test_log::test;
trait Assert<'a, P = ()> {
fn assert(&self, m: &Machine<'a, P>);
}
macro_rules! ValueEqAssert {
($t:ty) => {
impl<'a, P> Assert<'a, P> for $t {
fn assert(&self, m: &Machine<'a, P>) {
assert_eq!(m.stack.len(), 1);
assert_eq!(m.stack[0], self.clone().into());
}
}
};
}
ValueEqAssert!(bool);
ValueEqAssert!(i32);
ValueEqAssert!(Name);
ValueEqAssert!(RuntimeDictionary<'a, P>);
ValueEqAssert!(Array);
impl<'a, const N: usize, P> Assert<'a, P> for [u8; N] {
fn assert(&self, m: &Machine<'a, P>) {
assert_eq!(m.stack.len(), 1);
assert_eq!(m.stack[0], (*self).into());
}
}
impl<'a, P> Assert<'a, P> for f32 {
fn assert(&self, m: &Machine<'a, P>) {
assert_eq!(m.stack.len(), 1);
assert_approx_eq!(m.stack[0].real().unwrap(), *self);
}
}
impl<'a, P> Assert<'a, P> for Vec<Box<dyn Assert<'a, P>>> {
fn assert(&self, m: &Machine<'a, P>) {
for a in self {
a.assert(m);
}
}
}
struct Stack<'a, P>(Vec<RuntimeValue<'a, P>>);
impl<'a, P> Assert<'a, P> for Stack<'a, P> {
fn assert(&self, m: &Machine<'a, P>) {
assert_eq!(m.stack.len(), self.0.len());
for (i, v) in self.0.iter().enumerate() {
assert_eq!(m.stack[i], v.clone());
}
}
}
macro_rules! asserts {
($($e:expr),*) => {
{
let v: Vec<Box<dyn Assert<'_>>> = vec![$(Box::new($e)),*];
v
}
}
}
#[derive(Clone)]
struct VariableStack<'a, P>(RuntimeDictionary<'a, P>);
impl<'a, P> Assert<'a, P> for VariableStack<'a, P> {
fn assert(&self, m: &Machine<'a, P>) {
assert_eq!(&*m.variable_stack.top().unwrap().borrow(), &self.0);
}
}
fn assert_op<'a>(s: &'a str, exp_result: impl Assert<'a>) {
let mut machine = Machine::new(s.as_bytes());
machine.execute().unwrap();
exp_result.assert(&machine);
}
#[test]
fn test_dict() {
assert_op("10 dict", RuntimeDictionary::new());
}
#[test]
fn test_begin() {
assert_op("0 0 dict begin", asserts![
0,
VariableStack(RuntimeDictionary::new())
]);
}
#[test]
fn test_dup() {
assert_op("2 dup", Stack(rt_values![2, 2]));
}
#[test]
fn copy() {
assert_op(
"1 2 3 4 5 3 copy",
Stack(rt_values![1, 2, 3, 4, 5, 3, 4, 5]),
);
}
#[test]
fn count() {
assert_op("10 2 count", Stack(rt_values![10, 2, 2]));
}
#[test]
fn test_def() {
assert_op(
"10 dict begin /foo 10 def currentdict",
dict![sname("foo") => 10],
);
}
#[test]
fn test_end() {
assert_op(
"0 dict begin 1 dict begin /foo 10 def end currentdict",
RuntimeDictionary::new(),
);
}
#[test]
fn and() {
assert_op("true true and", true);
assert_op("true false and", false);
assert_op("false false and", false);
assert_op("99 1 and", 1);
assert_op("52 7 and", 4);
}
#[test]
fn or() {
assert_op("true true or", true);
assert_op("true false or", true);
assert_op("false false or", false);
assert_op("17 5 or", 21);
}
#[test]
fn not() {
assert_op("true not", false);
assert_op("false not", true);
assert_op("52 not", -53);
}
#[test]
fn xor() {
assert_op("true true xor", false);
assert_op("true false xor", true);
assert_op("false false xor", false);
assert_op("7 3 xor", 4);
assert_op("12 3 xor", 15);
}
#[test]
fn eq() {
assert_op("4 4 eq", true);
assert_op("4.0 4 eq", true);
assert_op("4 4.0 eq", true);
assert_op("(abc) (abc) eq", true);
assert_op("(abc) /abc eq", true);
assert_op("/abc (abc) eq", true);
assert_op("[1 2 3] dup eq", true);
assert_op("[1 2 3] [1 2 3] eq", false);
assert_op("{} dup eq", true);
assert_op("{} {} eq", false);
assert_op("1 dict 1 dict eq", false);
}
#[test]
fn ne() {
assert_op("4 4 ne", false);
assert_op("4.0 4 ne", false);
assert_op("4 4.0 ne", false);
assert_op("(abc) (abc) ne", false);
assert_op("(abc) /abc ne", false);
assert_op("/abc (abc) ne", false);
assert_op("[1 2 3] dup ne", false);
assert_op("[1 2 3] [1 2 3] ne", true);
assert_op("{} dup ne", false);
assert_op("{} {} ne", true);
assert_op("1 dict 1 dict ne", true);
}
#[test]
fn le() {
assert_op("4 4 le", true);
assert_op("3 4 le", true);
assert_op("5 4 le", false);
assert_op("4.0 4.0 le", true);
assert_op("3.0 4.0 le", true);
assert_op("5.0 4.0 le", false);
assert_op("4.0 4 le", true);
assert_op("3.0 4 le", true);
assert_op("5.0 4 le", false);
assert_op("4 4.0 le", true);
assert_op("3 4.0 le", true);
assert_op("5 4.0 le", false);
assert_op("(4) (4) le", true);
assert_op("(3) (4) le", true);
assert_op("(5) (4) le", false);
assert_op("(4) (40) le", true);
assert_op("(40) (4) le", false);
}
#[test]
fn lt() {
assert_op("4 4 lt", false);
assert_op("3 4 lt", true);
assert_op("5 4 lt", false);
assert_op("4.0 4.0 lt", false);
assert_op("3.0 4.0 lt", true);
assert_op("5.0 4.0 lt", false);
assert_op("4.0 4 lt", false);
assert_op("3.0 4 lt", true);
assert_op("5.0 4 lt", false);
assert_op("4 4.0 lt", false);
assert_op("3 4.0 lt", true);
assert_op("5 4.0 lt", false);
assert_op("(4) (4) lt", false);
assert_op("(3) (4) lt", true);
assert_op("(5) (4) lt", false);
assert_op("(4) (40) lt", true);
assert_op("(40) (4) lt", false);
}
#[test]
fn ge() {
assert_op("4 4 ge", true);
assert_op("3 4 ge", false);
assert_op("5 4 ge", true);
assert_op("4.0 4.0 ge", true);
assert_op("3.0 4.0 ge", false);
assert_op("5.0 4.0 ge", true);
assert_op("4.0 4 ge", true);
assert_op("3.0 4 ge", false);
assert_op("5.0 4 ge", true);
assert_op("4 4.0 ge", true);
assert_op("3 4.0 ge", false);
assert_op("5 4.0 ge", true);
assert_op("(4) (4) ge", true);
assert_op("(3) (4) ge", false);
assert_op("(5) (4) ge", true);
assert_op("(4) (40) ge", false);
assert_op("(40) (4) ge", true);
}
#[test]
fn gt() {
assert_op("4 4 gt", false);
assert_op("3 4 gt", false);
assert_op("5 4 gt", true);
assert_op("4.0 4.0 gt", false);
assert_op("3.0 4.0 gt", false);
assert_op("5.0 4.0 gt", true);
assert_op("4.0 4 gt", false);
assert_op("3.0 4 gt", false);
assert_op("5.0 4 gt", true);
assert_op("4 4.0 gt", false);
assert_op("3 4.0 gt", false);
assert_op("5 4.0 gt", true);
assert_op("(4) (4) gt", false);
assert_op("(3) (4) gt", false);
assert_op("(5) (4) gt", true);
assert_op("(4) (40) gt", false);
assert_op("(40) (4) gt", true);
}
#[test]
fn test_array() {
assert_op("2 array", values![Value::Null, Value::Null]);
}
#[test]
fn test_index() {
assert_op("1 2 3 4 5 3 index", Stack(rt_values![1, 2, 3, 4, 5, 2]));
}
#[test]
fn test_exch() {
assert_op("3 4 5 exch", Stack(rt_values![3, 5, 4]));
}
#[test]
fn test_put() {
assert_op(
"10 dict begin /foo 10 def currentdict /foo 20 put currentdict",
dict![sname("foo") => 20],
);
assert_op("2 array dup 1 10 put", values![Value::Null, 10]);
}
#[test]
fn get() {
assert_op("2 array dup 1 10 put 1 get", 10);
assert_op("10 dict begin /foo 10 def currentdict /foo get", 10);
assert_op("3 string dup 0 65 put 0 get", 65);
assert_op("{1 2 3} 1 get", 2);
}
#[test]
fn test_for() {
assert_op("0 1 1 10 {add} for", 55);
}
#[test]
fn test_if() {
assert_op("true {1} if", 1);
assert_op("2 false {1} if", 2);
}
#[test]
fn ifelse() {
assert_op("true {1} {2} ifelse", 1);
assert_op("false {1} {2} ifelse", 2);
}
#[test]
fn test_cleartomark() {
assert_op("1 2 mark 3 4 5 cleartomark", Stack(rt_values![1, 2]));
}
#[test]
fn test_create_array_on_stack() {
assert_op("[ 1 2 3 4 5 ]", values![1, 2, 3, 4, 5]);
}
#[test]
fn test_create_dict_on_stack() {
assert_op("<<>>", dict![]);
assert_op(
"<< /foo 10/bar<<>> >>",
dict![sname("foo") => 10, sname("bar") => dict![]],
);
}
#[test]
fn test_string() {
assert_op("3 string", *b"\0\0\0");
}
#[test]
fn array_literal() {
assert_op("[]", values![]);
}
#[test]
fn known() {
assert_op("1 dict /foo known", false);
assert_op("1 dict begin /foo 10 def currentdict end /foo known", true);
}
#[test]
fn execute_on_file() {
let data = include_bytes!("./cmsy9.pfb");
let mut machine = Machine::new(data);
match machine.execute() {
Ok(_) => {}
Err(e) => {
println!("{}:\n{:?}", e, machine.stack);
panic!();
}
}
}
#[test]
fn sub() {
assert_op("1 2 sub", -1);
assert_op("1.0 2.0 sub", -1.0);
assert_op("1.0 2 sub", -1.0);
assert_op("1 2.0 sub", -1.0);
}
#[test]
fn abs() {
assert_op("1 abs", 1);
assert_op("-1 abs", 1);
assert_op("-1.0 abs", 1.0);
}
#[test]
fn idiv() {
assert_op("10 3 idiv", 3);
assert_op("10 -3 idiv", -3);
assert_op("-10 3 idiv", -3);
assert_op("-10 -3 idiv", 3);
}
#[test]
fn div() {
assert_op("10 3 div", 3.3333333);
assert_op("10.0 -3 div", -3.3333333);
assert_op("-10 3.0 div", -3.3333333);
assert_op("-10.0 -3.0 div", 3.3333333);
assert_op("9 3 div", 3.0);
}
#[test]
fn test_mod() {
assert_op("10 3 mod", 1);
assert_op("-5 3 mod", -2);
}
#[test]
fn mul() {
assert_op("10 3 mul", 30);
assert_op("-5.0 3.0 mul", -15.0);
assert_op("-5.0 -3 mul", 15.0);
assert_op("-5 -3.0 mul", 15.0);
}
#[test]
fn neg() {
assert_op("10 neg", -10);
assert_op("-5.0 neg", 5.0);
}
#[test]
fn ceiling() {
assert_op("3.2 ceiling", 4.0);
assert_op("-4.8 ceiling", -4.0);
assert_op("99 ceiling", 99);
}
#[test]
fn floor() {
assert_op("3.2 floor", 3.0);
assert_op("-4.8 floor", -5.0);
assert_op("99 floor", 99);
}
#[test]
fn round() {
assert_op("3.2 round", 3.0);
assert_op("-4.8 round", -5.0);
assert_op("99 round", 99);
}
#[test]
fn truncate() {
assert_op("3.2 truncate", 3.0);
assert_op("-4.8 truncate", -4.0);
assert_op("99 truncate", 99);
}
#[test]
fn sqrt() {
assert_op("4 sqrt", 2.0);
assert_op("4.0 sqrt", 2.0);
assert_op("99 sqrt", 9.949874);
}
#[test]
fn atan() {
assert_op("0.0 1.0 atan", 0.0);
assert_op("1 0 atan", 90.0);
assert_op("-100.0 0 atan", 270.0);
assert_op("4 4.0 atan", 45.0);
}
#[test]
fn cos() {
assert_op("0 cos", 1.0);
assert_op("90.0 cos", 0.0);
assert_op("180 cos", -1.0);
assert_op("270.0 cos", 0.0);
}
#[test]
fn sin() {
assert_op("0 sin", 0.0);
assert_op("90.0 sin", 1.0);
assert_op("180 sin", 0.0);
assert_op("270.0 sin", -1.0);
}
#[test]
fn exp() {
assert_op("9 0.5 exp", 3.0);
assert_op("9.0 -1 exp", 0.111111);
}
#[test]
fn ln() {
assert_op("1 ln", 0.0);
assert_op("2.0 ln", std::f32::consts::LN_2);
assert_op("10 ln", std::f32::consts::LN_10);
}
#[test]
fn log() {
assert_op("10 log", 1.0);
assert_op("100.0 log", 2.0);
}
#[test]
fn cvi() {
assert_op("10 cvi", 10);
assert_op("-10.1234 cvi", -10);
assert_op("(10.45) cvi", 10);
}
#[test]
fn cvr() {
assert_op("10 cvr", 10.0);
assert_op("-10.1234 cvr", -10.1234);
assert_op("(10.45) cvr", 10.45);
}
#[test]
fn bitshit() {
assert_op("7 3 bitshift", 56);
assert_op("142 -3 bitshift", 17);
}
#[test]
fn roll() {
assert_op("1 2 3 3 -1 roll", Stack(rt_values![2, 3, 1]));
assert_op("1 2 3 3 1 roll", Stack(rt_values![3, 1, 2]));
assert_op("1 2 3 3 0 roll", Stack(rt_values![1, 2, 3]));
}