Last active
August 17, 2018 02:12
-
-
Save niconii/b24f01caab534fc5a38e3a14b73454eb to your computer and use it in GitHub Desktop.
Forth number formatting, ported to Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use nfmt::Nfmt; | |
fn main() { | |
println!("{}", number(25252)); // 25,252 | |
println!("{}", number(-500)); // -500 | |
println!("{}", cents(1234567890)); // $12,345,678.90 | |
println!("{}", cents(-200000)); // ($2,000.00) | |
println!("{}", hex(32768)); // 0x8000 | |
println!("{}", hex(!0)); // 0xFFFFFFFFFFFFFFFF | |
println!("{}", yen(573765)); // 57万3765円 | |
println!("{}", yen(-9876543210)); // -98億7654万3210円 | |
} | |
fn sign_u64(n: i64) -> (bool, u64) { | |
(n < 0, n.abs() as u64) | |
} | |
fn thousands(f: &mut Nfmt, n: &mut u64) { | |
while *n > 999 { | |
for _ in 0..3 { f.digit(n); } | |
f.hold(b','); | |
} | |
f.digits(n); | |
} | |
fn number(n: i64) -> String { | |
let (sign, ref mut n) = sign_u64(n); | |
let mut f = Nfmt::new(10); | |
thousands(&mut f, n); | |
if sign { f.hold(b'-'); } | |
f.as_str().to_owned() | |
} | |
fn cents(n: i64) -> String { | |
let (sign, ref mut n) = sign_u64(n); | |
let mut f = Nfmt::new(10); | |
if sign { f.hold(b')'); } // accounting-style negative numbers | |
f.digit(n); | |
f.digit(n); | |
f.hold(b'.'); | |
thousands(&mut f, n); | |
f.hold(b'$'); | |
if sign { f.hold(b'('); } | |
f.as_str().to_owned() | |
} | |
fn hex(mut n: u64) -> String { | |
let n = &mut n; | |
let mut f = Nfmt::new(16); | |
f.digits(n); | |
f.holds("0x"); | |
f.as_str().to_owned() | |
} | |
fn yen(n: i64) -> String { | |
let (sign, ref mut n) = sign_u64(n); | |
let mut f = Nfmt::new(10); | |
f.xhold('円'); | |
for c in "万億兆京垓".chars() { | |
if *n > 9999 { | |
for _ in 0..4 { f.digit(n); } | |
f.xhold(c); | |
} | |
} | |
f.digits(n); | |
if sign { f.hold(b'-'); } | |
f.as_str().to_owned() | |
} | |
mod nfmt { | |
use std::str; | |
const BUF_LEN: usize = 64; | |
static DIGITS: [u8; 36] = *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | |
pub struct Nfmt { | |
buf: [u8; BUF_LEN], | |
start: usize, | |
base: u8 | |
} | |
impl Nfmt { | |
/// Forth: `base <#` | |
pub fn new(base: u8) -> Nfmt { | |
assert!(base >= 2); | |
assert!(base <= 36); | |
Nfmt { | |
buf: [0; BUF_LEN], | |
start: BUF_LEN, | |
base: base | |
} | |
} | |
/// Forth: `hold` | |
pub fn hold(&mut self, c: u8) { | |
assert!(c <= 0x7f); | |
self.start -= 1; | |
self.buf[self.start] = c; | |
} | |
/// Forth: `holds` | |
pub fn holds(&mut self, s: &str) { | |
let len = s.len(); | |
self.start -= len; | |
self.as_bytes_mut()[0..len].copy_from_slice(s.as_bytes()); | |
} | |
/// Forth: `xhold` | |
pub fn xhold(&mut self, c: char) { | |
let len = c.len_utf8(); | |
self.start -= len; | |
c.encode_utf8(self.as_bytes_mut()); | |
} | |
/// Forth: `#` | |
pub fn digit(&mut self, n: &mut u64) { | |
let digit = DIGITS[(*n % (self.base as u64)) as usize]; | |
*n /= self.base as u64; | |
self.hold(digit); | |
} | |
/// Forth: `#s` | |
pub fn digits(&mut self, n: &mut u64) { | |
self.digit(n); | |
while *n > 0 { | |
self.digit(n); | |
} | |
} | |
pub fn as_bytes(&self) -> &[u8] { | |
&self.buf[(self.start)..] | |
} | |
fn as_bytes_mut(&mut self) -> &mut [u8] { | |
&mut self.buf[(self.start)..] | |
} | |
/// Forth: `#>` | |
pub fn as_str(&self) -> &str { | |
str::from_utf8(self.as_bytes()).unwrap() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment