Skip to content

Commit dd3e9d7

Browse files
committed
auto merge of #12652 : rcxdude/rust/hexfloatext, r=alexcrichton
Closes #1433. Implemented after suggestion by @cmr in #12323 This is slightly less flexible than the implementation in #12323 (binary and octal floats aren't supported, nor are underscores in the literal), but is cleaner in that it doesn't modify the core grammar, or require odd syntax for the number itself. The missing features could be added back with relatively little effort (the main awkwardness is parsing the string. Is there a good approach for this in the stdlib currently?)
2 parents cad7d24 + a38e148 commit dd3e9d7

File tree

5 files changed

+273
-1
lines changed

5 files changed

+273
-1
lines changed

Diff for: mk/crates.mk

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

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

@@ -76,6 +76,7 @@ DEPS_sync := std
7676
DEPS_getopts := std
7777
DEPS_collections := std
7878
DEPS_fourcc := syntax std
79+
DEPS_hexfloat := syntax std
7980
DEPS_num := std
8081
DEPS_test := std extra collections getopts serialize term
8182
DEPS_time := std serialize

Diff for: src/libhexfloat/lib.rs

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
/*!
12+
Syntax extension to create floating point literals from hexadecimal strings
13+
14+
Once loaded, hexfloat!() is called with a string containing the hexadecimal
15+
floating-point literal, and an optional type (f32 or f64).
16+
If the type is omitted, the literal is treated the same as a normal unsuffixed
17+
literal.
18+
19+
# Examples
20+
21+
To load the extension and use it:
22+
23+
```rust,ignore
24+
#[phase(syntax)]
25+
extern crate hexfloat;
26+
27+
fn main() {
28+
let val = hexfloat!("0x1.ffffb4", f32);
29+
}
30+
```
31+
32+
# References
33+
34+
* [ExploringBinary: hexadecimal floating point constants]
35+
(http://www.exploringbinary.com/hexadecimal-floating-point-constants/)
36+
37+
*/
38+
39+
#[crate_id = "hexfloat#0.10-pre"];
40+
#[crate_type = "rlib"];
41+
#[crate_type = "dylib"];
42+
#[license = "MIT/ASL2"];
43+
44+
#[feature(macro_registrar, managed_boxes)];
45+
46+
extern crate syntax;
47+
48+
use syntax::ast;
49+
use syntax::ast::Name;
50+
use syntax::codemap::{Span, mk_sp};
51+
use syntax::ext::base;
52+
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr};
53+
use syntax::ext::build::AstBuilder;
54+
use syntax::parse;
55+
use syntax::parse::token;
56+
57+
#[macro_registrar]
58+
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
59+
register(token::intern("hexfloat"),
60+
NormalTT(~BasicMacroExpander {
61+
expander: expand_syntax_ext,
62+
span: None,
63+
},
64+
None));
65+
}
66+
67+
//Check if the literal is valid (as LLVM expects),
68+
//and return a descriptive error if not.
69+
fn hex_float_lit_err(s: &str) -> Option<(uint, ~str)> {
70+
let mut chars = s.chars().peekable();
71+
let mut i = 0;
72+
if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
73+
if chars.next() != Some('0') { return Some((i, ~"Expected '0'")); } i+=1;
74+
if chars.next() != Some('x') { return Some((i, ~"Expected 'x'")); } i+=1;
75+
let mut d_len = 0;
76+
for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; d_len += 1;}
77+
if chars.next() != Some('.') { return Some((i, ~"Expected '.'")); } i+=1;
78+
let mut f_len = 0;
79+
for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; f_len += 1;}
80+
if d_len == 0 && f_len == 0 {
81+
return Some((i, ~"Expected digits before or after decimal point"));
82+
}
83+
if chars.next() != Some('p') { return Some((i, ~"Expected 'p'")); } i+=1;
84+
if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
85+
let mut e_len = 0;
86+
for _ in chars.take_while(|c| c.is_digit()) { chars.next(); i+=1; e_len += 1}
87+
if e_len == 0 {
88+
return Some((i, ~"Expected exponent digits"));
89+
}
90+
match chars.next() {
91+
None => None,
92+
Some(_) => Some((i, ~"Expected end of string"))
93+
}
94+
}
95+
96+
pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
97+
let (expr, ty_lit) = parse_tts(cx, tts);
98+
99+
let ty = match ty_lit {
100+
None => None,
101+
Some(Ident{ident, span}) => match token::get_ident(ident).get() {
102+
"f32" => Some(ast::TyF32),
103+
"f64" => Some(ast::TyF64),
104+
_ => {
105+
cx.span_err(span, "invalid floating point type in hexfloat!");
106+
None
107+
}
108+
}
109+
};
110+
111+
let s = match expr.node {
112+
// expression is a literal
113+
ast::ExprLit(lit) => match lit.node {
114+
// string literal
115+
ast::LitStr(ref s, _) => {
116+
s.clone()
117+
}
118+
_ => {
119+
cx.span_err(expr.span, "unsupported literal in hexfloat!");
120+
return base::MacResult::dummy_expr(sp);
121+
}
122+
},
123+
_ => {
124+
cx.span_err(expr.span, "non-literal in hexfloat!");
125+
return base::MacResult::dummy_expr(sp);
126+
}
127+
};
128+
129+
{
130+
let err = hex_float_lit_err(s.get());
131+
match err {
132+
Some((err_pos, err_str)) => {
133+
let pos = expr.span.lo + syntax::codemap::Pos::from_uint(err_pos + 1);
134+
let span = syntax::codemap::mk_sp(pos,pos);
135+
cx.span_err(span, format!("invalid hex float literal in hexfloat!: {}", err_str));
136+
return base::MacResult::dummy_expr(sp);
137+
}
138+
_ => ()
139+
}
140+
}
141+
142+
let lit = match ty {
143+
None => ast::LitFloatUnsuffixed(s),
144+
Some (ty) => ast::LitFloat(s, ty)
145+
};
146+
MRExpr(cx.expr_lit(sp, lit))
147+
}
148+
149+
struct Ident {
150+
ident: ast::Ident,
151+
span: Span
152+
}
153+
154+
fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) {
155+
let p = &mut parse::new_parser_from_tts(cx.parse_sess(),
156+
cx.cfg(),
157+
tts.iter()
158+
.map(|x| (*x).clone())
159+
.collect());
160+
let ex = p.parse_expr();
161+
let id = if p.token == token::EOF {
162+
None
163+
} else {
164+
p.expect(&token::COMMA);
165+
let lo = p.span.lo;
166+
let ident = p.parse_ident();
167+
let hi = p.last_span.hi;
168+
Some(Ident{ident: ident, span: mk_sp(lo, hi)})
169+
};
170+
if p.token != token::EOF {
171+
p.unexpected();
172+
}
173+
(ex, id)
174+
}
175+
176+
// FIXME (10872): This is required to prevent an LLVM assert on Windows
177+
#[test]
178+
fn dummy_test() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-stage1
12+
// ignore-pretty
13+
// ignore-cross-compile #12102
14+
15+
#[feature(phase)];
16+
17+
#[phase(syntax)]
18+
extern crate hexfloat;
19+
20+
fn main() {
21+
hexfloat!("foo");
22+
//~^ ERROR invalid hex float literal in hexfloat!: Expected '0'
23+
hexfloat!("0");
24+
//~^ERROR invalid hex float literal in hexfloat!: Expected 'x'
25+
hexfloat!("0x");
26+
//~^ERROR invalid hex float literal in hexfloat!: Expected '.'
27+
hexfloat!("0x.");
28+
//~^ERROR invalid hex float literal in hexfloat!: Expected digits before or after decimal point
29+
hexfloat!("0x0.0");
30+
//~^ERROR invalid hex float literal in hexfloat!: Expected 'p'
31+
hexfloat!("0x0.0p");
32+
//~^ERROR invalid hex float literal in hexfloat!: Expected exponent digits
33+
hexfloat!("0x0.0p0f");
34+
//~^ERROR invalid hex float literal in hexfloat!: Expected end of string
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-stage1
12+
// ignore-pretty
13+
// ignore-cross-compile #12102
14+
15+
#[feature(phase)];
16+
17+
#[phase(syntax)]
18+
extern crate hexfloat;
19+
20+
fn main() {
21+
hexfloat!(foo);
22+
//~^ ERROR non-literal in hexfloat!
23+
hexfloat!(0);
24+
//~^ ERROR unsupported literal in hexfloat!
25+
hexfloat!("0x0.p0", invalid);
26+
//~^ ERROR invalid floating point type in hexfloat!
27+
}

Diff for: src/test/run-pass/syntax-extension-hexfloat.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-stage1
12+
// ignore-pretty
13+
// ignore-cross-compile #12102
14+
// ignore-fast
15+
16+
#[feature(phase)];
17+
#[phase(syntax)]
18+
extern crate hexfloat;
19+
20+
pub fn main() {
21+
let a = hexfloat!("0x1.999999999999ap-4");
22+
assert_eq!(a, 0.1);
23+
let b = hexfloat!("-0x1.fffp-4", f32);
24+
assert_eq!(b, -0.12498474_f32);
25+
let c = hexfloat!("0x.12345p5", f64);
26+
let d = hexfloat!("0x0.12345p5", f64);
27+
assert_eq!(c,d);
28+
let f = hexfloat!("0x10.p4", f32);
29+
let g = hexfloat!("0x10.0p4", f32);
30+
assert_eq!(f,g);
31+
}

0 commit comments

Comments
 (0)