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