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:
+ *
+ * - 单行注释:以 {@code //} 开头,直至行尾或文件末尾。
+ * 多行注释:以 {@code /*} 开头,以 */ 结束,可跨多行。
+ *
+ *
+ * 本扫描器遵循“发现即捕获”原则:
+ * 注释文本被完整保留在 Token 中,供后续的文档提取、源映射等分析使用。
+ *
+ * 错误处理策略
*
- * - 单行注释(以 "//" 开头,直到行尾)
- * - 多行注释(以 "/*" 开头,以 "*/" 结尾)
+ * - 未终止的多行注释:若文件结束时仍未遇到
*/,抛出 {@link LexicalException}。
*
- *
- * 本扫描器会识别注释并生成 {@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);
}
}