refactor: 重构字符串扫描器实现
- 优化了 StringTokenScanner 类的文档注释,增加了状态机说明 -重新组织了代码结构,提高了可读性和可维护性 - 添加了对未闭合字符串的处理逻辑,增强了健壮性 - 优化了状态机实现,保证了字符串解析的准确性
This commit is contained in:
parent
5f0931155d
commit
e6ad4ff282
@ -5,91 +5,118 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
|
||||
/**
|
||||
* 字符串扫描器: 处理双引号包裹的字符串字面量,支持基本的转义字符。
|
||||
* 字符串扫描器(StringTokenScanner)用于处理由双引号包裹的字符串字面量,
|
||||
* 并支持常见转义字符的解析。该扫描器采用有限状态机(状态机)实现,
|
||||
* 保证对各类字符串格式进行准确的词法分析。
|
||||
* <p>
|
||||
* 支持格式示例:
|
||||
* 主要支持如下字符串样式:
|
||||
* <ul>
|
||||
* <li>"hello"</li>
|
||||
* <li>"line\\nbreak"</li>
|
||||
* <li>"escaped \\\" quote"</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 扫描器会保留原始字符串的形式(包含双引号和转义符),
|
||||
* 并生成 {@code STRING_LITERAL} 类型的 Token。
|
||||
* 扫描器会保留字符串的原始形式(含双引号和转义符),
|
||||
* 并返回{@link TokenType#STRING_LITERAL}类型的Token。
|
||||
* </p>
|
||||
* <p>
|
||||
* 状态机说明:
|
||||
* <ul>
|
||||
* <li>START:起始状态,准备扫描第一个双引号</li>
|
||||
* <li>STRING:扫描字符串内容</li>
|
||||
* <li>ESCAPE:处理转义字符</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author 你的名字
|
||||
* @since 2024
|
||||
*/
|
||||
public class StringTokenScanner extends AbstractTokenScanner {
|
||||
|
||||
/**
|
||||
* 判断是否可以处理当前位置的字符。
|
||||
* <p>当字符为双引号(")时,认为是字符串字面量的开始。</p>
|
||||
* 判断当前位置的字符是否为字符串起始符号。
|
||||
* <p>
|
||||
* 只有遇到双引号(")时,才由本扫描器处理。
|
||||
* </p>
|
||||
*
|
||||
* @param c 当前字符
|
||||
* @param ctx 当前词法上下文
|
||||
* @return 如果为字符串起始符,则返回 true
|
||||
* @param c 当前待扫描字符
|
||||
* @param ctx 当前词法分析上下文
|
||||
* @return 如果是字符串起始符号,返回 true,否则返回 false
|
||||
*/
|
||||
@Override
|
||||
public boolean canHandle(char c, LexerContext ctx) {
|
||||
return c == '"'; // 只处理字符串开始符号
|
||||
return c == '"'; // 只处理双引号起始
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行字符串的扫描逻辑。
|
||||
* <p>从当前位置开始,读取直到匹配结束的双引号。
|
||||
* 支持转义字符(如 \"、\\n 等),不会中断字符串扫描。</p>
|
||||
* 执行字符串字面量的具体扫描过程。采用有限状态机处理逻辑,
|
||||
* 从起始的双引号开始,逐字符处理字符串内容,支持基本转义序列(如 \\n、\\t、\\\" 等)。
|
||||
* <p>
|
||||
* 扫描遇到未转义的结尾双引号时即结束,并立即返回Token。
|
||||
* 如果遇到换行或文件结束但未遇到结尾引号,视为字符串未闭合,仍返回已扫描内容。
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 词法上下文
|
||||
* @param line 当前行号
|
||||
* @param col 当前列号
|
||||
* @return 字符串字面量类型的 Token
|
||||
* @param ctx 词法分析上下文,支持字符遍历与回退等操作
|
||||
* @param line 字符串开始行号(用于错误定位)
|
||||
* @param col 字符串开始列号(用于错误定位)
|
||||
* @return 解析得到的字符串字面量类型Token
|
||||
*/
|
||||
@Override
|
||||
protected Token scanToken(LexerContext ctx, int line, int col) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 当前状态
|
||||
State currentState = State.START; // 初始状态为开始扫描字符串
|
||||
StringBuilder sb = new StringBuilder(); // 用于收集字符串原文
|
||||
State currentState = State.START; // 初始状态为START
|
||||
|
||||
// 开始扫描字符串
|
||||
// 主循环,直到文件结束或状态机中止
|
||||
while (!ctx.isAtEnd()) {
|
||||
char c = ctx.advance();
|
||||
char c = ctx.advance(); // 消耗当前字符
|
||||
sb.append(c);
|
||||
|
||||
switch (currentState) {
|
||||
case START:
|
||||
// 开始状态,遇到第一个双引号
|
||||
// 第一个双引号,状态切换到STRING
|
||||
currentState = State.STRING;
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
if (c == '\\') {
|
||||
// 遇到转义字符,进入 ESCAPE 状态
|
||||
// 遇到转义符,切换到ESCAPE状态
|
||||
currentState = State.ESCAPE;
|
||||
} else if (c == '"') {
|
||||
// 遇到结束的双引号,结束扫描
|
||||
currentState = State.END;
|
||||
// 遇到结束双引号,立即返回Token(字符串扫描完毕)
|
||||
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||
} else if (c == '\n' || c == '\r') {
|
||||
// 若字符串未闭合且遇到换行,提前返回(可根据需要抛异常或报错)
|
||||
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||
}
|
||||
// 其他字符,保持在STRING状态继续扫描
|
||||
break;
|
||||
|
||||
case ESCAPE:
|
||||
// 在转义状态下,处理转义字符
|
||||
sb.append(ctx.advance()); // 加入转义字符后的字符
|
||||
currentState = State.STRING; // 返回字符串状态
|
||||
// ESCAPE状态:下一个字符会作为转义内容,无论是"、n、t等
|
||||
// 注意advance已经处理,所以不需要再append
|
||||
currentState = State.STRING;
|
||||
break;
|
||||
|
||||
case END:
|
||||
// 结束状态,字符串扫描完成
|
||||
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有结束的双引号,则表示错误,或者未正确处理
|
||||
// 若扫描到文件尾仍未遇到结尾引号,则返回当前内容
|
||||
return new Token(TokenType.STRING_LITERAL, sb.toString(), line, col);
|
||||
}
|
||||
|
||||
// 定义状态枚举
|
||||
/**
|
||||
* 状态机枚举类型,表示当前字符串解析所处的状态。
|
||||
*/
|
||||
private enum State {
|
||||
START, // 开始状态,寻找字符串的开始双引号
|
||||
STRING, // 字符串扫描状态,处理字符串中的字符
|
||||
ESCAPE, // 处理转义字符状态
|
||||
END // 字符串结束状态
|
||||
/**
|
||||
* 起始状态,等待遇到第一个双引号
|
||||
*/
|
||||
START,
|
||||
/**
|
||||
* 字符串内容状态,处理实际字符串及普通字符
|
||||
*/
|
||||
STRING,
|
||||
/**
|
||||
* 处理转义序列状态,遇到'\\'后切换到此状态
|
||||
*/
|
||||
ESCAPE,
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user