diff --git a/includes/ast/lexer.vp b/includes/ast/lexer.vp index 2b82bb8..924b020 100644 --- a/includes/ast/lexer.vp +++ b/includes/ast/lexer.vp @@ -473,6 +473,8 @@ def _read_string(lx: Lexer | t.CPtr): # Read string content content_buf_start: t.CSizeT = lx.pos + # f-string {} 表达式块嵌套深度(PEP 701 同引号嵌套字符串支持用) + fstr_depth: t.CInt = 0 while lx.pos < lx.len: ch = lx.src[lx.pos] if is_triple: @@ -498,6 +500,50 @@ def _read_string(lx: Lexer | t.CPtr): lx.pos += 1 lx.col += 1 else: + if is_fstring: + # {{ 与 }} 是转义字面量,不参与深度计数 + if ch == '{' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '{': + lx.pos += 2 + lx.col += 2 + continue + if ch == '}' and lx.pos + 1 < lx.len and lx.src[lx.pos + 1] == '}': + lx.pos += 2 + lx.col += 2 + continue + if ch == '{': + fstr_depth += 1 + lx.pos += 1 + lx.col += 1 + continue + if ch == '}': + if fstr_depth > 0: + fstr_depth -= 1 + lx.pos += 1 + lx.col += 1 + continue + if fstr_depth > 0 and (ch == '\'' or ch == '"'): + # {} 表达式块内允许嵌套字符串字面量(PEP 701 同引号嵌套): + # 跳过到匹配引号,块内引号不终止外层 f-string。 + nq: t.CChar = ch + lx.pos += 1 + lx.col += 1 + while lx.pos < lx.len: + c2: t.CChar = lx.src[lx.pos] + if c2 == '\\': + lx.pos += 2 + lx.col += 2 + continue + if c2 == nq: + lx.pos += 1 + lx.col += 1 + break + if c2 == '\n': + # 单行字符串不能跨行 + lx.error_count += 1 + return + lx.pos += 1 + lx.col += 1 + continue if ch == quote: break if ch == '\n': diff --git a/includes/ast/parser.vp b/includes/ast/parser.vp index 50cd84c..6bbf8dc 100644 --- a/includes/ast/parser.vp +++ b/includes/ast/parser.vp @@ -1286,6 +1286,21 @@ def _parse_fstring(ps: Parser | t.CPtr, content: str, fmt_pos: t.CSizeT = 0 while j < content_len and depth > 0: c: t.CChar = content[j] + if c == '\'' or c == '"': + # 跳过表达式块内的嵌套字符串字面量(PEP 701 同引号嵌套), + # 避免其内部字符干扰 {} 深度/转换/格式分隔符定位 + nq: t.CChar = c + j += 1 + while j < content_len: + c2: t.CChar = content[j] + if c2 == '\\': + j += 2 + continue + if c2 == nq: + j += 1 + break + j += 1 + continue if c == '{': depth += 1 elif c == '}': @@ -1307,6 +1322,7 @@ def _parse_fstring(ps: Parser | t.CPtr, content: str, fmt_pos = j j += 1 if depth != 0: + _record_parse_err(ps) ps.error_count += 1 break # Extract expression text @@ -1317,6 +1333,7 @@ def _parse_fstring(ps: Parser | t.CPtr, content: str, expr_end = fmt_pos expr_len: t.CSizeT = expr_end - i - 1 if expr_len == 0: + _record_parse_err(ps) ps.error_count += 1 i = j + 1 continue @@ -1356,6 +1373,7 @@ def _parse_fstring(ps: Parser | t.CPtr, content: str, lit_pos += 1 i += 2 else: + _record_parse_err(ps) ps.error_count += 1 i += 1 else: