From b43245b1f58814937abac26a9ee1a31bf38f8485 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 1 Jul 2025 17:02:01 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20IdentifierTokenScanner=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E4=B8=BA=E7=8A=B6=E6=80=81=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scanners/IdentifierTokenScanner.java | 82 ++++++++++++++----- 1 file changed, 63 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java index 633d834..1e18cbb 100644 --- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java +++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/IdentifierTokenScanner.java @@ -1,30 +1,34 @@ 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.TokenFactory; +import org.jcnc.snow.compiler.lexer.token.TokenType; /** - * 标识符扫描器:处理标识符的识别,如变量名、函数名等。 - *

- * 识别规则如下: + * {@code IdentifierTokenScanner} —— 标识符扫描器,负责识别源代码中的标识符(如变量名、函数名等)。 + * + *

标识符的识别遵循以下规则:

* - *

- * 扫描完成后会调用 {@link TokenFactory} 自动判断是否为关键字, - * 并返回对应类型的 {@link Token}。 + * + *

在扫描过程中,标识符会被处理为一个 {@link Token} 对象。如果该标识符是一个关键字, + * 扫描器会通过 {@link TokenFactory} 自动识别并返回相应的 {@link TokenType}。

+ * + *

本扫描器实现了一个有限状态机(FSM),它能够在不同状态之间转换,确保标识符的正确识别。

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

如果字符为字母或下划线,则认为是标识符的起始。

+ * 判断当前字符是否可以作为标识符的起始字符。 + *

如果字符为字母或下划线,则认为是标识符的开始。

* * @param c 当前字符 * @param ctx 当前词法上下文 - * @return 如果是标识符起始字符,则返回 true + * @return 如果字符是标识符的起始字符,则返回 {@code true};否则返回 {@code false}。 */ @Override public boolean canHandle(char c, LexerContext ctx) { @@ -32,17 +36,57 @@ public class IdentifierTokenScanner extends AbstractTokenScanner { } /** - * 执行标识符的扫描逻辑。 - *

连续读取满足标识符规则的字符序列,交由 {@code TokenFactory} 创建对应的 Token。

+ * 执行标识符扫描。 + *

使用状态机模式扫描标识符。首先从初始状态开始,读取标识符的起始字符(字母或下划线)。 + * 然后,进入标识符状态,继续读取标识符字符(字母、数字或下划线)。一旦遇到不符合标识符规则的字符, + * 标识符扫描结束,返回一个 {@link Token}。

* - * @param ctx 词法上下文 - * @param line 当前行号 - * @param col 当前列号 - * @return 标识符或关键字类型的 Token + * @param ctx 词法上下文,用于获取字符流 + * @param line 当前行号(1 基) + * @param col 当前列号(1 基) + * @return 返回一个包含标识符或关键字的 {@link Token} 对象。 + * @throws LexicalException 如果标识符以非法字符(如点号)开头,则抛出异常 */ @Override protected Token scanToken(LexerContext ctx, int line, int col) { - String lexeme = readWhile(ctx, ch -> Character.isLetterOrDigit(ch) || ch == '_'); - return TokenFactory.create(lexeme, line, col); + StringBuilder lexeme = new StringBuilder(); // 用于构建标识符的字符串 + State currentState = State.INITIAL; // 初始状态 + + // 遍历字符流,直到遇到不合法的字符或流结束 + while (!ctx.isAtEnd()) { + char currentChar = ctx.peek(); // 获取当前字符 + switch (currentState) { + case INITIAL: + // 初始状态,标识符开始 + if (Character.isLetter(currentChar) || currentChar == '_') { + lexeme.append(ctx.advance()); // 接受当前字符 + currentState = State.IDENTIFIER; // 进入标识符状态 + } else { + return null; // 当前字符不符合标识符的规则,返回 null + } + break; + + case IDENTIFIER: + // 标识符状态,继续读取合法标识符字符 + if (Character.isLetterOrDigit(currentChar) || currentChar == '_') { + lexeme.append(ctx.advance()); // 继续接受合法字符 + } else { + // 当前字符不符合标识符的规则,标识符结束,返回 token + return TokenFactory.create(lexeme.toString(), line, col); + } + break; + } + } + + // 如果字符流结束,返回标识符 token + return TokenFactory.create(lexeme.toString(), line, col); + } + + /** + * 枚举类型表示标识符扫描的状态。 + */ + private enum State { + INITIAL, // 初始状态,等待标识符的开始 + IDENTIFIER // 标识符状态,继续读取标识符字符 } }