Skip to content

Commit e28a292

Browse files
committed
Implement more math filters
1 parent 23187ab commit e28a292

File tree

5 files changed

+239
-2
lines changed

5 files changed

+239
-2
lines changed

src/intrinsic/math.rs

+74-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,48 @@ macro_rules! as_math_fn {
1717
};
1818
}
1919

20+
macro_rules! as_math_fn2 {
21+
($name: ident) => {
22+
crate::vm::bytecode::NamedFn2 {
23+
name: stringify!($name),
24+
func: |_: Value, v: Value, w: Value| match (v, w) {
25+
(crate::Value::Number(v), crate::Value::Number(w)) => {
26+
crate::intrinsic::math::$name(v, w).map(Into::into)
27+
}
28+
(v, crate::Value::Number(_)) => crate::vm::Result::Err(
29+
crate::vm::QueryExecutionError::InvalidArgType(stringify!($name), v),
30+
),
31+
(_, w) => crate::vm::Result::Err(crate::vm::QueryExecutionError::InvalidArgType(
32+
stringify!($name),
33+
w,
34+
)),
35+
},
36+
}
37+
};
38+
}
39+
40+
macro_rules! as_math_fn3 {
41+
($name: ident) => {
42+
crate::vm::bytecode::NamedFn3 {
43+
name: stringify!($name),
44+
func: |_: Value, v: Value, w: Value, x: Value| match (v, w, x) {
45+
(crate::Value::Number(v), crate::Value::Number(w), crate::Value::Number(x)) => {
46+
crate::intrinsic::math::$name(v, w, x).map(Into::into)
47+
}
48+
(v, crate::Value::Number(_), crate::Value::Number(_)) => crate::vm::Result::Err(
49+
crate::vm::QueryExecutionError::InvalidArgType(stringify!($name), v),
50+
),
51+
(_, w, crate::Value::Number(_)) => crate::vm::Result::Err(
52+
crate::vm::QueryExecutionError::InvalidArgType(stringify!($name), w),
53+
),
54+
(_, _, x) => crate::vm::Result::Err(
55+
crate::vm::QueryExecutionError::InvalidArgType(stringify!($name), x),
56+
),
57+
},
58+
}
59+
};
60+
}
61+
2062
pub(crate) fn nan(_: Value) -> Result<Value> {
2163
Ok(Number::nan().into())
2264
}
@@ -37,6 +79,10 @@ pub(crate) fn is_infinite(v: Number) -> Result<bool> {
3779
Ok(v.is_infinite())
3880
}
3981

