feat(fstring): add sign-aware alignment

This commit is contained in:
2026-02-08 01:31:04 +01:00
parent 2bfde130e8
commit 88a72e5426
6 changed files with 42 additions and 11 deletions

View File

@@ -55,5 +55,12 @@ print(f"{"test":<<9}")
print(f"{"test":>>9}")
print(f"{"test":^^9}")
print()
print(f"{d2:8}")
print(f"{d2:08}")
print(f"{d2:<<8}")
print(f"{d2:>>8}")
print(f"{d2:^^8}")
print(f"{d2:==8}")
print()
print("Complex")

View File

@@ -83,6 +83,8 @@ class FormatSpecLexer:
self.add_token(TokenType.RIGHT)
case "^":
self.add_token(TokenType.CENTER)
case "=":
self.add_token(TokenType.EQUAL)
case "+":
self.add_token(TokenType.PLUS)
case "-":
@@ -113,6 +115,8 @@ class FormatSpecLexer:
self.add_token(TokenType.T_PCT)
case ".":
self.add_token(TokenType.DOT)
case "0":
self.add_token(TokenType.ZERO)
case _:
if char.isdigit():
self.scan_number()

View File

@@ -23,13 +23,14 @@ class FormatSpecParser:
ALIGNMENT: set[TokenType] = {
TokenType.LEFT,
TokenType.RIGHT,
TokenType.CENTER
TokenType.CENTER,
TokenType.EQUAL,
}
SIGN: set[TokenType] = {
TokenType.PLUS,
TokenType.MINUS,
TokenType.SPACE
TokenType.SPACE,
}
def __init__(self, tokens: list[Token]):
@@ -93,9 +94,12 @@ class FormatSpecParser:
if self.match(*self.SIGN):
sign = self.previous()
zfill: bool = self.match(TokenType.ZERO)
return FormatSpecOptions(
alignment=alignment,
sign=sign
sign=sign,
zfill=zfill
)
def alignment(self) -> Optional[FormatSpecAlignment]:

View File

@@ -14,6 +14,7 @@ class FormatSpecAlignment:
class FormatSpecOptions:
alignment: Optional[FormatSpecAlignment]
sign: Optional[Token]
zfill: bool
@dataclass(frozen=True)

View File

@@ -69,8 +69,7 @@ class StringFormatter:
else:
fmt_type, obj = self.guess_type(obj)
align_side: TokenType = TokenType.LEFT
fill_char: str = " "
is_num: bool = False
match fmt_type:
case None:
res = self.stringify(obj)
@@ -78,18 +77,27 @@ 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)
align_side = TokenType.RIGHT
is_num = True
case TokenType.T_FIX:
res = self.format_float_fix(obj, spec, fmt_type)
align_side = TokenType.RIGHT
is_num = True
case TokenType.T_PCT:
res = self.format_float_fix(obj * 100, spec, TokenType.T_FIX) + "%"
align_side = TokenType.RIGHT
is_num = True
case TokenType.T_SCI:
res = self.format_float_sci(obj, spec, fmt_type)
align_side = TokenType.RIGHT
is_num = True
align_spec: Optional[FormatSpecAlignment] = spec.options.alignment
align_side: TokenType = TokenType.LEFT
fill_char: str = "0" if spec.options.zfill else " "
if is_num:
align_side = TokenType.EQUAL if spec.options.zfill else TokenType.RIGHT
elif align_side == TokenType.EQUAL:
# Should always be true at this point
if align_spec is not None:
raise PebbleRuntimeError(align_spec.align, "Sign-aware alignment is only valid for numbers.")
if align_spec is not None:
align_side = align_spec.align.type
if align_spec.fill is not None:
@@ -190,11 +198,16 @@ class StringFormatter:
case TokenType.LEFT:
left = 0
right = to_pad
case TokenType.RIGHT:
case TokenType.RIGHT | TokenType.EQUAL:
left = to_pad
right = 0
case TokenType.CENTER:
left = to_pad // 2
right = to_pad - left
return char * left + string + char * right
sign: str = ""
if side == TokenType.EQUAL:
if string.startswith(("+", "-", " ")):
sign = string[0]
string = string[1:]
return sign + char * left + string + char * right

View File

@@ -10,6 +10,7 @@ class TokenType(Enum):
LEFT = auto()
RIGHT = auto()
CENTER = auto()
EQUAL = auto()
# Sign
PLUS = auto()
@@ -37,6 +38,7 @@ class TokenType(Enum):
T_PCT = auto()
# Misc
ZERO = auto()
NUMBER = auto()
DOT = auto()
EOF = auto()