From c4d9be84034767e5700e6b22ac26f72a189d3a39 Mon Sep 17 00:00:00 2001 From: Luke Date: Wed, 16 Jul 2025 22:14:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=95=B0=E5=AD=97?= =?UTF-8?q?=E5=AD=97=E9=9D=A2=E9=87=8F=E8=A7=A3=E6=9E=90=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增加对数字后紧跟未知标识符的错误处理 - 增加对数字后紧跟下划线的错误处理 - 优化数字类型后缀的处理逻辑,防止多字符后缀 --- .../lexer/scanners/NumberTokenScanner.java | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java index 7b983d7..40575c9 100644 --- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/NumberTokenScanner.java @@ -8,17 +8,17 @@ import org.jcnc.snow.compiler.lexer.token.TokenType; /** * NumberTokenScanner —— 基于有限状态机(FSM)的数字字面量解析器。 *

- * 该扫描器负责将源码中的数字字符串切分为 NUMBER_LITERAL token,当前支持: + * 该扫描器负责将源码中的数字字符串切分为 NUMBER_LITERAL token,当前支持: *

    *
  1. 十进制整数(如 0,42,123456)
  2. *
  3. 十进制小数(如 3.14,0.5)
  4. *
  5. 单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)
  6. *
- * + *

* 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。 * *

- * 状态机简述: 
+ * 状态机简述:
  *   INT_PART   --'.'-->  DEC_POINT
  *      |                 |
  *      |                 v
@@ -27,21 +27,21 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
  *      v
  *   DEC_POINT  --digit--> FRAC_PART
  * 
- * 状态说明: + * 状态说明: * - * - * 错误处理策略: + *

+ * 错误处理策略: *

    *
  1. 数字后跟未知字母(如 42X)—— 抛出 LexicalException
  2. *
  3. 数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException
  4. *
  5. 小数点后缺失数字(如 1.)—— 抛出 LexicalException
  6. *
- * + *

* 支持的单字符类型后缀包括: b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set。 */ public class NumberTokenScanner extends AbstractTokenScanner { @@ -125,13 +125,30 @@ public class NumberTokenScanner extends AbstractTokenScanner { if (!ctx.isAtEnd()) { char next = ctx.peek(); - /* 2-A. 合法单字符后缀(紧邻数字,不允许空格) */ + /* 2-A. 合法单字符后缀(紧邻数字,无空格) */ if (SUFFIX_CHARS.indexOf(next) >= 0) { literal.append(ctx.advance()); - } - /* 2-B. 未知紧邻字母后缀 —— 报错 */ - else if (Character.isLetter(next)) { - throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col); + + /* 后缀只能出现一次:再次出现字母/数字/点即视为非法 */ + if (!ctx.isAtEnd()) { + char peekAfterSuffix = ctx.peek(); + if (Character.isLetter(peekAfterSuffix) + || Character.isDigit(peekAfterSuffix) + || peekAfterSuffix == '.') { + throw new LexicalException( + "数字类型后缀只能是单字符,非法续接 '" + peekAfterSuffix + "'", + line, col); + } + } + + /* 2-B. **非法字母**(既不是后缀,也没有空白隔开) */ + } else if (Character.isLetter(next)) { + throw new LexicalException( + "数字后不能紧跟未知标识符 '" + next + "'", line, col); + /* 2-C. **非法下划线** */ + } else if (next == '_') { + throw new LexicalException( + "数字后不能紧跟下划线 '_'", line, col); } /* 其余情况交由外层扫描器处理(包括空白及其它符号) */ } @@ -140,15 +157,25 @@ public class NumberTokenScanner extends AbstractTokenScanner { return new Token(TokenType.NUMBER_LITERAL, literal.toString(), line, col); } - /** FSM 内部状态定义 */ + /** + * FSM 内部状态定义 + */ private enum State { - /** 整数部分 */ + /** + * 整数部分 + */ INT_PART, - /** 已读到小数点,但还未读到第一位小数数字 */ + /** + * 已读到小数点,但还未读到第一位小数数字 + */ DEC_POINT, - /** 小数部分 */ + /** + * 小数部分 + */ FRAC_PART, - /** 主体结束,准备处理后缀或交还控制权 */ + /** + * 主体结束,准备处理后缀或交还控制权 + */ END } }