82+
pub(crate) fn exp10(v: Number) -> Result<Number> {
83+
Ok(Number::from(10).powf(v))
84+
}
85+
4086
macro_rules! pub_math_fn {
4187
($($name: ident),*) => {
4288
$(
@@ -47,4 +93,31 @@ macro_rules! pub_math_fn {
4793
};
4894
}
4995

50-
pub_math_fn!(floor, sqrt, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh);
96+
pub_math_fn!(
97+
floor, round, ceil, trunc, abs, sqrt, cbrt, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh,
98+
asinh, acosh, atanh, exp, exp2, exp_m1, ln, log2, log10
99+
);
100+
101+
pub(crate) fn fmax(v: Number, w: Number) -> Result<Number> {
102+
Ok(Float::max(v, w))
103+
}
104+
105+
pub(crate) fn fmin(v: Number, w: Number) -> Result<Number> {
106+
Ok(Float::min(v, w))
107+
}
108+
109+
macro_rules! pub_math_fn2 {
110+
($($name: ident),*) => {
111+
$(
112+
pub(crate) fn $name(v: Number, w: Number) -> Result<Number> {
113+
Ok(v.$name(w))
114+
}
115+
)*
116+
};
117+
}
118+
119+
pub_math_fn2!(copysign, atan2, hypot, powf);
120+
121+
pub(crate) fn fma(v: Number, w: Number, x: Number) -> Result<Number> {
122+
Ok(v.mul_add(w, x))
123+
}

src/intrinsic/mod.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
compile::compiler::{ArgType, FunctionIdentifier},
1717
util::make_owned,
1818
vm::{
19-
bytecode::{NamedFn0, NamedFn1, NamedFn2},
19+
bytecode::{NamedFn0, NamedFn1, NamedFn2, NamedFn3},
2020
error::Result,
2121
ByteCode, QueryExecutionError,
2222
},
@@ -62,7 +62,12 @@ static INTRINSICS0: phf::Map<&'static str, NamedFn0> = phf_map! {
6262
"isnormal" => as_math_fn!(is_normal),
6363
"isinfinite" => as_math_fn!(is_infinite),
6464
"floor" => as_math_fn!(floor),
65+
"round" => as_math_fn!(round),
66+
"ceil" => as_math_fn!(ceil),
67+
"trunc" => as_math_fn!(trunc),
68+
"fabs" => as_math_fn!(abs),
6569
"sqrt" => as_math_fn!(sqrt),
70+
"cbrt" => as_math_fn!(cbrt),
6671
"sin" => as_math_fn!(sin),
6772
"cos" => as_math_fn!(cos),
6873
"tan" => as_math_fn!(tan),
@@ -75,6 +80,13 @@ static INTRINSICS0: phf::Map<&'static str, NamedFn0> = phf_map! {
7580
"asinh" => as_math_fn!(asinh),
7681
"acosh" => as_math_fn!(acosh),
7782
"atanh" => as_math_fn!(atanh),
83+
"exp" => as_math_fn!(exp),
84+
"exp2" => as_math_fn!(exp2),
85+
"exp10" => as_math_fn!(exp10),
86+
"expm1" => as_math_fn!(exp_m1),
87+
"log" => as_math_fn!(ln),
88+
"log2" => as_math_fn!(log2),
89+
"log10" => as_math_fn!(log10),
7890
};
7991
static INTRINSICS1: phf::Map<&'static str, NamedFn1> = phf_map! {
8092
"error" => NamedFn1 { name: "error", func: error1 },
@@ -101,6 +113,16 @@ static INTRINSICS1: phf::Map<&'static str, NamedFn1> = phf_map! {
101113
static INTRINSICS2: phf::Map<&'static str, NamedFn2> = phf_map! {
102114
"setpath" => NamedFn2 { name: "setpath", func: path::set_path },
103115
"__split_match_impl" => NamedFn2 { name: "__split_match_impl", func: regex::split_match_impl },
116+
117+
"fmax" => as_math_fn2!(fmax),
118+
"fmin" => as_math_fn2!(fmin),
119+
"copysign" => as_math_fn2!(copysign),
120+
"atan2" => as_math_fn2!(atan2),
121+
"hypot" => as_math_fn2!(hypot),
122+
"pow" => as_math_fn2!(powf),
123+
};
124+
static INTRINSICS3: phf::Map<&'static str, NamedFn3> = phf_map! {
125+
"fma" => as_math_fn3!(fma),
104126
};
105127

106128
pub(crate) fn lookup_intrinsic_fn(
@@ -123,6 +145,13 @@ pub(crate) fn lookup_intrinsic_fn(
123145
vec![ArgType::Value, ArgType::Value],
124146
)
125147
})
148+
} else if *n_args == 3 {
149+
INTRINSICS3.get(&ident.0).cloned().map(|f| {
150+
(
151+
ByteCode::Intrinsic3(f),
152+
vec![ArgType::Value, ArgType::Value, ArgType::Value],
153+
)
154+
})
126155
} else {
127156
None
128157
}

src/vm/bytecode.rs

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) struct ClosureAddress(pub(crate) Address);
1717
pub type NamedFn0 = NamedFunction<fn(Value) -> Result<Value>>;
1818
pub type NamedFn1 = NamedFunction<fn(Value, Value) -> Result<Value>>;
1919
pub type NamedFn2 = NamedFunction<fn(Value, Value, Value) -> Result<Value>>;
20+
pub type NamedFn3 = NamedFunction<fn(Value, Value, Value, Value) -> Result<Value>>;
2021

2122
impl<F: Clone> Debug for NamedFunction<F> {
2223
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
@@ -189,6 +190,12 @@ pub(crate) enum ByteCode {
189190
/// # Panics
190191
/// Panics if the stack had less than 3 elements, or the invoked function panicked.
191192
Intrinsic2(NamedFn2),
193+
/// Pops a value `arg3` from the stack, pops another value `arg2` from the stack, pops another value `arg1` from the stack,
194+
/// and another value `context` from the stack, and invokes the function with the arg `context, arg1, arg2, arg3`,
195+
/// and pushes the resulting value to the stack.
196+
/// # Panics
197+
/// Panics if the stack had less than 4 elements, or the invoked function panicked.
198+
Intrinsic3(NamedFn3),
192199
}
193200

194201
#[derive(Debug, Clone)]

src/vm/machine.rs

+18
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,24 @@ fn run_code(
971971
Err(e) => err = Some(e),
972972
}
973973
}
974+
Intrinsic3(NamedFunction { name, func }) => {
975+
let arg3 = state.pop();
976+
let arg2 = state.pop();
977+
let arg1 = state.pop();
978+
let context = state.pop();
979+
log::trace!(
980+
"Calling function {} with context {:?} and arg1 {:?} and arg2 {:?} and arg3 {:?}",
981+
name,
982+
context,
983+
arg1,
984+
arg2,
985+
arg3
986+
);
987+
match func(context, arg1, arg2, arg3) {
988+
Ok(value) => state.push(value),
989+
Err(e) => err = Some(e),
990+
}
991+
}
974992
}
975993
state.pc.next();
976994
}

