Skip to content

Commit

Permalink
Implement hexadecimal floating point literals via a syntax extension
Browse files Browse the repository at this point in the history
closes #1433
  • Loading branch information
rcxdude committed Mar 10, 2014
1 parent 62f1d68 commit a38e148
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 1 deletion.
3 changes: 2 additions & 1 deletion mk/crates.mk
Expand Up @@ -51,7 +51,7 @@

TARGET_CRATES := std extra green rustuv native flate arena glob term semver \
uuid serialize sync getopts collections num test time
HOST_CRATES := syntax rustc rustdoc fourcc
HOST_CRATES := syntax rustc rustdoc fourcc hexfloat
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
TOOLS := compiletest rustdoc rustc

Expand All @@ -76,6 +76,7 @@ DEPS_sync := std
DEPS_getopts := std
DEPS_collections := std
DEPS_fourcc := syntax std
DEPS_hexfloat := syntax std
DEPS_num := std
DEPS_test := std extra collections getopts serialize term
DEPS_time := std serialize
Expand Down
178 changes: 178 additions & 0 deletions src/libhexfloat/lib.rs
@@ -0,0 +1,178 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to create floating point literals from hexadecimal strings
Once loaded, hexfloat!() is called with a string containing the hexadecimal
floating-point literal, and an optional type (f32 or f64).
If the type is omitted, the literal is treated the same as a normal unsuffixed
literal.
# Examples
To load the extension and use it:
```rust,ignore
#[phase(syntax)]
extern crate hexfloat;
fn main() {
let val = hexfloat!("0x1.ffffb4", f32);
}
```
# References
* [ExploringBinary: hexadecimal floating point constants]
(http://www.exploringbinary.com/hexadecimal-floating-point-constants/)
*/

#[crate_id = "hexfloat#0.10-pre"];
#[crate_type = "rlib"];
#[crate_type = "dylib"];
#[license = "MIT/ASL2"];

#[feature(macro_registrar, managed_boxes)];

extern crate syntax;

use syntax::ast;
use syntax::ast::Name;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse;
use syntax::parse::token;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
register(token::intern("hexfloat"),
NormalTT(~BasicMacroExpander {
expander: expand_syntax_ext,
span: None,
},
None));
}

//Check if the literal is valid (as LLVM expects),
//and return a descriptive error if not.
fn hex_float_lit_err(s: &str) -> Option<(uint, ~str)> {
let mut chars = s.chars().peekable();
let mut i = 0;
if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
if chars.next() != Some('0') { return Some((i, ~"Expected '0'")); } i+=1;
if chars.next() != Some('x') { return Some((i, ~"Expected 'x'")); } i+=1;
let mut d_len = 0;
for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; d_len += 1;}
if chars.next() != Some('.') { return Some((i, ~"Expected '.'")); } i+=1;
let mut f_len = 0;
for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; f_len += 1;}
if d_len == 0 && f_len == 0 {
return Some((i, ~"Expected digits before or after decimal point"));
}
if chars.next() != Some('p') { return Some((i, ~"Expected 'p'")); } i+=1;
if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
let mut e_len = 0;
for _ in chars.take_while(|c| c.is_digit()) { chars.next(); i+=1; e_len += 1}
if e_len == 0 {
return Some((i, ~"Expected exponent digits"));
}
match chars.next() {
None => None,
Some(_) => Some((i, ~"Expected end of string"))
}
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
let (expr, ty_lit) = parse_tts(cx, tts);

let ty = match ty_lit {
None => None,
Some(Ident{ident, span}) => match token::get_ident(ident).get() {
"f32" => Some(ast::TyF32),
"f64" => Some(ast::TyF64),
_ => {
cx.span_err(span, "invalid floating point type in hexfloat!");
None
}
}
};

let s = match expr.node {
// expression is a literal
ast::ExprLit(lit) => match lit.node {
// string literal
ast::LitStr(ref s, _) => {
s.clone()
}
_ => {
cx.span_err(expr.span, "unsupported literal in hexfloat!");
return base::MacResult::dummy_expr(sp);
}
},
_ => {
cx.span_err(expr.span, "non-literal in hexfloat!");
return base::MacResult::dummy_expr(sp);
}
};

