feat(fstring): add sign-aware alignment
This commit is contained in:
@@ -55,5 +55,12 @@ print(f"{"test":<<9}")
|
|||||||
print(f"{"test":>>9}")
|
print(f"{"test":>>9}")
|
||||||
print(f"{"test":^^9}")
|
print(f"{"test":^^9}")
|
||||||
print()
|
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")
|
print("Complex")
|
||||||
@@ -83,6 +83,8 @@ class FormatSpecLexer:
|
|||||||
self.add_token(TokenType.RIGHT)
|
self.add_token(TokenType.RIGHT)
|
||||||
case "^":
|
case "^":
|
||||||
self.add_token(TokenType.CENTER)
|
self.add_token(TokenType.CENTER)
|
||||||
|
case "=":
|
||||||
|
self.add_token(TokenType.EQUAL)
|
||||||
case "+":
|
case "+":
|
||||||
self.add_token(TokenType.PLUS)
|
self.add_token(TokenType.PLUS)
|
||||||
case "-":
|
case "-":
|
||||||
@@ -113,6 +115,8 @@ class FormatSpecLexer:
|
|||||||
self.add_token(TokenType.T_PCT)
|
self.add_token(TokenType.T_PCT)
|
||||||
case ".":
|
case ".":
|
||||||
self.add_token(TokenType.DOT)
|
self.add_token(TokenType.DOT)
|
||||||
|
case "0":
|
||||||
|
self.add_token(TokenType.ZERO)
|
||||||
case _:
|
case _:
|
||||||
if char.isdigit():
|
if char.isdigit():
|
||||||
self.scan_number()
|
self.scan_number()
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ class FormatSpecParser:
|
|||||||
ALIGNMENT: set[TokenType] = {
|
ALIGNMENT: set[TokenType] = {
|
||||||
TokenType.LEFT,
|
TokenType.LEFT,
|
||||||
TokenType.RIGHT,
|
TokenType.RIGHT,
|
||||||
TokenType.CENTER
|
TokenType.CENTER,
|
||||||
|
TokenType.EQUAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
SIGN: set[TokenType] = {
|
SIGN: set[TokenType] = {
|
||||||
TokenType.PLUS,
|
TokenType.PLUS,
|
||||||
TokenType.MINUS,
|
TokenType.MINUS,
|
||||||
TokenType.SPACE
|
TokenType.SPACE,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, tokens: list[Token]):
|
def __init__(self, tokens: list[Token]):
|
||||||
@@ -93,9 +94,12 @@ class FormatSpecParser:
|
|||||||
if self.match(*self.SIGN):
|
if self.match(*self.SIGN):
|
||||||
sign = self.previous()
|
sign = self.previous()
|
||||||
|
|
||||||
|
zfill: bool = self.match(TokenType.ZERO)
|
||||||
|
|
||||||
return FormatSpecOptions(
|
return FormatSpecOptions(
|
||||||
alignment=alignment,
|
alignment=alignment,
|
||||||
sign=sign
|
sign=sign,
|
||||||
|
zfill=zfill
|
||||||
)
|
)
|
||||||
|
|
||||||
def alignment(self) -> Optional[FormatSpecAlignment]:
|
def alignment(self) -> Optional[FormatSpecAlignment]:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class FormatSpecAlignment:
|
|||||||
class FormatSpecOptions:
|
class FormatSpecOptions:
|
||||||
alignment: Optional[FormatSpecAlignment]
|
alignment: Optional[FormatSpecAlignment]
|
||||||
sign: Optional[Token]
|
sign: Optional[Token]
|
||||||
|
zfill: bool
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ class StringFormatter:
|
|||||||
else:
|
else:
|
||||||
fmt_type, obj = self.guess_type(obj)
|
fmt_type, obj = self.guess_type(obj)
|
||||||
|
|
||||||
align_side: TokenType = TokenType.LEFT
|
is_num: bool = False
|
||||||
fill_char: str = " "
|
|
||||||
match fmt_type:
|
match fmt_type:
|
||||||
case None:
|
case None:
|
||||||
res = self.stringify(obj)
|
res = self.stringify(obj)
|
||||||
@@ -78,18 +77,27 @@ 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)
|
||||||
align_side = TokenType.RIGHT
|
is_num = True
|
||||||
case TokenType.T_FIX:
|
case TokenType.T_FIX:
|
||||||
res = self.format_float_fix(obj, spec, fmt_type)
|
res = self.format_float_fix(obj, spec, fmt_type)
|
||||||
align_side = TokenType.RIGHT
|
is_num = True
|
||||||
case TokenType.T_PCT:
|
case TokenType.T_PCT:
|
||||||
res = self.format_float_fix(obj * 100, spec, TokenType.T_FIX) + "%"
|
res = self.format_float_fix(obj * 100, spec, TokenType.T_FIX) + "%"
|
||||||
align_side = TokenType.RIGHT
|
is_num = True
|
||||||
case TokenType.T_SCI:
|
case TokenType.T_SCI:
|
||||||
res = self.format_float_sci(obj, spec, fmt_type)
|
res = self.format_float_sci(obj, spec, fmt_type)
|
||||||
align_side = TokenType.RIGHT
|
is_num = True
|
||||||
|
|
||||||
align_spec: Optional[FormatSpecAlignment] = spec.options.alignment
|
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:
|
if align_spec is not None:
|
||||||
align_side = align_spec.align.type
|
align_side = align_spec.align.type
|
||||||
if align_spec.fill is not None:
|
if align_spec.fill is not None:
|
||||||
@@ -190,11 +198,16 @@ class StringFormatter:
|
|||||||
case TokenType.LEFT:
|
case TokenType.LEFT:
|
||||||
left = 0
|
left = 0
|
||||||
right = to_pad
|
right = to_pad
|
||||||
case TokenType.RIGHT:
|
case TokenType.RIGHT | TokenType.EQUAL:
|
||||||
left = to_pad
|
left = to_pad
|
||||||
right = 0
|
right = 0
|
||||||
case TokenType.CENTER:
|
case TokenType.CENTER:
|
||||||
left = to_pad // 2
|
left = to_pad // 2
|
||||||
right = to_pad - left
|
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
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class TokenType(Enum):
|
|||||||
LEFT = auto()
|
LEFT = auto()
|
||||||
RIGHT = auto()
|
RIGHT = auto()
|
||||||
CENTER = auto()
|
CENTER = auto()
|
||||||
|
EQUAL = auto()
|
||||||
|
|
||||||
# Sign
|
# Sign
|
||||||
PLUS = auto()
|
PLUS = auto()
|
||||||
@@ -37,6 +38,7 @@ class TokenType(Enum):
|
|||||||
T_PCT = auto()
|
T_PCT = auto()
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
|
ZERO = auto()
|
||||||
NUMBER = auto()
|
NUMBER = auto()
|
||||||
DOT = auto()
|
DOT = auto()
|
||||||
EOF = auto()
|
EOF = auto()
|
||||||
|
|||||||
Reference in New Issue
Block a user