synthphonia/text/formatting/
float.rs1use std::cmp::{max, min};
2
3use regex::Regex;
4
5use crate::forward::enumeration::Enumerator1;
6use crate::utils::F64;
7use crate::value::{ConstValue, Value};
8use crate::{ impl_name, impl_op1, parser::config::Config};
9
10use crate::galloc::{AllocForExactSizeIter, AllocForStr};
11
12use super::FormattingOp;
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct FormatFloat{
15 cost: usize,
16 padding: (usize, usize),
17 min_size : (usize, usize),
18}
19
20impl FormatFloat {
21 pub fn from_config(config: &Config) -> Self {
22 Self{
23 cost: config.get_usize("cost").unwrap_or(1),
24 padding: (config.get_usize("left").unwrap_or(0), config.get_usize("right").unwrap_or(0)),
25 min_size: (0, 0)
26 }
27 }
28 pub fn format_single(&self, value: F64) -> String {
29 let value = *value;
30 let value_int = if value >= 0.0 { value.floor() } else { value.ceil() };
31 let left = if self.padding.0 > 0 {
32 format!("{:0left$}", value_int, left= self.padding.0)
33 } else { format!("{}", value_int) };
34
35 if let Some(mut right) = format!("{}", value).split_once('.').map(|x| x.1.to_string()) {
36 while right.len() < self.padding.1 {
37 right.push('0');
38 }
39 left + "." + &right
40 } else if self.padding.1 > 0 {
41 left + "." + &"0".repeat(self.padding.1)
42 } else { left }
43 }
44 pub fn get_format(input: &str) -> Self {
45 let endzero = input.ends_with("0") && input.contains(".");
46 let startzero = input.starts_with("+0") || input.starts_with("-0") || input.starts_with("0");
47 let min_left = input.chars().position(|x| x == '.').unwrap_or(input.len());
48 let min_right = input.chars().position(|x| x == '.').map(|x| input.len() - 1 - x).unwrap_or(0);
49 let before_dot = if startzero { min_left } else { 0 };
50 let after_dot = if endzero { min_right } else { 0 };
51 Self { cost: 1, padding: (before_dot, after_dot), min_size: (min_left, min_right) }
52 }
53}
54
55impl FormatFloat {
56 pub fn name() -> &'static str {
57 "float.fmt"
58 }
59}
60
61impl std::fmt::Display for FormatFloat {
62 fn fmt(&self,f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(f, "float.fmt #left:{} #right:{}", self.padding.0, self.padding.1)
64 }
65}
66
67impl Default for FormatFloat {
68 fn default() -> Self {
69 Self::from_config(&Default::default())
70 }
71}
72
73impl Enumerator1 for FormatFloat {
74 fn enumerate(&self, this: &'static crate::expr::ops::Op1Enum, exec: &'static crate::forward::executor::Executor, opnt: [usize; 1]) -> Result<(), ()> { Ok(()) }
75}
76
77crate::impl_formatop!(FormatFloat, Float, |this: &FormatFloat| this.cost);
78
79impl FormattingOp for FormatFloat {
80 fn format(&self, input: &'static str) -> Option<(Self, crate::value::ConstValue, &'static str)> {
81 let regex = Regex::new(r"^\-?\d+(\.\d*)?".to_string().as_str()).unwrap();
82 if let Some(a) = regex.find(input) {
83 if a.as_str().ends_with(".") { return None; }
84 if let Ok(r) = a.as_str().parse::<f64>() {
85 let cv: ConstValue = F64::new(r).into();
86 Some((Self::get_format(a.as_str()), cv, &input[a.as_str().len()..]))
87 } else { None }
88 } else { None }
89 }
90
91 fn union(self, other: Self) -> Option<Self> {
92 let left = conflict(self.padding.0, other.padding.0)?;
93 let right = conflict(self.padding.1, other.padding.1)?;
94 let min_left = min(self.min_size.0, other.min_size.0);
95 let min_right = min(self.min_size.1, other.min_size.1);
96 if left > min_left { return None; }
97 if right > min_right { return None; }
98 Some(Self{ cost: 1, padding: (left, right), min_size: (min_left, min_right)})
99 }
100
101 fn bad_value() -> crate::value::ConstValue {
102 crate::value::ConstValue::Float(0.0.into())
103 }
104}
105
106fn conflict(a: usize, b: usize) -> Option<usize> {
107 if a > 0 && b > 0 && a != b { return None; }
108 Some(max(a, b))
109}
110
111#[cfg(test)]
112mod tests {
113 use crate::text::formatting::FormatFloat;
114
115 #[test]
116 fn format() {
117 let a = "001234000.01010";
118 assert_eq!(FormatFloat::get_format(a).format_single(a.parse::<f64>().unwrap().into()), a);
119 let a = "001234000.0101";
120 assert_eq!(FormatFloat::get_format(a).format_single(a.parse::<f64>().unwrap().into()), a);
121 let a = "1234000.01010";
122 assert_eq!(FormatFloat::get_format(a).format_single(a.parse::<f64>().unwrap().into()), a);
123 let a = "1234000.01010";
124 assert_eq!(FormatFloat::get_format(a).format_single(a.parse::<f64>().unwrap().into()), a);
125 let a = "-01234000.01010";
126 assert_eq!(FormatFloat::get_format(a).format_single(a.parse::<f64>().unwrap().into()), a);
127 let a = "-1234000.0101000";
128 assert_eq!(FormatFloat::get_format(a).format_single(a.parse::<f64>().unwrap().into()), a);
129 }
130}