feat: 增强变量声明语句解析功能

- 支持类型标识符与自定义结构体名
- 支持带初始值的声明
- 增加对常量声明的支持(使用 const 关键字)
- 优化错误处理,提高语法错误提示的准确性
This commit is contained in:
Luke 2025-08-29 17:16:14 +08:00
parent 1a34e3e436
commit fb441c01af

View File

@ -10,75 +10,96 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
/**
* {@code DeclarationStatementParser} 负责解析变量声明语句节点
* <p>
* 支持以下两种语法结构
* <pre>{@code
* declare myVar:Integer
* declare myVar:Integer = 42 + 3
* }</pre>
* 解析器能够识别多维数组类型 {@code int[]}, {@code string[][]}并支持可选初始化表达式
* <p>
* 每个声明语句均要求以换行符结尾语法不合法时会抛出异常
* </p>
* <ul>
* <li>支持类型标识符与自定义结构体名</li>
* <li>支持多维数组类型 <code>int[][]</code></li>
* <li>支持带初始值的声明</li>
* </ul>
*/
public class DeclarationStatementParser implements StatementParser {
/**
* 解析一条 {@code declare} 语句生成对应的抽象语法树节点 {@link DeclarationNode}
* <p>
* 支持类型标注和可选初始化表达式类型部分自动拼接数组维度 int[][]
* </p>
* 解析变量或常量声明语句
*
* @param ctx 当前语法解析上下文包含词法流错误信息等
* @return {@link DeclarationNode} 表示声明语句结构
* @throws RuntimeException 语法不合法时抛出
* @param ctx 语法分析上下文提供词法单元流与其他辅助功能
* @return 解析得到的声明节点 {@link DeclarationNode}
* @throws org.jcnc.snow.compiler.parser.context.UnexpectedToken 若语法不合法则抛出异常
*/
@Override
public DeclarationNode parse(ParserContext ctx) {
// 便捷引用词法 token
var tokens = ctx.getTokens();
var tokens = ctx.getTokens(); // 获取词法单元流
// 获取当前 token 的行号列号和文件名
// 记录声明语句在源码中的位置信息文件名
int line = tokens.peek().getLine();
int column = tokens.peek().getCol();
String file = ctx.getSourceName();
// 声明语句必须以 "declare" 开头
tokens.expect("declare");
// 是否声明为常量
boolean isConst = tokens.match("const");
// 获取变量名称标识符
String name = tokens
.expectType(TokenType.IDENTIFIER)
.getLexeme();
// 类型标注的冒号分隔符
tokens.expect(":");
// 获取变量类型类型标识符
StringBuilder type = new StringBuilder(
tokens
.expectType(TokenType.TYPE)
.getLexeme()
);
// 消费多维数组类型后缀 "[]"
while (tokens.match("[")) {
tokens.expectType(TokenType.RBRACKET); // 必须配对
type.append("[]"); // 类型名称拼接 [] int[][]
// 判断并消费声明关键字 declare const
boolean isConst = false;
String first = tokens.peek().getLexeme();
if ("declare".equals(first)) {
tokens.next(); // 消费 declare
// declare 后可选 const用于声明常量
if ("const".equals(tokens.peek().getLexeme())) {
isConst = true;
tokens.next(); // 消费 const
}
} else if ("const".equals(first)) {
// 支持 const 开头的声明写法
isConst = true;
tokens.next(); // 消费 const
} else {
// 不符合语法规则抛出异常
throw new org.jcnc.snow.compiler.parser.context.UnexpectedToken(
"声明应以 'declare' 或 'declare const' 开始,而不是 '" + first + "'",
tokens.peek().getLine(), tokens.peek().getCol());
}
// 可选初始化表达式=号右侧
// 获取变量名标识符
String name = tokens.expectType(TokenType.IDENTIFIER).getLexeme();
// 检查并消费冒号 :
tokens.expect(":");
// 解析变量类型类型标识符或自定义结构体名
StringBuilder type = new StringBuilder();
if (tokens.peek().getType() == TokenType.TYPE || tokens.peek().getType() == TokenType.IDENTIFIER) {
// 类型可以是基础类型或结构体名
type.append(tokens.next().getLexeme());
} else {
// 类型不是合法的 Token抛出异常
var t = tokens.peek();
throw new org.jcnc.snow.compiler.parser.context.UnexpectedToken(
"期望的标记类型为 TYPE 或 IDENTIFIER但实际得到的是 "
+ t.getType() + " ('" + t.getLexeme() + "')",
t.getLine(), t.getCol()
);
}
// 处理多维数组类型后缀支持 int[][] 等类型
while (tokens.match("[")) {
// 消费左中括号 '[' 后必须跟右中括号 ']'
tokens.expectType(TokenType.RBRACKET); // 消费 ']'
type.append("[]"); // 追加数组后缀
}
// 可选的初始化表达式 = 10
ExpressionNode init = null;
if (tokens.match("=")) {
// 使用 Pratt 解析器解析表达式获得初始化表达式节点
init = new PrattExpressionParser().parse(ctx);
}
// 声明语句必须以换行符结尾
// 声明语句必须以换行符 NEWLINE 结尾
tokens.expectType(TokenType.NEWLINE);
// 返回构建好的声明语法树节点
return new DeclarationNode(name, type.toString(), isConst, init, new NodeContext(line, column, file));
// 组装声明节点并返回
return new DeclarationNode(
name, // 变量/常量名
type.toString(), // 类型字符串
isConst, // 是否常量
init, // 初始化表达式节点可为 null
new NodeContext(line, column, file) // 源码位置信息
);
}
}