Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@niconii
Last active August 17, 2018 02:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niconii/b24f01caab534fc5a38e3a14b73454eb to your computer and use it in GitHub Desktop.
Save niconii/b24f01caab534fc5a38e3a14b73454eb to your computer and use it in GitHub Desktop.
Forth number formatting, ported to Rust
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