diff --git a/.gitignore b/.gitignore index 94408df6..c1e82cfd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,5 @@ *.exe # Generated by Cargo -/target/ +target/ Cargo.lock diff --git a/decimal-macros/Cargo.toml b/decimal-macros/Cargo.toml new file mode 100644 index 00000000..94cb0abb --- /dev/null +++ b/decimal-macros/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "decimal-macros" +version = "0.1.0" +authors = ["Callum Tolley "] + +[lib] +name = "decimal_macros" +plugin = true + +[dependencies] +decimal = { path = "../" } +libc = "~0.2" + +[build-dependencies] +gcc = "~0.3" diff --git a/decimal-macros/examples/test.rs b/decimal-macros/examples/test.rs new file mode 100644 index 00000000..2430e8be --- /dev/null +++ b/decimal-macros/examples/test.rs @@ -0,0 +1,19 @@ +#![feature(plugin)] +#![plugin(decimal_macros)] +extern crate decimal; + +fn main() { + let a = d128!(0.1); + let b = d128!(0.2); + let c = d128!(0.3); + let res = a + b; + let eq = res == c; + if eq { + println!("{} + {} = {}", a, b, res); + } else { + println!("{} + {} = {} (expected {})", a, b, res, c); + } + assert!(eq); + + assert_eq!(d128!(0.1), d128!(1) / d128!(10)); +} diff --git a/decimal-macros/src/lib.rs b/decimal-macros/src/lib.rs new file mode 100644 index 00000000..81f3d2a5 --- /dev/null +++ b/decimal-macros/src/lib.rs @@ -0,0 +1,91 @@ +#![feature(plugin_registrar, rustc_private)] + +extern crate libc; +extern crate decimal; + +extern crate rustc_plugin; +extern crate syntax; + +use rustc_plugin::Registry; +use syntax::ext::base::{DummyResult, MacEager, ExtCtxt, MacResult}; +use syntax::ext::build::AstBuilder; +use syntax::ext::source_util; +use syntax::codemap::Span; +use syntax::ast::{ExprKind, TokenTree, LitKind, StrStyle}; + +use decimal::d128; + +fn d128_lit<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box { + // Turn input into a string literal + // e.g. d128!(0.1) -> "0.1", d128!(NaN) -> "NaN" + let mac_res = source_util::expand_stringify(cx, sp, tts); + + let ex = match mac_res.make_expr() { + Some(ex) => ex, + None => { + // I don't know when this occurs + cx.span_err(sp, "one argument needed"); + return DummyResult::any(sp); + } + }; + + match &ex.node { + &ExprKind::Lit(ref lit) => match &lit.node { + &LitKind::Str(ref s, StrStyle::Cooked) => { + // Check for empty argument + if (*s).len() == 0 { + cx.span_err(sp, "one argument needed"); + return DummyResult::any(sp); + } + let num = match from_str(&*s) { + Ok(num) => num, + Err(s) => { + cx.span_err(lit.span, s); + return DummyResult::any(sp); + } + }; + let num = unsafe { ::std::mem::transmute::(num) }; + + // Create array literal + let mut vec = Vec::new(); + for i in 0..16 { + vec.push(cx.expr_u8(lit.span, num[i])); + } + let vec = cx.expr_vec(lit.span, vec); + let ids = vec![cx.ident_of("decimal"), cx.ident_of("d128"), cx.ident_of("from_bytes")]; + let ex = cx.expr_call_global(lit.span, ids, vec![vec]); + + return MacEager::expr(ex); + }, + _ => {} + }, + _ => {} + } + // This should never happen. + cx.span_err(sp, "not a valid d128 number"); + DummyResult::any(sp) +} + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_macro("d128", d128_lit) +} + +fn from_str(s: &str) -> Result { + use std::str::FromStr; + d128::set_status(decimal::Status::empty()); + let res = d128::from_str(s); + + let status = d128::get_status(); + if status.contains(decimal::CONVERSION_SYNTAX) { + Err("not a valid d128 number") + } else if status.contains(decimal::OVERFLOW) { + Err("too large for a d128 number") + } else if status.contains(decimal::UNDERFLOW) { + Err("too small for a d128 number") + } else if !status.is_empty() { + Err("not a valid d128 number") + } else { + Ok(res.unwrap()) + } +} diff --git a/src/dec128.rs b/src/dec128.rs index 946e9a36..51e63b15 100644 --- a/src/dec128.rs +++ b/src/dec128.rs @@ -436,6 +436,15 @@ impl d128 { } } } + + /// Hidden function for initializing a d128 from an array of bytes. When using this function + /// you should be careful about endianness issues. + #[doc(hidden)] + pub fn from_bytes(bytes: [u8; 16]) -> d128 { + d128 { + bytes: unsafe { ::std::mem::transmute(bytes) } + } + } // Utilities and conversions, extractors, etc.