{
let err = hex_float_lit_err(s.get());
match err {
Some((err_pos, err_str)) => {
let pos = expr.span.lo + syntax::codemap::Pos::from_uint(err_pos + 1);
let span = syntax::codemap::mk_sp(pos,pos);
cx.span_err(span, format!("invalid hex float literal in hexfloat!: {}", err_str));
return base::MacResult::dummy_expr(sp);
}
_ => ()
}
}

let lit = match ty {
None => ast::LitFloatUnsuffixed(s),
Some (ty) => ast::LitFloat(s, ty)
};
MRExpr(cx.expr_lit(sp, lit))
}

struct Ident {
ident: ast::Ident,
span: Span
}

fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) {
let p = &mut parse::new_parser_from_tts(cx.parse_sess(),
cx.cfg(),
tts.iter()
.map(|x| (*x).clone())
.collect());
let ex = p.parse_expr();
let id = if p.token == token::EOF {
None
} else {
p.expect(&token::COMMA);
let lo = p.span.lo;
let ident = p.parse_ident();
let hi = p.last_span.hi;
Some(Ident{ident: ident, span: mk_sp(lo, hi)})
};
if p.token != token::EOF {
p.unexpected();
}
(ex, id)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
35 changes: 35 additions & 0 deletions src/test/compile-fail/syntax-extension-hexfloat-bad-lits.rs
@@ -0,0 +1,35 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-stage1
// ignore-pretty
// ignore-cross-compile #12102

#[feature(phase)];

#[phase(syntax)]
extern crate hexfloat;

fn main() {
hexfloat!("foo");
//~^ ERROR invalid hex float literal in hexfloat!: Expected '0'
hexfloat!("0");
//~^ERROR invalid hex float literal in hexfloat!: Expected 'x'
hexfloat!("0x");
//~^ERROR invalid hex float literal in hexfloat!: Expected '.'
hexfloat!("0x.");
//~^ERROR invalid hex float literal in hexfloat!: Expected digits before or after decimal point
hexfloat!("0x0.0");
//~^ERROR invalid hex float literal in hexfloat!: Expected 'p'
hexfloat!("0x0.0p");
//~^ERROR invalid hex float literal in hexfloat!: Expected exponent digits
hexfloat!("0x0.0p0f");
//~^ERROR invalid hex float literal in hexfloat!: Expected end of string
}
27 changes: 27 additions & 0 deletions src/test/compile-fail/syntax-extension-hexfloat-bad-types.rs
@@ -0,0 +1,27 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-stage1
// ignore-pretty
// ignore-cross-compile #12102

#[feature(phase)];

#[phase(syntax)]
extern crate hexfloat;

fn main() {
hexfloat!(foo);
//~^ ERROR non-literal in hexfloat!
hexfloat!(0);
//~^ ERROR unsupported literal in hexfloat!
hexfloat!("0x0.p0", invalid);
//~^ ERROR invalid floating point type in hexfloat!
}
31 changes: 31 additions & 0 deletions src/test/run-pass/syntax-extension-hexfloat.rs
@@ -0,0 +1,31 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-stage1
// ignore-pretty
// ignore-cross-compile #12102
// ignore-fast

#[feature(phase)];
#[phase(syntax)]
extern crate hexfloat;

pub fn main() {
let a = hexfloat!("0x1.999999999999ap-4");
assert_eq!(a, 0.1);
let b = hexfloat!("-0x1.fffp-4", f32);
assert_eq!(b, -0.12498474_f32);
let c = hexfloat!("0x.12345p5", f64);
let d = hexfloat!("0x0.12345p5", f64);
assert_eq!(c,d);
let f = hexfloat!("0x10.p4", f32);
let g = hexfloat!("0x10.0p4", f32);
assert_eq!(f,g);
}

9 comments on commit a38e148

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from alexcrichton
at rcxdude@a38e148

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging rcxdude/rust/hexfloatext = a38e148 into auto

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rcxdude/rust/hexfloatext = a38e148 merged ok, testing candidate = d025620

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from alexcrichton
at rcxdude@a38e148

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging rcxdude/rust/hexfloatext = a38e148 into auto

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rcxdude/rust/hexfloatext = a38e148 merged ok, testing candidate = dd3e9d7

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on a38e148 Mar 11, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = dd3e9d7

Please sign in to comment.