feat: 支持以 'this' 开头的表达式并优化赋值语句解析
- 允许 'this'作为表达式起始,支持 'this.xxx' 形式的赋值 - 优化赋值语句解析逻辑,支持更复杂的左值表达式 - 新增对 'this' 成员赋值的特殊处理,降级为普通变量赋值 - 改进错误处理,对不支持的赋值左值类型抛出异常
This commit is contained in:
parent
5b259a01d8
commit
2dd5e64c3a
@ -14,17 +14,17 @@ import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
/**
|
||||
* {@code ExpressionStatementParser} 用于解析通用表达式语句(赋值或普通表达式)。
|
||||
* <p>
|
||||
* 支持以下两种语法结构:
|
||||
* 支持以下两种语法结构:
|
||||
* <pre>{@code
|
||||
* x = 1 + 2 // 赋值语句
|
||||
* doSomething() // 一般表达式语句
|
||||
* this.name = n // 将 this.name 赋值语法糖为对 name 的赋值
|
||||
* }</pre>
|
||||
* <ul>
|
||||
* <li>以标识符开头且后接 {@code =} 时,解析为 {@link AssignmentNode}。</li>
|
||||
* <li>否则视为普通表达式,解析为 {@link ExpressionStatementNode}。</li>
|
||||
* <li>所有表达式语句必须以换行符({@code NEWLINE})结尾。</li>
|
||||
* </ul>
|
||||
* 若语句起始为关键字或空行,将直接抛出异常,防止非法语法进入表达式解析流程。
|
||||
*
|
||||
* - 以标识符开头且后接 '=' 时,解析为 {@link AssignmentNode}。
|
||||
* - 否则先解析为一般表达式;若后续遇到 '=',则回退为“<expr> = <expr>”赋值语句。
|
||||
* - 所有表达式语句必须以换行符(NEWLINE)结尾。
|
||||
* <p>
|
||||
*/
|
||||
public class ExpressionStatementParser implements StatementParser {
|
||||
|
||||
@ -33,49 +33,81 @@ public class ExpressionStatementParser implements StatementParser {
|
||||
*
|
||||
* @param ctx 当前解析上下文,提供词法流与环境信息
|
||||
* @return {@link AssignmentNode} 或 {@link ExpressionStatementNode} 语法节点
|
||||
* @throws UnexpectedToken 若遇到非法起始(关键字、空行等)
|
||||
* @throws UnexpectedToken 若遇到非法起始(关键字 'end' 等)
|
||||
*/
|
||||
@Override
|
||||
public StatementNode parse(ParserContext ctx) {
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
|
||||
// ----------- 起始 token 合法性检查(放宽以支持 this 开头)-----------
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
// 空行不应进入表达式解析,直接抛出异常
|
||||
throw new UnexpectedToken(
|
||||
"无法解析以关键字开头的表达式: " + ts.peek().getLexeme(),
|
||||
"无法解析以空行开头的表达式",
|
||||
ts.peek().getLine(),
|
||||
ts.peek().getCol()
|
||||
);
|
||||
}
|
||||
if (ts.peek().getType() == TokenType.KEYWORD) {
|
||||
String kw = ts.peek().getLexeme();
|
||||
// 仅允许 this 作为表达式起始;其它关键字(如 end/if/else 等)仍禁止
|
||||
if (!"this".equals(kw)) {
|
||||
throw new UnexpectedToken(
|
||||
"无法解析以关键字开头的表达式: " + kw,
|
||||
ts.peek().getLine(),
|
||||
ts.peek().getCol()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int line = ts.peek().getLine();
|
||||
int column = ts.peek().getCol();
|
||||
String file = ctx.getSourceName();
|
||||
|
||||
// 简单形式: IDENTIFIER = expr
|
||||
// ------------- 简单形式: IDENTIFIER = expr -------------
|
||||
// 快速路径:如 "a = ...",直接识别为赋值语句,无需完整表达式树回退
|
||||
if (ts.peek().getType() == TokenType.IDENTIFIER && "=".equals(ts.peek(1).getLexeme())) {
|
||||
String varName = ts.next().getLexeme();
|
||||
ts.expect("=");
|
||||
ExpressionNode value = new PrattExpressionParser().parse(ctx);
|
||||
String varName = ts.next().getLexeme(); // 消费 IDENTIFIER
|
||||
ts.expect("="); // 消费 '='
|
||||
ExpressionNode value = new PrattExpressionParser().parse(ctx); // 解析右侧表达式
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
// 返回简单变量赋值节点
|
||||
return new AssignmentNode(varName, value, new NodeContext(line, column, file));
|
||||
}
|
||||
|
||||
// 尝试解析更通用的左值形式(支持下标): <expr> = <expr>
|
||||
// ------------- 通用形式: <expr> [= <expr>] -------------
|
||||
// 先解析潜在“左值表达式”或普通表达式
|
||||
ExpressionNode lhs = new PrattExpressionParser().parse(ctx);
|
||||
|
||||
// 若遇到等号,则尝试回退为赋值语句(兼容更复杂的左值表达式)
|
||||
if ("=".equals(ts.peek().getLexeme())) {
|
||||
ts.next(); // consume '='
|
||||
ExpressionNode rhs = new PrattExpressionParser().parse(ctx);
|
||||
ts.next(); // 消费 '='
|
||||
ExpressionNode rhs = new PrattExpressionParser().parse(ctx); // 解析右值表达式
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
// 根据左值类型构造具体赋值节点
|
||||
|
||||
// 根据左值 AST 类型,生成不同赋值节点
|
||||
if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode id) {
|
||||
// 变量名赋值:a = rhs
|
||||
return new AssignmentNode(id.name(), rhs, new NodeContext(line, column, file));
|
||||
|
||||
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.IndexExpressionNode idx) {
|
||||
// 下标赋值:a[i] = rhs
|
||||
return new org.jcnc.snow.compiler.parser.ast.IndexAssignmentNode(idx, rhs, new NodeContext(line, column, file));
|
||||
|
||||
} else if (lhs instanceof org.jcnc.snow.compiler.parser.ast.MemberExpressionNode mem
|
||||
&& mem.object() instanceof org.jcnc.snow.compiler.parser.ast.IdentifierNode oid
|
||||
&& "this".equals(oid.name())) {
|
||||
// 支持:this.field = rhs
|
||||
// 语法糖:降级为对当前作用域同名变量的赋值,相当于 "field = rhs"
|
||||
return new AssignmentNode(mem.member(), rhs, new NodeContext(line, column, file));
|
||||
|
||||
} else {
|
||||
// 其它成员赋值(如 a.b = ...)不支持,报错
|
||||
throw new UnexpectedToken("不支持的赋值左值类型: " + lhs.getClass().getSimpleName(), line, column);
|
||||
}
|
||||
}
|
||||
// 普通表达式语句
|
||||
|
||||
// 不是赋值,则当作普通表达式语句处理
|
||||
ts.expectType(TokenType.NEWLINE);
|
||||
return new ExpressionStatementNode(lhs, new NodeContext(line, column, file));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user