fix(lang): 宽度后缀支持任意位宽并修复数字分隔符/进制解析
1) _read_number 只在 '_' 后接该进制合法数字时吞下划线,'_u8' 等后缀不再被数字循环吃掉;_read_width_suffix 取消 1..64 限制,只保留 int 溢出保护。2) 负的显式宽度字面量在 parser 折叠符号,-64_i7 / -128_i8 不再先按正幅值报越界。3) atoll/atof 支持 0b/0o 前缀与 '_' 分隔符,修复 1_000 被解析成 1、0b1010 被解析成 0 的问题。
This commit is contained in:
1 parent
52face702b
commit
ee2a9f0832
3 files changed
+68
-38
No files matched your search
+27
-12
@@ -584,6 +584,20 @@ def _read_name(lx: Lexer*):
|
||||
# Number Handling
|
||||
# ============================================================
|
||||
|
||||
# _digit_sep - 下划线分隔符:仅当后面紧跟该进制的合法数字时才算分隔符。
|
||||
# 这样 `128_u8` 的 `_` 会留给 `_read_width_suffix`(宽度后缀),而 `1_000` 仍按分隔符处理。
|
||||
def _digit_sep(lx: Lexer*, base: int) -> int:
|
||||
"""1 when src[pos]=='_' and the next char is a digit of base (digit separator)."""
|
||||
if lx.pos >= lx.len: return 0
|
||||
if lx.src[lx.pos] != '_': return 0
|
||||
if lx.pos + 1 >= lx.len: return 0
|
||||
ch: char = lx.src[lx.pos + 1]
|
||||
if base == 16: return 1 if _is_hex(ch) != 0 else 0
|
||||
if base == 8: return 1 if ch >= '0' and ch <= '7' else 0
|
||||
if base == 2: return 1 if ch == '0' or ch == '1' else 0
|
||||
return 1 if _is_digit(ch) != 0 else 0
|
||||
|
||||
|
||||
def _read_number(lx: Lexer*):
|
||||
start: sizet = lx.pos
|
||||
start_lineno: int = lx.lineno
|
||||
@@ -600,7 +614,7 @@ def _read_number(lx: Lexer*):
|
||||
lx.col += 2
|
||||
while lx.pos < lx.len:
|
||||
ch: char = lx.src[lx.pos]
|
||||
if _is_hex(ch) or ch == '_':
|
||||
if _is_hex(ch) != 0 or _digit_sep(lx, 16) != 0:
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
else:
|
||||
@@ -612,10 +626,7 @@ def _read_number(lx: Lexer*):
|
||||
lx.col += 2
|
||||
while lx.pos < lx.len:
|
||||
ch: char = lx.src[lx.pos]
|
||||
if ch >= '0' and ch <= '7':
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
elif ch == '_':
|
||||
if (ch >= '0' and ch <= '7') or _digit_sep(lx, 8) != 0:
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
else:
|
||||
@@ -627,7 +638,7 @@ def _read_number(lx: Lexer*):
|
||||
lx.col += 2
|
||||
while lx.pos < lx.len:
|
||||
ch: char = lx.src[lx.pos]
|
||||
if ch == '0' or ch == '1' or ch == '_':
|
||||
if ch == '0' or ch == '1' or _digit_sep(lx, 2) != 0:
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
else:
|
||||
@@ -638,7 +649,7 @@ def _read_number(lx: Lexer*):
|
||||
# Decimal integer/float
|
||||
while lx.pos < lx.len:
|
||||
ch: char = lx.src[lx.pos]
|
||||
if _is_digit(ch) or ch == '_':
|
||||
if _is_digit(ch) != 0 or _digit_sep(lx, 10) != 0:
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
else:
|
||||
@@ -651,7 +662,7 @@ def _read_number(lx: Lexer*):
|
||||
lx.col += 1
|
||||
while lx.pos < lx.len:
|
||||
ch: char = lx.src[lx.pos]
|
||||
if _is_digit(ch) or ch == '_':
|
||||
if _is_digit(ch) != 0 or _digit_sep(lx, 10) != 0:
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
else:
|
||||
@@ -671,7 +682,7 @@ def _read_number(lx: Lexer*):
|
||||
lx.col += 1
|
||||
while lx.pos < lx.len:
|
||||
ch: char = lx.src[lx.pos]
|
||||
if _is_digit(ch) or ch == '_':
|
||||
if _is_digit(ch) != 0 or _digit_sep(lx, 10) != 0:
|
||||
lx.pos += 1
|
||||
lx.col += 1
|
||||
else:
|
||||
@@ -696,7 +707,8 @@ def _suffix_is(lx: Lexer*, p: sizet, s: str, n: int) -> int:
|
||||
|
||||
# _read_width_suffix - 解析整型字面量的显式宽度后缀(_int<N>/_uint<N>/_i<N>/_u<N>)。
|
||||
# 命中则推进 lx.pos/col,返回 +N(有符号)/ -N(无符号);未命中返回 0 且不动位置。
|
||||
# N 取 1..64(字面量值本身是 64 位);后缀之后不得直接跟标识符字符。
|
||||
# 语言层不限制 N(LLVM/MLIR 支持任意位宽);仅防止 int 溢出(超出 int32 的宽度视为无效后缀)。
|
||||
# 后缀之后不得直接跟标识符字符。
|
||||
def _read_width_suffix(lx: Lexer*) -> int:
|
||||
"""Explicit integer width suffix: +N signed / -N unsigned / 0 none (advances on hit)."""
|
||||
if lx.pos >= lx.len: return 0
|
||||
@@ -720,10 +732,13 @@ def _read_width_suffix(lx: Lexer*) -> int:
|
||||
while p < lx.len:
|
||||
ch: char = lx.src[p]
|
||||
if ch < '0' or ch > '9': break
|
||||
nbits = nbits * 10 + (int(ch) - 48)
|
||||
d: int = int(ch) - 48
|
||||
# Overflow guard only: widths are otherwise unrestricted (LLVM/MLIR support iN for any N).
|
||||
if nbits > (2147483647 - d) / 10: return 0
|
||||
nbits = nbits * 10 + d
|
||||
ndig += 1
|
||||
p += 1
|
||||
if ndig == 0 or nbits <= 0 or nbits > 64: return 0
|
||||
if ndig == 0 or nbits <= 0: return 0
|
||||
if p < lx.len:
|
||||
ch2: char = lx.src[p]
|
||||
if (ch2 >= '0' and ch2 <= '9') or (ch2 >= 'a' and ch2 <= 'z') \
|
||||
|
||||
@@ -2279,6 +2279,16 @@ def _parse_factor(ps: Parser*) -> AST*:
|
||||
_record_neg_literal_overflow(_cur_str(ps), lineno, col)
|
||||
ps.error_count += 1
|
||||
return None
|
||||
# Explicit-width negative literal: fold the sign into the constant so `-64_i7` /
|
||||
# `-128_i8` are range-checked as negative values instead of a positive magnitude.
|
||||
if op_token == TokOp.Minus and _cur_type(ps) == TokenType.Number \
|
||||
and ps.cur.is_float == 0 and ps.cur.int_width > 0:
|
||||
w_tok: Token* = ps.cur
|
||||
_advance(ps)
|
||||
neg_node: AST* = _new_const_int(ps.pool, -w_tok.int_val, lineno, col,
|
||||
w_tok.int_width, w_tok.int_unsigned)
|
||||
_set_pos(neg_node, lineno, col, 0, 0)
|
||||
return neg_node
|
||||
operand: AST* = _parse_factor(ps)
|
||||
ast_op: int = OpKind.UAdd
|
||||
if op_token == TokOp.Minus:
|
||||
|
||||
@@ -298,30 +298,31 @@ def atoll(src: str) -> int64:
|
||||
s += 1
|
||||
elif s[0] == '+':
|
||||
s += 1
|
||||
# Hex prefix 0x / 0X
|
||||
is_hex: int = 0
|
||||
if s[0] == '0' and (s[1] == 'x' or s[1] == 'X'):
|
||||
is_hex = 1
|
||||
s += 2
|
||||
if is_hex:
|
||||
for ch in s:
|
||||
d: int = 0
|
||||
if ch >= '0' and ch <= '9':
|
||||
d = ch - '0'
|
||||
elif ch >= 'a' and ch <= 'f':
|
||||
d = ch - 'a' + 10
|
||||
elif ch >= 'A' and ch <= 'F':
|
||||
d = ch - 'A' + 10
|
||||
else:
|
||||
break
|
||||
digit: int64 = int64(d)
|
||||
num = num * 16 + digit
|
||||
else:
|
||||
for ch in s:
|
||||
if ch < '0' or ch > '9': break
|
||||
d: int = ch - '0'
|
||||
digit: int64 = int64(d)
|
||||
num = num * 10 + digit
|
||||
# Radix prefixes: 0x/0X hex, 0b/0B binary, 0o/0O octal (language literals).
|
||||
base: int64 = 10
|
||||
if s[0] == '0':
|
||||
c1: char = s[1]
|
||||
if c1 == 'x' or c1 == 'X':
|
||||
base = 16
|
||||
s += 2
|
||||
elif c1 == 'b' or c1 == 'B':
|
||||
base = 2
|
||||
s += 2
|
||||
elif c1 == 'o' or c1 == 'O':
|
||||
base = 8
|
||||
s += 2
|
||||
for ch in s:
|
||||
if ch == '_':
|
||||
continue
|
||||
d: int = -1
|
||||
if ch >= '0' and ch <= '9':
|
||||
d = ch - '0'
|
||||
elif ch >= 'a' and ch <= 'f':
|
||||
d = ch - 'a' + 10
|
||||
elif ch >= 'A' and ch <= 'F':
|
||||
d = ch - 'A' + 10
|
||||
if d < 0 or int64(d) >= base: break
|
||||
num = num * base + int64(d)
|
||||
return num * sign
|
||||
|
||||
|
||||
@@ -498,6 +499,9 @@ def atof(src: str) -> double:
|
||||
rounded = 1
|
||||
elif ch == '.':
|
||||
seen_dot = 1
|
||||
elif ch == '_':
|
||||
# Digit separator inside a numeric literal: ignore it.
|
||||
pass
|
||||
else:
|
||||
break
|
||||
s += 1
|
||||
@@ -517,8 +521,9 @@ def atof(src: str) -> double:
|
||||
s += 1
|
||||
elif s[0] == '+':
|
||||
s += 1
|
||||
while s[0] >= '0' and s[0] <= '9':
|
||||
exp = exp * 10 + (s[0] - '0')
|
||||
while (s[0] >= '0' and s[0] <= '9') or s[0] == '_':
|
||||
if s[0] != '_':
|
||||
exp = exp * 10 + (s[0] - '0')
|
||||
s += 1
|
||||
exp = exp * exp_sign
|
||||
p: int = dexp + exp
|
||||
|
||||
Reference in new issue
Block a user