From c88404fada081eddc9c9184764383e1a6e9f1cd8 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 1 Jul 2025 00:15:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=B8=BA?= =?UTF-8?q?=E5=9F=BA=E4=BA=8E=E6=9C=89=E9=99=90=E7=8A=B6=E6=80=81=E6=9C=BA?= =?UTF-8?q?=EF=BC=88FSM=EF=BC=89=E7=9A=84=E6=B3=A8=E9=87=8A=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lexer/scanners/CommentTokenScanner.java | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java index a8a3313..328dee7 100644 --- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/CommentTokenScanner.java @@ -1,29 +1,31 @@ package org.jcnc.snow.compiler.lexer.scanners; import org.jcnc.snow.compiler.lexer.core.LexerContext; +import org.jcnc.snow.compiler.lexer.core.LexicalException; import org.jcnc.snow.compiler.lexer.token.Token; import org.jcnc.snow.compiler.lexer.token.TokenType; /** - * 注释扫描器:处理源代码中的注释部分,包括: + * {@code CommentTokenScanner} —— 注释解析器,基于有限状态机(FSM)。 + * + *

负责将源码中的两种注释形式切分为 {@link TokenType#COMMENT COMMENT} token:

+ *
    + *
  1. 单行注释:以 {@code //} 开头,直至行尾或文件末尾。
  2. + * 多行注释:以 {@code /*} 开头,以 */ 结束,可跨多行。 + *
+ * + *

本扫描器遵循“发现即捕获”原则: + * 注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。

+ * + *

错误处理策略

* - *

- * 本扫描器会识别注释并生成 {@code TokenType.COMMENT} 类型的 Token, - * 不会丢弃注释内容,而是将完整注释文本保留在 Token 中,便于后续分析(如文档提取、保留注释等场景)。 - *

*/ public class CommentTokenScanner extends AbstractTokenScanner { /** - * 判断是否可以处理当前位置的字符。 - *

当当前位置字符为 '/' 且下一个字符为 '/' 或 '*' 时,表示可能是注释的起始。

- * - * @param c 当前字符 - * @param ctx 当前词法上下文 - * @return 如果是注释的起始符,则返回 true + * 仅当当前字符为 {@code '/'} 且下一个字符为 {@code '/'} 或 {@code '*'} 时,由本扫描器处理。 */ @Override public boolean canHandle(char c, LexerContext ctx) { @@ -31,44 +33,54 @@ public class CommentTokenScanner extends AbstractTokenScanner { } /** - * 实现注释的扫描逻辑。 - *

支持两种注释格式:

- * + * 执行注释扫描,生成 {@code COMMENT} Token。 * * @param ctx 词法上下文 - * @param line 当前行号(用于 Token 位置信息) - * @param col 当前列号(用于 Token 位置信息) - * @return 包含完整注释内容的 COMMENT 类型 Token + * @param line 起始行号(1 基) + * @param col 起始列号(1 基) + * @return 包含完整注释文本的 Token + * @throws LexicalException 若遇到未终止的多行注释 */ @Override protected Token scanToken(LexerContext ctx, int line, int col) { - // 消费第一个 '/' 字符 - ctx.advance(); - StringBuilder sb = new StringBuilder("/"); + StringBuilder literal = new StringBuilder(); - // 处理单行注释 // + /* + * 1. 读取注释起始符 + * - 已由 canHandle 保证当前位置一定是 '/' + */ + literal.append(ctx.advance()); // 消费首个 '/' + + // -------- 单行注释 (//) -------- if (ctx.match('/')) { - sb.append('/'); + literal.append('/'); while (!ctx.isAtEnd() && ctx.peek() != '\n') { - sb.append(ctx.advance()); + literal.append(ctx.advance()); } + // 行尾或文件尾时退出,换行符留给上层扫描器处理。 } - // 处理多行注释 /* ... */ + // -------- 多行注释 (/* ... */) -------- else if (ctx.match('*')) { - sb.append('*'); + literal.append('*'); + boolean terminated = false; while (!ctx.isAtEnd()) { char ch = ctx.advance(); - sb.append(ch); + literal.append(ch); if (ch == '*' && ctx.peek() == '/') { - sb.append(ctx.advance()); // 消费 '/' + literal.append(ctx.advance()); // 追加 '/' + terminated = true; break; } } + if (!terminated) { + // 文件结束仍未闭合,抛 LexicalException + throw new LexicalException("未终止的多行注释", line, col); + } } - return new Token(TokenType.COMMENT, sb.toString(), line, col); + /* + * 2. 生成并返回 Token + */ + return new Token(TokenType.COMMENT, literal.toString(), line, col); } }