fix: 修复数字字面量解析中的错误处理
- 增加对数字后紧跟未知标识符的错误处理 - 增加对数字后紧跟下划线的错误处理 - 优化数字类型后缀的处理逻辑,防止多字符后缀
This commit is contained in:
parent
4a26bd50ca
commit
c4d9be8403
@ -14,7 +14,7 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
* <li>十进制小数(如 3.14,0.5)</li>
|
* <li>十进制小数(如 3.14,0.5)</li>
|
||||||
* <li>单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)</li>
|
* <li>单字符类型后缀(如 2.0f,255B,合法集合见 SUFFIX_CHARS)</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
* <p>
|
||||||
* 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。
|
* 如果后续需要支持科学计数法、下划线分隔符、不同进制等,只需扩展现有状态机的转移规则。
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -34,14 +34,14 @@ import org.jcnc.snow.compiler.lexer.token.TokenType;
|
|||||||
* <li>FRAC_PART : 读取小数部分,遇非法字符则结束主体。</li>
|
* <li>FRAC_PART : 读取小数部分,遇非法字符则结束主体。</li>
|
||||||
* <li>END : 主体扫描结束,进入后缀/尾随字符判定。</li>
|
* <li>END : 主体扫描结束,进入后缀/尾随字符判定。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
* <p>
|
||||||
* 错误处理策略:
|
* 错误处理策略:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>数字后跟未知字母(如 42X)—— 抛出 LexicalException</li>
|
* <li>数字后跟未知字母(如 42X)—— 抛出 LexicalException</li>
|
||||||
* <li>数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException</li>
|
* <li>数字与合法后缀间有空白(如 3 L)—— 抛出 LexicalException</li>
|
||||||
* <li>小数点后缺失数字(如 1.)—— 抛出 LexicalException</li>
|
* <li>小数点后缺失数字(如 1.)—— 抛出 LexicalException</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
*
|
* <p>
|
||||||
* 支持的单字符类型后缀包括: b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set<String>。
|
* 支持的单字符类型后缀包括: b, s, l, f, d 及其大写形式。若需支持多字符后缀,可将该集合扩展为 Set<String>。
|
||||||
*/
|
*/
|
||||||
public class NumberTokenScanner extends AbstractTokenScanner {
|
public class NumberTokenScanner extends AbstractTokenScanner {
|
||||||
@ -125,13 +125,30 @@ public class NumberTokenScanner extends AbstractTokenScanner {
|
|||||||
if (!ctx.isAtEnd()) {
|
if (!ctx.isAtEnd()) {
|
||||||
char next = ctx.peek();
|
char next = ctx.peek();
|
||||||
|
|
||||||
/* 2-A. 合法单字符后缀(紧邻数字,不允许空格) */
|
/* 2-A. 合法单字符后缀(紧邻数字,无空格) */
|
||||||
if (SUFFIX_CHARS.indexOf(next) >= 0) {
|
if (SUFFIX_CHARS.indexOf(next) >= 0) {
|
||||||
literal.append(ctx.advance());
|
literal.append(ctx.advance());
|
||||||
}
|
|
||||||
/* 2-B. 未知紧邻字母后缀 —— 报错 */
|
/* 后缀只能出现一次:再次出现字母/数字/点即视为非法 */
|
||||||
else if (Character.isLetter(next)) {
|
if (!ctx.isAtEnd()) {
|
||||||
throw new LexicalException("未知的数字类型后缀 '" + next + "'", line, col);
|
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);
|
return new Token(TokenType.NUMBER_LITERAL, literal.toString(), line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** FSM 内部状态定义 */
|
/**
|
||||||
|
* FSM 内部状态定义
|
||||||
|
*/
|
||||||
private enum State {
|
private enum State {
|
||||||
/** 整数部分 */
|
/**
|
||||||
|
* 整数部分
|
||||||
|
*/
|
||||||
INT_PART,
|
INT_PART,
|
||||||
/** 已读到小数点,但还未读到第一位小数数字 */
|
/**
|
||||||
|
* 已读到小数点,但还未读到第一位小数数字
|
||||||
|
*/
|
||||||
DEC_POINT,
|
DEC_POINT,
|
||||||
/** 小数部分 */
|
/**
|
||||||
|
* 小数部分
|
||||||
|
*/
|
||||||
FRAC_PART,
|
FRAC_PART,
|
||||||
/** 主体结束,准备处理后缀或交还控制权 */
|
/**
|
||||||
|
* 主体结束,准备处理后缀或交还控制权
|
||||||
|
*/
|
||||||
END
|
END
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user