feat(fstring): implement scientific and percentage formatting

This commit is contained in:
2026-02-08 00:35:55 +01:00
parent d3c6fc0219
commit f5f8158ff1
2 changed files with 45 additions and 10 deletions

View File

@@ -1,33 +1,48 @@
// Basic type print("Basic type")
let a = 42 let a = 42
print(f"int: {a:d}; hex: {a:x}; HEX: {a:X}; oct: {a:o}; bin: {a:b}") print(f"int: {a:d}; hex: {a:x}; HEX: {a:X}; oct: {a:o}; bin: {a:b}")
print()
// Grouping print("Grouping")
let b = 1234567890 let b = 1234567890
print(f"{b:,}") print(f"{b:,}")
print(f"{b:_}") print(f"{b:_}")
print()
let c = 1234.5678 let c = 1234.5678
print(f"{c:,._}") print(f"{c:,._}")
print(f"{c:_.,}") print(f"{c:_.,}")
print()
// Sign print("Sign")
let d1 = 14 let d1 = 14
let d2 = -26 let d2 = -26
print(f"{d1} {d2}") print(f"{d1} {d2}")
print(f"{d1:+} {d2:+}") print(f"{d1:+} {d2:+}")
print(f"{d1:-} {d2:-}") print(f"{d1:-} {d2:-}")
print(f"{d1: } {d2: }") print(f"{d1: } {d2: }")
print()
// Percentage print("Percentage")
let pts = 19 let pts = 19
let total = 22 let total = 22
print(f"Correct answers: {pts/total:.2%}") print(f"Correct answers: {pts/total:.2%}")
print()
// Precision print("Precision")
print(f"{c:8}") print(f"{c:8}")
print(f"{c:8.2}") print(f"{c:8.2}")
print(f"{c:.2}") print(f"{c:.2}")
print(f"{c:.8}") print(f"{c:.8}")
print()
// Complex print("Scientific")
print(f"{a:e}")
print(f"{b:e}")
print(f"{c:e}")
print(f"{d1:e}")
print(f"{d2:e}")
print(f"{0:e}")
print()
print("Complex")

View File

@@ -1,3 +1,4 @@
from math import log10, floor
from typing import Any, Optional from typing import Any, Optional
from src.core.format_spec.spec import FormatSpec from src.core.format_spec.spec import FormatSpec
@@ -75,8 +76,12 @@ class StringFormatter:
res = obj res = obj
case TokenType.T_BIN | TokenType.T_DEC | TokenType.T_HEX | TokenType.T_HEX_CAPS | TokenType.T_OCT: case TokenType.T_BIN | TokenType.T_DEC | TokenType.T_HEX | TokenType.T_HEX_CAPS | TokenType.T_OCT:
res = self.format_int(obj, spec, fmt_type) res = self.format_int(obj, spec, fmt_type)
case TokenType.T_SCI | TokenType.T_FIX | TokenType.T_PCT: case TokenType.T_FIX:
res = self.format_float(obj, spec, fmt_type) res = self.format_float_fix(obj, spec, fmt_type)
case TokenType.T_PCT:
res = self.format_float_fix(obj * 100, spec, TokenType.T_FIX) + "%"
case TokenType.T_SCI:
res = self.format_float_sci(obj, spec, fmt_type)
if spec.number.integral.width is not None: if spec.number.integral.width is not None:
res = self.pad(res, spec.number.integral.width) res = self.pad(res, spec.number.integral.width)
@@ -92,7 +97,7 @@ class StringFormatter:
return sign + string return sign + string
def format_float(self, value: float, spec: FormatSpec, fmt_type: TokenType) -> str: def format_float_fix(self, value: float, spec: FormatSpec, fmt_type: TokenType) -> str:
sign: str = self.make_sign(value, spec) sign: str = self.make_sign(value, spec)
value = abs(value) value = abs(value)
integer: int = int(value) integer: int = int(value)
@@ -100,9 +105,16 @@ class StringFormatter:
integer_spec = spec.number.integral integer_spec = spec.number.integral
decimal_spec = spec.number.decimal decimal_spec = spec.number.decimal
precision: Optional[int] = None
if fmt_type == TokenType.T_SCI:
precision = 6
if decimal_spec is not None: if decimal_spec is not None:
if decimal_spec.precision is not None: if decimal_spec.precision is not None:
decimal = round(decimal, decimal_spec.precision) precision = decimal_spec.precision
if precision is not None:
decimal = round(decimal, precision)
integer_str: str = self.format_int_part(integer, fmt_type, integer_spec.grouping) integer_str: str = self.format_int_part(integer, fmt_type, integer_spec.grouping)
decimal_str: str = self.format_int_part(int(str(decimal)[2:]), fmt_type, None if decimal_spec is None else decimal_spec.grouping) decimal_str: str = self.format_int_part(int(str(decimal)[2:]), fmt_type, None if decimal_spec is None else decimal_spec.grouping)
@@ -111,6 +123,14 @@ class StringFormatter:
return f"{sign}{integer_str}{decimal_str}" return f"{sign}{integer_str}{decimal_str}"
def format_float_sci(self, value: float, spec: FormatSpec, fmt_type: TokenType) -> str:
sign: str = self.make_sign(value, spec)
value = abs(value)
mag: int = 0 if value == 0 else floor(log10(value))
coef: float = value / (10 ** mag)
coef_str: str = self.format_float_fix(coef, spec, fmt_type)
return f"{sign}{coef_str}e{mag:+03d}"
def format_int_part(self, value: int, fmt_type: TokenType, grouping: Optional[Token]): def format_int_part(self, value: int, fmt_type: TokenType, grouping: Optional[Token]):
groups: int = 4 groups: int = 4
string: str = str(value) string: str = str(value)