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
print(f"int: {a:d}; hex: {a:x}; HEX: {a:X}; oct: {a:o}; bin: {a:b}")
print()
// Grouping
print("Grouping")
let b = 1234567890
print(f"{b:,}")
print(f"{b:_}")
print()
let c = 1234.5678
print(f"{c:,._}")
print(f"{c:_.,}")
print()
// Sign
print("Sign")
let d1 = 14
let d2 = -26
print(f"{d1} {d2}")
print(f"{d1:+} {d2:+}")
print(f"{d1:-} {d2:-}")
print(f"{d1: } {d2: }")
print()
// Percentage
print("Percentage")
let pts = 19
let total = 22
print(f"Correct answers: {pts/total:.2%}")
print()
// Precision
print("Precision")
print(f"{c:8}")
print(f"{c:8.2}")
print(f"{c:.2}")
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 src.core.format_spec.spec import FormatSpec
@@ -75,8 +76,12 @@ class StringFormatter:
res = obj
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)
case TokenType.T_SCI | TokenType.T_FIX | TokenType.T_PCT:
res = self.format_float(obj, spec, fmt_type)
case TokenType.T_FIX:
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:
res = self.pad(res, spec.number.integral.width)
@@ -92,7 +97,7 @@ class StringFormatter:
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)
value = abs(value)
integer: int = int(value)
@@ -100,9 +105,16 @@ class StringFormatter:
integer_spec = spec.number.integral
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.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)
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}"
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]):
groups: int = 4
string: str = str(value)