tests/hand_written/math.rs

+110
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,37 @@ test!(
1616
"#
1717
);
1818

19+
test!(
20+
floor_round_ceil_trunc_fabs_functions,
21+
r#"
22+
map(floor), map(round), map(ceil), map(trunc), map(fabs)
23+
"#,
24+
r#"
25+
[-3.5,-2.1,-1,-0.3,0,0.3,0.7,1,1.2,2.8,3.5]
26+
"#,
27+
r#"
28+
[-4,-3,-1,-1,0,0,0,1,1,2,3]
29+
[-4,-2,-1,-0,0,0,1,1,1,3,4]
30+
[-3,-2,-1,-0,0,1,1,1,2,3,4]
31+
[-3,-2,-1,-0,0,0,0,1,1,2,3]
32+
[3.5,2.1,1,0.3,0,0.3,0.7,1,1.2,2.8,3.5]
33+
"#
34+
);
35+
36+
test!(
37+
sqrt_cbrt_functions,
38+
r#"
39+
map(sqrt | select(isnan | not)), map(cbrt) | map(. * 10000 | round)
40+
"#,
41+
r#"
42+
[-4,-3.375,0,0.125,1,8,9,16.777216]
43+
"#,
44+
r#"
45+
[0,3536,10000,28284,30000,40960]
46+
[-15874,-15000,0,5000,10000,20000,20801,25600]
47+
"#
48+
);
49+
1950
test!(
2051
trigonometric_functions,
2152
r#"
@@ -75,3 +106,82 @@ test!(
75106
[0,0.346573590,0.804718956]
76107
"#
77108
);
109+
110+
test!(
111+
exponential_functions,
112+
r#"
113+
map(exp), map(exp2), map(exp10), map(expm1) | map(. * 1000000000 | floor / 1000000000)
114+
"#,
115+
r#"
116+
[-1,-0.5,0,0.5,1,1.5,2]
117+
"#,
118+
r#"
119+
[0.367879441,0.606530659,1,1.64872127,2.718281828,4.48168907,7.389056098]
120+
[0.5,0.707106781,1,1.414213562,2,2.828427124,4]
121+
[0.1,0.316227766,1,3.16227766,10,31.622776601,100]
122+
[-0.632120559,-0.393469341,0,0.64872127,1.718281828,3.48168907,6.389056098]
123+
"#
124+
);
125+
126+
test!(
127+
logarithmic_functions,
128+
r#"
129+
map(log), map(log2), map(log10) | map(. * 1000000000 | floor / 1000000000)
130+
"#,
131+
r#"
132+
[0.1,0.5,1,2,3,10]
133+
"#,
134+
r#"
135+
[-2.302585093,-0.693147181,0,0.69314718,1.098612288,2.302585092]
136+
[-3.321928095,-1,0,1,1.5849625,3.321928094]
137+
[-1,-0.301029996,0,0.301029995,0.477121254,1]
138+
"#
139+
);
140+
141+
test!(
142+
fmax_fmin_copysign_functions,
143+
r#"
144+
[fmax(0.1,0.3; 0.2,0.4)],
145+
[fmin(0.1,0.3; 0.2,0.4)],
146+
[copysign(0.1,-0.3; 0.2,-0.4)]
147+
"#,
148+
r#"
149+
null
150+
"#,
151+
r#"
152+
[0.2,0.4,0.3,0.4]
153+
[0.1,0.1,0.2,0.3]
154+
[0.1,-0.1,0.3,-0.3]
155+
"#
156+
);
157+
158+
test!(
159+
atan2_hypot_powf_functions,
160+
r#"
161+
[atan2(0.1,0.3; 0.2,0.4)],
162+
[hypot(0.1,0.3; 0.2,0.4)],
163+
[pow(0.1,0.3; 0.2,0.4)] |
164+
map(. * 1000000000 | floor / 1000000000)
165+
"#,
166+
r#"
167+
null
168+
"#,
169+
r#"
170+
[0.463647609,0.244978663,0.982793723,0.643501108]
171+
[0.223606797,0.412310562,0.360555127,0.5]
172+
[0.630957344,0.39810717,0.786003085,0.61780085]
173+
"#
174+
);
175+
176+
test!(
177+
fma_function,
178+
r#"
179+
[fma(1.5,2.5; 3.5,4.5; 5.25,6.75)]
180+
"#,
181+
r#"
182+
null
183+
"#,
184+
r#"
185+
[10.5,12,12,13.5,14,15.5,16.5,18]
186+
"#
187+
);

0 commit comments

Comments
 (0)